diff --git a/c/input/line_readers.c b/c/input/line_readers.c index 9caaf7cd0..2f881bd1b 100644 --- a/c/input/line_readers.c +++ b/c/input/line_readers.c @@ -90,7 +90,7 @@ char* mlr_get_sline(FILE* fp, char* irs, int irslen) { } // ---------------------------------------------------------------- -int local_getdelim(char** restrict pline, size_t* restrict plinecap, int delimiter, FILE* restrict stream) { +ssize_t local_getdelim(char** restrict pline, size_t* restrict plinecap, int delimiter, FILE* restrict stream) { size_t linecap = INITIAL_SIZE; char* restrict line = mlr_malloc_or_die(INITIAL_SIZE); char* restrict p = line; @@ -106,26 +106,32 @@ int local_getdelim(char** restrict pline, size_t* restrict plinecap, int delimit } c = mlr_arch_getc(stream); if (c == delimiter) { - *(p++) = 0; + *(p++) = delimiter; break; } else if (c == EOF) { if (p == line) eof = TRUE; - *p = 0; break; } else { *(p++) = c; } } + // xxx check length + size_t offset = p - line; + if (offset >= linecap) { + linecap = linecap + 1; + line = mlr_realloc_or_die(line, linecap); + p = line + offset; + } + p[1] = 0; + + *pline = line; + *plinecap = linecap; if (eof) { - free(line); - *pline = NULL; - *plinecap = 0; + **pline = 0; return -1; } else { - *pline = line; - *plinecap = linecap; return p - line; } } diff --git a/c/input/line_readers.h b/c/input/line_readers.h index d5f669621..12e61b4e0 100644 --- a/c/input/line_readers.h +++ b/c/input/line_readers.h @@ -22,6 +22,6 @@ char* mlr_get_sline(FILE* input_stream, char* irs, int irslen); // getdelim is built-in on OSX and modern unix-like OSs. For MSYS2, we need to // roll our own. The function is exposed publicly here, rather than privately // inside mlr_arch.c, for unit-testing visibility. -int local_getdelim(char** restrict pline, size_t* restrict plinecap, int delimiter, FILE* restrict stream); +ssize_t local_getdelim(char** restrict pline, size_t* restrict plinecap, int delimiter, FILE* restrict stream); #endif // LINE_READERS_H diff --git a/c/unit_test/test_line_readers.c b/c/unit_test/test_line_readers.c index 3078249a4..fece80cf8 100644 --- a/c/unit_test/test_line_readers.c +++ b/c/unit_test/test_line_readers.c @@ -13,6 +13,8 @@ int tests_failed = 0; int assertions_run = 0; int assertions_failed = 0; +typedef ssize_t getdelim_t(char** restrict pline, size_t* restrict plinecap, int delimiter, FILE* restrict stream); + // ---------------------------------------------------------------- static FILE* fopen_or_die(char* filename) { FILE* fp = fopen(filename, "r"); @@ -24,20 +26,7 @@ static FILE* fopen_or_die(char* filename) { return fp; } -// ---------------------------------------------------------------- -// This tests our homemade getdelim replacement, for running on Windows which lacks getdelim. - -static char* test_local_getdelim() { - - return NULL; -} - -// ---------------------------------------------------------------- -// On unixish platforms this is testing the system getdelim() which is correct by definition. -// (At least, we verify it's behaving as we expect.) On Windows, this tests our local_getdelim -// which is a radundant test -- but again, it confirms behavior is as expected. - -static char* test_getdelim() { +static char* test_getdelim_impl(getdelim_t* pgetdelim) { char delimiter = '\n'; char* contents = NULL; char* path = NULL; @@ -54,7 +43,7 @@ static char* test_getdelim() { // Read line line = NULL; linecap = 0; - rc = getdelim(&line, &linecap, delimiter, fp); + rc = (*pgetdelim)(&line, &linecap, delimiter, fp); mu_assert_lf(rc == -1); mu_assert_lf(streq(line, "")); mu_assert_lf(linecap >= 1+strlen(contents)); @@ -62,7 +51,7 @@ static char* test_getdelim() { // Read past EOF line = NULL; linecap = 0; - rc = getdelim(&line, &linecap, delimiter, fp); + rc = (*pgetdelim)(&line, &linecap, delimiter, fp); mu_assert_lf(rc == -1); mu_assert_lf(streq(line, "")); @@ -78,7 +67,7 @@ static char* test_getdelim() { // Read line line = NULL; linecap = 0; - rc = getdelim(&line, &linecap, delimiter, fp); + rc = (*pgetdelim)(&line, &linecap, delimiter, fp); mu_assert_lf(rc == 1); mu_assert_lf(streq(line, "\n")); mu_assert_lf(linecap >= 1+strlen(contents)); @@ -86,7 +75,7 @@ static char* test_getdelim() { // Read past EOF line = NULL; linecap = 0; - rc = getdelim(&line, &linecap, delimiter, fp); + rc = (*pgetdelim)(&line, &linecap, delimiter, fp); mu_assert_lf(rc == -1); mu_assert_lf(streq(line, "")); @@ -102,7 +91,7 @@ static char* test_getdelim() { // Read line line = NULL; linecap = 0; - rc = getdelim(&line, &linecap, delimiter, fp); + rc = (*pgetdelim)(&line, &linecap, delimiter, fp); mu_assert_lf(rc == 2); mu_assert_lf(streq(line, "\r\n")); mu_assert_lf(linecap >= 1+strlen(contents)); @@ -110,7 +99,7 @@ static char* test_getdelim() { // Read past EOF line = NULL; linecap = 0; - rc = getdelim(&line, &linecap, delimiter, fp); + rc = (*pgetdelim)(&line, &linecap, delimiter, fp); mu_assert_lf(rc == -1); mu_assert_lf(streq(line, "")); @@ -121,10 +110,24 @@ static char* test_getdelim() { return NULL; } +// ---------------------------------------------------------------- +// On unixish platforms this is testing the system getdelim() which is correct by definition. +// (At least, we verify it's behaving as we expect.) On Windows, this tests our local_getdelim +// which is a radundant test -- but again, it confirms behavior is as expected. +static char* test_getdelim() { + return test_getdelim_impl(&mlr_arch_getdelim); +} + +// ---------------------------------------------------------------- +// This tests our homemade getdelim replacement, for running on Windows which lacks getdelim. +static char* test_local_getdelim() { + return test_getdelim_impl(&local_getdelim); +} + // ================================================================ static char * run_all_tests() { - mu_run_test(test_local_getdelim); mu_run_test(test_getdelim); + mu_run_test(test_local_getdelim); return 0; }