From cbcfcabc8f9b7f0ca14c6dce2ee5644e4e80e5e4 Mon Sep 17 00:00:00 2001 From: John Marshall Date: Tue, 9 Jun 2015 03:29:54 +0100 Subject: [PATCH] Add kgetline() to kstring.c/.h Similar to BSD's getline() but omits the \n terminator and manages the memory as a kstring. Call with "(kgets_func *) fgets" to read from stdio, or implement an fgets()-style function to read from other streams, e.g., a wrapper around gzgets() that reorders its parameters as per fgets(). --- kstring.c | 20 ++++++++++++++++++ kstring.h | 7 +++++++ test/kstring_test.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/kstring.c b/kstring.c index f029317..253d281 100644 --- a/kstring.c +++ b/kstring.c @@ -105,6 +105,26 @@ int ksplit_core(char *s, int delimiter, int *_max, int **_offsets) return n; } +int kgetline(kstring_t *s, kgets_func *fgets_fn, void *fp) +{ + size_t l0 = s->l; + + while (s->l == l0 || s->s[s->l-1] != '\n') { + if (s->m - s->l < 200) ks_resize(s, s->m + 200); + if (fgets_fn(s->s + s->l, s->m - s->l, fp) == NULL) break; + s->l += strlen(s->s + s->l); + } + + if (s->l == l0) return EOF; + + if (s->l > l0 && s->s[s->l-1] == '\n') { + s->l--; + if (s->l > l0 && s->s[s->l-1] == '\r') s->l--; + } + s->s[s->l] = '\0'; + return 0; +} + /********************** * Boyer-Moore search * **********************/ diff --git a/kstring.h b/kstring.h index 2567efc..f13fcd9 100644 --- a/kstring.h +++ b/kstring.h @@ -82,6 +82,13 @@ extern "C" { * if sep is not changed. */ char *kstrtok(const char *str, const char *sep, ks_tokaux_t *aux); + /* kgetline() uses the supplied fgets()-like function to read a "\n"- + * or "\r\n"-terminated line from fp. The line read is appended to the + * kstring without its terminator and 0 is returned; EOF is returned at + * EOF or on error (determined by querying fp, as per fgets()). */ + typedef char *kgets_func(char *, int, void *); + int kgetline(kstring_t *s, kgets_func *fgets, void *fp); + #ifdef __cplusplus } #endif diff --git a/test/kstring_test.c b/test/kstring_test.c index 76f9532..9f9b6e6 100644 --- a/test/kstring_test.c +++ b/test/kstring_test.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -37,7 +38,40 @@ void test_kputl(kstring_t *ks, long n) check("kputl()", ks, buf); } -int main() +static char *mem_gets(char *buf, int buflen, void *vtextp) +{ + const char **textp = (const char **) vtextp; + + const char *nl = strchr(*textp, '\n'); + size_t n = nl? nl - *textp + 1 : strlen(*textp); + + if (n == 0) return NULL; + + if (n > buflen-1) n = buflen-1; + memcpy(buf, *textp, n); + buf[n] = '\0'; + *textp += n; + return buf; +} + +void test_kgetline(kstring_t *ks, const char *text, ...) +{ + const char *exp; + va_list arg; + + va_start(arg, text); + while ((exp = va_arg(arg, const char *)) != NULL) { + ks->l = 0; + if (kgetline(ks, mem_gets, &text) != 0) kputs("EOF", ks); + check("kgetline()", ks, exp); + } + va_end(arg); + + ks->l = 0; + if (kgetline(ks, mem_gets, &text) == 0) check("kgetline()", ks, "EOF"); +} + +int main(int argc, char **argv) { kstring_t ks; @@ -65,6 +99,28 @@ int main() test_kputl(&ks, -LONG_MAX); test_kputl(&ks, LONG_MIN); + test_kgetline(&ks, "", NULL); + test_kgetline(&ks, "apple", "apple", NULL); + test_kgetline(&ks, "banana\n", "banana", NULL); + test_kgetline(&ks, "carrot\r\n", "carrot", NULL); + test_kgetline(&ks, "\n", "", NULL); + test_kgetline(&ks, "\n\n", "", "", NULL); + test_kgetline(&ks, "foo\nbar", "foo", "bar", NULL); + test_kgetline(&ks, "foo\nbar\n", "foo", "bar", NULL); + test_kgetline(&ks, + "abcdefghijklmnopqrstuvwxyz0123456789\nABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "abcdefghijklmnopqrstuvwxyz0123456789", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", NULL); + + if (argc > 1) { + FILE *f = fopen(argv[1], "r"); + if (f) { + for (ks.l = 0; kgetline(&ks, (kgets_func *)fgets, f) == 0; ks.l = 0) + puts(ks.s); + fclose(f); + } + } + free(ks.s); if (nfail > 0) {