From: Jeremy Kerr Date: Tue, 10 Dec 2013 06:12:48 +0000 (+0800) Subject: lib/fold: Add text fold utility X-Git-Tag: v1.0.0~242 X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=commitdiff_plain;h=485680a5bfeb952fd652a59efcce35636d6aec00;hp=5a271400e9b5396cb90258b7582fe7ac6bcc2230 lib/fold: Add text fold utility 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 --- diff --git a/lib/Makefile.am b/lib/Makefile.am index 1c19e11..f6009cf 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 index 0000000..ec10c8c --- /dev/null +++ b/lib/fold/fold.c @@ -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 index 0000000..834fcf2 --- /dev/null +++ b/lib/fold/fold.h @@ -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 */ + diff --git a/test/lib/Makefile.am b/test/lib/Makefile.am index ae6027f..ed570af 100644 --- a/test/lib/Makefile.am +++ b/test/lib/Makefile.am @@ -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 index 0000000..1f58fdf --- /dev/null +++ b/test/lib/test-fold.c @@ -0,0 +1,157 @@ + +#include +#include +#include + +#include +#include +#include + +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; +}