lib/fold: Add text fold utility
authorJeremy Kerr <jk@ozlabs.org>
Tue, 10 Dec 2013 06:12:48 +0000 (14:12 +0800)
committerJeremy Kerr <jk@ozlabs.org>
Fri, 31 Jan 2014 00:46:34 +0000 (08:46 +0800)
We want to fold help text into the ncurses UI, so add a little module to
split text into lines.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
lib/Makefile.am
lib/fold/fold.c [new file with mode: 0644]
lib/fold/fold.h [new file with mode: 0644]
test/lib/Makefile.am
test/lib/test-fold.c [new file with mode: 0644]

index 1c19e11821f428ea2c0c4975760d0979a95312f8..f6009cfd84ee3a0e6dae988715cc67593ddb218f 100644 (file)
@@ -20,6 +20,8 @@ AM_CFLAGS = $(DEFAULT_CFLAGS)
 noinst_LTLIBRARIES = libpbcore.la
 
 libpbcore_la_SOURCES = \
+       fold/fold.h \
+       fold/fold.c \
        log/log.h \
        log/log.c \
        list/list.c \
diff --git a/lib/fold/fold.c b/lib/fold/fold.c
new file mode 100644 (file)
index 0000000..ec10c8c
--- /dev/null
@@ -0,0 +1,44 @@
+
+#include "fold/fold.h"
+
+void fold_text(const char *text,
+               int linelen,
+               int line_cb(void *arg, const char *start, int len),
+               void *arg)
+{
+       const char *start, *end, *sep;
+       int rc = 0;
+
+       start = end = sep = text;
+
+       while (!rc) {
+
+               if (*end == '\n') {
+                       rc = line_cb(arg, start, end - start);
+                       start = sep = ++end;
+
+               } else if (*end == '\0') {
+                       line_cb(arg, start, end - start);
+                       rc = 1;
+
+               } else if (end - start >= linelen - 1) {
+                       if (sep != start) {
+                               /* split on a previous word boundary, if
+                                * possible */
+                               rc = line_cb(arg, start, sep - start);
+                               start = end = ++sep;
+                       } else {
+                               /* otherwise, break the word */
+                               end++;
+                               rc = line_cb(arg, start, end - start);
+                               start = sep = end;
+                       }
+
+               } else {
+                       end++;
+                       /* record our last separator */
+                       if (*end == ' ')
+                               sep = end;
+               }
+       }
+}
diff --git a/lib/fold/fold.h b/lib/fold/fold.h
new file mode 100644 (file)
index 0000000..834fcf2
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *  Copyright (C) 2013 IBM Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef FOLD_H
+#define FOLD_H
+
+void fold_text(const char *text,
+               int linelen,
+               int line_cb(void *arg, const char *start, int len),
+               void *arg);
+
+#endif /* FOLD_H */
+
index ae6027f80eb2ff933bcd332451f87eb7b2a7d773..ed570af14605d5610424156d40dc6e947657bc83 100644 (file)
@@ -31,7 +31,8 @@ check_PROGRAMS = list-test \
        test-process-async \
        test-process-async-stdout \
        test-process-parent-stdout \
-       test-process-both
+       test-process-both \
+       test-fold
 
 TESTS = $(check_PROGRAMS)
 
diff --git a/test/lib/test-fold.c b/test/lib/test-fold.c
new file mode 100644 (file)
index 0000000..1f58fdf
--- /dev/null
@@ -0,0 +1,157 @@
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <fold/fold.h>
+#include <list/list.h>
+#include <talloc/talloc.h>
+
+struct line {
+       const char              *buf;
+       unsigned int            len;
+       struct list_item        list;
+};
+
+struct ctx {
+       struct list             lines;
+};
+
+struct test {
+       const char      *in;
+       unsigned int    linelen;
+       const char      *out[];
+};
+
+/* split on newline boundaries, no actual folding */
+struct test test_split = {
+       .in     = "Lorem ipsum dolor\nsit amet,\nconsectetuer\n",
+       .linelen = 20,
+       .out = {
+               "Lorem ipsum dolor",
+               "sit amet,",
+               "consectetuer",
+               "",
+               NULL,
+       },
+};
+
+/* fold a long line */
+struct test test_fold_line = {
+       .in = "Lorem ipsum dolor sit amet, consectetuer adipiscing "
+               "elit, sed diam nonummy nibh euismod tincidunt ut "
+               "laoreet dolore magna aliquam erat volutpat.",
+       .linelen = 20,
+       .out = {
+               "Lorem ipsum dolor",
+               "sit amet,",
+               "consectetuer",
+               "adipiscing elit,",
+               "sed diam nonummy",
+               "nibh euismod",
+               "tincidunt ut",
+               "laoreet dolore",
+               "magna aliquam erat",
+               "volutpat.",
+               NULL
+       },
+};
+
+/* break a word */
+struct test test_break = {
+       .in = "Lorem ipsum dolor sit amet, consectetuer",
+       .linelen = 10,
+       .out = {
+               "Lorem",
+               "ipsum",
+               "dolor sit",
+               "amet,",
+               "consectetu",
+               "er",
+               NULL
+       },
+};
+
+static struct test *tests[] = {
+       &test_split, &test_fold_line, &test_break,
+};
+
+static void __attribute__((noreturn)) fail(struct ctx *ctx,
+               struct test *test, const char *msg)
+{
+       struct line *line;
+       int i;
+
+       fprintf(stderr, "%s\n", msg);
+       fprintf(stderr, "input:\n%s\n", test->in);
+
+       fprintf(stderr, "expected:\n");
+       for (i = 0; test->out[i]; i++)
+               fprintf(stderr, "  '%s'\n", test->out[i]);
+
+       fprintf(stderr, "actual:\n");
+       list_for_each_entry(&ctx->lines, line, list) {
+               char *buf = talloc_strndup(ctx, line->buf, line->len);
+               fprintf(stderr, "  '%s'\n", buf);
+               talloc_free(buf);
+       }
+
+       exit(EXIT_FAILURE);
+}
+
+static int fold_line_cb(void *arg, const char *start, int len)
+{
+       struct ctx *ctx = arg;
+       struct line *line;
+
+       line = talloc(ctx, struct line);
+       line->buf = start;
+       line->len = len;
+       list_add_tail(&ctx->lines, &line->list);
+
+       return 0;
+}
+
+static void run_test(struct test *test)
+{
+       struct line *line;
+       struct ctx *ctx;
+       int i;
+
+       ctx = talloc(NULL, struct ctx);
+       list_init(&ctx->lines);
+       fold_text(test->in, test->linelen, fold_line_cb, ctx);
+
+       i = 0;
+       list_for_each_entry(&ctx->lines, line, list) {
+               if (!test->out[i])
+                       fail(ctx, test,
+                               "fold_text returned more lines than expected");
+
+               if (line->len > test->linelen)
+                       fail(ctx, test, "line too long");
+
+               if (line->len != strlen(test->out[i]))
+                       fail(ctx, test, "line lengths differ");
+
+               if (strncmp(line->buf, test->out[i], line->len))
+                       fail(ctx, test, "line data differs");
+
+               i++;
+       }
+
+       if (test->out[i])
+               fail(ctx, test, "fold_text returned fewer lines than expected");
+
+       talloc_free(ctx);
+}
+
+int main(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(tests); i++)
+               run_test(tests[i]);
+
+       return EXIT_SUCCESS;
+}