]> git.kaiwu.me - klib.git/commitdiff
Add kgetline() to kstring.c/.h
authorJohn Marshall <jm18@sanger.ac.uk>
Tue, 9 Jun 2015 02:29:54 +0000 (03:29 +0100)
committerJohn Marshall <jm18@sanger.ac.uk>
Thu, 23 Jul 2015 09:31:57 +0000 (10:31 +0100)
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
kstring.h
test/kstring_test.c

index f0293172a8fd6c739271c6a117379df794d94aa1..253d2815396acb84cdc74b9dfb53c921067876a5 100644 (file)
--- 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 *
  **********************/
index 2567efcb721f273f82f435b291abea7c7565dd1c..f13fcd99f57e1f782d17de8c6b97a4b8e9e3b0c6 100644 (file)
--- 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
index 76f9532e7f569c95bb748bde5d7e115f5d6c4978..9f9b6e63ca6eba1c3a662cedaee7613057eea2af 100644 (file)
@@ -1,4 +1,5 @@
 #include <limits.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -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) {