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().
This commit is contained in:
John Marshall 2015-06-09 03:29:54 +01:00
parent 93531803a8
commit cbcfcabc8f
3 changed files with 84 additions and 1 deletions

View File

@ -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 File

@ -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 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) {