Permalink
Browse files

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().
  • Loading branch information...
1 parent 9353180 commit cbcfcabc8f9b7f0ca14c6dce2ee5644e4e80e5e4 @jmarshall jmarshall committed Jun 9, 2015
Showing with 84 additions and 1 deletion.
  1. +20 −0 kstring.c
  2. +7 −0 kstring.h
  3. +57 −1 test/kstring_test.c
View
@@ -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 *
**********************/
View
@@ -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
View
@@ -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) {

0 comments on commit cbcfcab

Please sign in to comment.