diff options
author | Casper Andersson <casper.casan@gmail.com> | 2022-10-04 09:58:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-04 09:58:51 +0200 |
commit | 7aed8551bb199f9aae6ceca4a015f0d2576007c8 (patch) | |
tree | 78956f73bb8c035573778cb418dca6045872d389 | |
parent | 4cf46a797145b378dc543346403d54b72b9d2a83 (diff) |
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.
-rw-r--r-- | center.c | 76 |
1 files changed, 73 insertions, 3 deletions
@@ -19,6 +19,7 @@ #define _POSIX_C_SOURCE 200809L #include <sys/ioctl.h> +#include <sys/queue.h> #include <ctype.h> #include <err.h> @@ -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) |