From 7aed8551bb199f9aae6ceca4a015f0d2576007c8 Mon Sep 17 00:00:00 2001 From: Casper Date: Tue, 4 Oct 2022 09:58:51 +0200 Subject: Add centering by longest line (#3) Centering by longest line requires inputting all lines before starting output, to determine which is longest. Use a tail queue for this to insert at end and then iterate from start. The lines are freed after being output. Unlike the static read buffer, we can't reuse the list buffers in an easy way if we are iterating over multiple input files. So they need to be freed to avoid memory leaks. --- center.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/center.c b/center.c index edd33f6..06141b8 100644 --- a/center.c +++ b/center.c @@ -19,6 +19,7 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include @@ -32,7 +33,15 @@ #define ESC 033 +struct line_item { + char *buffer; + STAILQ_ENTRY(line_item) list; +}; + +STAILQ_HEAD(lines_head, line_item); + static void center(FILE *); +static void center_by_longest(FILE *); static int cols(void); static int utf8len(const char *); static int noesclen(const char *); @@ -50,6 +59,7 @@ main(int argc, char **argv) { int opt; char *endptr; + void (*centerfunc)(FILE *) = center; while ((opt = getopt(argc, argv, ":ew:")) != -1) { switch (opt) { @@ -65,6 +75,9 @@ main(int argc, char **argv) if (errno == ERANGE || width > INT_MAX) warnx("Potential overflow of given width"); break; + case 'l': + centerfunc = center_by_longest; + break; default: fprintf(stderr, "Usage: %s [-e] [-w width] [file ...]\n", argv[0]); exit(EXIT_FAILURE); @@ -78,17 +91,17 @@ main(int argc, char **argv) argv += optind; if (argc == 0) - center(stdin); + centerfunc(stdin); else do { if (strcmp(*argv, "-") == 0) - center(stdin); + centerfunc(stdin); else { FILE *fp; if ((fp = fopen(*argv, "r")) == NULL) { warn("fopen"); rval = EXIT_FAILURE; } else { - center(fp); + centerfunc(fp); fclose(fp); } } @@ -124,6 +137,63 @@ center(FILE *fp) } } +/* Same as center(), but aligns all lines according to the longest line. + * Great for centering code. + */ +void +center_by_longest(FILE *fp) +{ + static char *buffer = NULL; + static size_t bs = 0; + struct lines_head list_head; + struct line_item *line; + struct line_item *tmp; + size_t longest = 0; + size_t curr_len; + + STAILQ_INIT(&list_head); + + /* Collect all input lines in a list and find the longest */ + while (getline(&buffer, &bs, fp) != -1) { + line = malloc(sizeof(struct line_item)); + if (!line) { + warn("malloc"); + rval = EXIT_FAILURE; + return; + } + + line->buffer = buffer; + STAILQ_INSERT_TAIL(&list_head, line, list); + curr_len = lenfunc(buffer); + if (curr_len > longest) + longest = curr_len; + + /* Reset buffer and bs so getline can allocate a new buffer next time */ + buffer = NULL; + bs = 0; + } + + if (ferror(fp)) { + warn("getline"); + rval = EXIT_FAILURE; + return; + } + + /* Output lines aligned to the longest and free them */ + line = STAILQ_FIRST(&list_head); + while (line != NULL) { + int len = longest; + for (int i = ((width - len) / 2); i; i--) + putchar(' '); + fputs(line->buffer, stdout); + + tmp = STAILQ_NEXT(line, list); + free(line->buffer); + free(line); + line = tmp; + } +} + /* Return the column width of the output device if it is a TTY. If it is not a TTY then return -1 */ int cols(void) -- cgit v1.2.3