/** * Copyright (c) 2022 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "csv.h" void csvParse(char *string, csv_t *csv) { char c; size_t i, j, length; csvparsestate_t state; int32_t rowCellCount; length = strlen(string); csv->buffer = malloc(sizeof(char) * length * 2); csv->cellCounts = malloc(sizeof(int32_t) * CSV_ROW_COUNT_MAX); csv->rows = malloc(sizeof(char *) * 32 * CSV_ROW_COUNT_MAX); i = 0; j = 0; rowCellCount = 0; csv->rowCount = 0; state = CSV_PARSE_STATE_FIND_CELL; while(i < length) { c = string[i++]; // What are we doing switch(state) { case CSV_PARSE_STATE_FIND_CELL: if(c == '"') { state = CSV_PARSE_STATE_PARSE_CELL_WITH_QUOTES; csv->rows[(csv->rowCount * CSV_ROW_COUNT_MAX) + rowCellCount] = csv->buffer + j; rowCellCount++; continue; } else if(c == '\r' || c == '\n') { // Newline (todo: is this a blank line?) state = CSV_PARSE_STATE_LINE_END; continue; } else if(c == ',') { csv->rows[(csv->rowCount * CSV_ROW_COUNT_MAX) + rowCellCount] = csv->buffer + j; csv->buffer[j++] = '\0'; rowCellCount++; continue; } else { state = CSV_PARSE_STATE_PARSE_CELL; csv->rows[(csv->rowCount * CSV_ROW_COUNT_MAX) + rowCellCount] = csv->buffer + j; csv->buffer[j++] = c; rowCellCount++; continue; } case CSV_PARSE_STATE_PARSE_CELL: if(c == '\r' || c == '\n') { state = CSV_PARSE_STATE_LINE_END; csv->buffer[j++] = '\0'; continue; } else if(c == ',') { state = CSV_PARSE_STATE_FIND_CELL; csv->buffer[j++] = '\0'; continue; } csv->buffer[j++] = c; continue; case CSV_PARSE_STATE_PARSE_CELL_WITH_QUOTES: if((c == '\\' && string[i] == '"') || (c == '"' && string[i] == '"')) { // Handle escaped quotes. I normally see [\"] but excel does [""] in // most cases csv->buffer[j++] = '"'; i++; continue; } else if(c == '"') { // Handle end of quoted string state = CSV_PARSE_STATE_FIND_CELL; csv->buffer[j++] = '\0'; // Because we tend to do [",] at the end of a quoted cell, we do this // to prevent [,,] cases being treated the same if(string[i] == ',') i++; continue; } // Normal character. csv->buffer[j++] = c; continue; case CSV_PARSE_STATE_LINE_END: // Skip blanks if(c == '\r' || c == '\n') continue; csv->cellCounts[csv->rowCount] = rowCellCount; csv->rowCount++; rowCellCount = 0; state = CSV_PARSE_STATE_FIND_CELL; i--; continue; default: printf("Error occured during parse operation."); free(NULL); } } csv->buffer[j++] = '\0'; if(rowCellCount != 0) { csv->cellCounts[csv->rowCount] = rowCellCount; csv->rowCount++; } } void csvDispose(csv_t *csv) { free(csv->buffer); free(csv->cellCounts); free(csv->rows); }