diff --git a/build/xlsxio_csv2xlsx.cbp b/build/xlsxio_csv2xlsx.cbp new file mode 100644 index 0000000..72c3c3e --- /dev/null +++ b/build/xlsxio_csv2xlsx.cbp @@ -0,0 +1,110 @@ + + + + + + diff --git a/src/xlsxio_csv2xlsx.c b/src/xlsxio_csv2xlsx.c new file mode 100644 index 0000000..f51294c --- /dev/null +++ b/src/xlsxio_csv2xlsx.c @@ -0,0 +1,187 @@ +/***************************************************************************** +Copyright (C) 2016 Brecht Sanders All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*****************************************************************************/ + +#include +#include +#include +#include +#include "xlsxio_write.h" +#include "xlsxio_version.h" + +#define BUFFER_SIZE 256 + +int append_buffer_data (char** pdata, size_t* pdatalen, const char* bufferdata, size_t bufferdatalen) +{ + //allocate larger data buffer, abort in case of memory allocation error + if ((*pdata = (char*)realloc(*pdata, *pdatalen + bufferdatalen + 1)) == NULL) + return 1; + //append new data and adjust length + memcpy(*pdata + *pdatalen, bufferdata, bufferdatalen); + *pdatalen += bufferdatalen; + return 0; +} + +void show_help () +{ + printf( + "Usage: xlsxio_csv2xlsx [-h] [-s separator] [-n] csvfile ...\n" + "Parameters:\n" + " -h \tdisplay command line help\n" + " -s separator\tspecify separator to use (default is comma)\n" + " -t \ttread top row as header row\n" + " csvfile \tpath to CSV file (multiple may be specified)\n" + "Description:\n" + "Converts all specified CSV files to .xlsx.\n" + "Version: " XLSXIO_VERSION_STRING "\n" + "\n" + ); +} + +int main (int argc, char* argv[]) +{ + int i; + char* param; + xlsxiowriter xlsxiowrite; + FILE* src; + char separator = ','; + char quote = '"'; + int toprowheaders = 0; + //process command line parameters + if (argc == 1) { + show_help(); + return 0; + } + for (i = 1; i < argc; i++) { + //check for command line parameters + if (argv[i][0] && (argv[i][0] == '/' || argv[i][0] == '-')) { + switch (tolower(argv[i][1])) { + case 'h' : + case '?' : + show_help(); + return 0; + case 's' : + if (argv[i][2]) + param = argv[i] + 2; + else if (i + 1 < argc && argv[i + 1]) + param = argv[++i]; + if (param) + separator = param[0]; + continue; + case 't' : + toprowheaders = 1; + continue; + } + } + //open CSV file + if ((src = fopen(argv[i], "rb")) == NULL) { + fprintf(stderr, "Error opening file: %s\n", argv[i]); + } else { + char* sheetname; + char* filename; + //determine sheetname + if ((sheetname = strdup(argv[i])) != NULL) { + char* p; + if ((p = strrchr(sheetname, '.')) != NULL) + *p = 0; + } + //determine output filename + if ((filename = (char*)malloc(strlen(argv[i]) + 6)) == NULL ){ + fprintf(stderr, "Memory allocation error\n"); + } else { + strcpy(filename, argv[i]); + strcat(filename, ".xlsx"); + //create .xslx file + if ((xlsxiowrite = xlsxiowrite_open(filename, sheetname)) != NULL) { + //skip UTF-8 BOM header if present + unsigned char bom[3]; + if (!(fread(bom, 1, 3, src) == 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF)) { + fseek(src, 0, SEEK_SET); + } + //process CSV file + char buf[BUFFER_SIZE]; + size_t buflen = 0; + size_t bufstart = 0; + size_t bufpos = 0; + char* value = NULL; + size_t valuelen = 0; + size_t unquoted = 1; + char lastchar = 0; + int toprow = 1; + //read data one buffer at a tome + while ((buflen = fread(buf, 1, BUFFER_SIZE, src)) > 0) { + //process each character in buffer + while (bufpos < buflen) { + if (unquoted) { + if (buf[bufpos] == separator || buf[bufpos] == '\n') { + //process end of cell + append_buffer_data(&value, &valuelen, buf + bufstart, bufpos - bufstart); + bufstart = bufpos + 1; + //detect CRLF line breaks and skip CR + if (buf[bufpos] == '\n' && valuelen > 0 && value[valuelen - 1] == '\r') + valuelen--; + //write cell value + if (value) + value[valuelen] = 0; + if (toprow && toprowheaders) + xlsxiowrite_add_column(xlsxiowrite, value, 0); + else + xlsxiowrite_add_cell_string(xlsxiowrite, value); + //clean up cell value + free(value); + value = NULL; + valuelen = 0; + //process end of line + if (buf[bufpos] == '\n') { + xlsxiowrite_next_row(xlsxiowrite); + toprow = 0; + } + } else if (buf[bufpos] == quote) { + //process upen quote + append_buffer_data(&value, &valuelen, buf + bufstart, bufpos - bufstart + (lastchar == quote ? 1 : 0)); + bufstart = bufpos + 1; + unquoted = 0; + } + } else if (buf[bufpos] == quote) { + //process close quote + append_buffer_data(&value, &valuelen, buf + bufstart, bufpos - bufstart); + bufstart = bufpos + 1; + unquoted = 1; + } + //prepare for processing next character + lastchar = buf[bufpos]; + bufpos++; + } + //dump data at end of buffer and reset counters + append_buffer_data(&value, &valuelen, buf + bufstart, bufpos - bufstart); + bufstart = 0; + bufpos = 0; + } + //close .xlsx file + xlsxiowrite_close(xlsxiowrite); + } + } + //close CSV file + fclose(src); + } + } + return 0; +}