dtree

Програм из командне линије за исцртавање стабала
git clone https://git.sr.ht/~strahinja/dtree
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување d22b9ddf144065211fda9ad4e01e0a4a04686f8c
родитељ 2b009519424711861dc3721f17547871d06064f6
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Sat, 29 Jun 2024 08:51:50 +0200

Apply optimizations

Diffstat:
Mdtree.c | 205+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Atest/test-003.txt | 6++++++
измењених датотека: 2, додавања: 119(+), брисања: 92(-)

diff --git a/dtree.c b/dtree.c @@ -2,6 +2,8 @@ * any later version. Copyright (C) 2023-2024 Страхиња Радић. * See the file LICENSE for exact copyright and license details. */ +#include <errno.h> +#include <limits.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -11,7 +13,8 @@ #include "utf8.h" #include "version.h" -#define BUFSIZE 4096 +#define LINE_DEFAULT 85 +#define LINE_ALLOC_DELTA ((96 - LINE_DEFAULT) + 96) #define COPYRIGHT \ (" This program is licensed under the terms of GNU GPL v3" \ " or (at your option)\n" \ @@ -34,22 +37,24 @@ struct Node { enum { TREE_SYMBOLS_ASCII, TREE_SYMBOLS_SINGLE, TREE_SYMBOLS_DOUBLE }; -struct Node* alloc_node(void); -void cleanup(void); -int error(int code, char* fmt, ...); -void free_node(struct Node** root); -void insert_node(struct Node** root, const int level, const char* label); -void print_indent(void); -void print_node(const struct Node* node); -void set_node(struct Node* node, const int level, const char* label); -size_t strlcat(char* dst, const char* src, size_t dsize); -size_t strlcpy(char* dst, const char* src, size_t dsize); -int string_to_symbol_set(const char* s); -void usage(void); -void version(const int full); -void warning(char* fmt, ...); +static struct Node* alloc_node(void); +static void cleanup(void); +static int error(int code, char* fmt, ...); +static void free_node(struct Node** root); +static void insert_node(struct Node** root, const int level, const char* label); +static void print_indent(void); +static void print_node(const struct Node* node); +static void set_node(struct Node* node, const int level, const char* label); +static void usage(void); +static void version(const int full); +static void warning(char* fmt, ...); /* clang-format off */ +static const int string_to_symbol_set[] = { + ['a'] = TREE_SYMBOLS_ASCII, + ['d'] = TREE_SYMBOLS_DOUBLE, + ['s'] = TREE_SYMBOLS_SINGLE +}; static const u32 tree_symbols[][5] = { [TREE_SYMBOLS_ASCII] = { (u32)L'`', (u32)L'+', (u32)L'-', @@ -71,21 +76,22 @@ static u32* pindent = NULL; static struct Node* root = NULL; static int symbol_set = TREE_SYMBOLS_SINGLE; -struct Node* +static struct Node* alloc_node(void) { struct Node* newnode = NULL; - newnode = (struct Node*)calloc(sizeof(struct Node), 1); + newnode = malloc(sizeof(struct Node)); if (!newnode) { - perror("calloc"); - exit(error(1, "Cannot alloc")); + perror(PROGRAMNAME ": malloc"); + exit(1); } newnode->level = 0; newnode->label = NULL; newnode->parent = NULL; + newnode->children = NULL; newnode->children_count = 0; newnode->last_child = NULL; newnode->next = NULL; @@ -93,18 +99,18 @@ alloc_node(void) return newnode; } -void +static void cleanup(void) { free_node(&root); - // free(root); + /* free(root); */ free(indent); } -int +static int error(int code, char* fmt, ...) { - char buf[BUFSIZE]; + char buf[LINE_DEFAULT]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof buf, (const char*)fmt, args); @@ -113,7 +119,7 @@ error(int code, char* fmt, ...) return code; } -void +static void free_node(struct Node** root) { if (!root || !*root) @@ -131,7 +137,7 @@ free_node(struct Node** root) free(*root); } -void +static void insert_node(struct Node** root, const int level, const char* label) { if (!root || !*root) @@ -155,11 +161,15 @@ insert_node(struct Node** root, const int level, const char* label) last->next = newnode; } + (*root)->last_child = newnode; } else if ((*root)->level + 1 < level) { if (!(*root)->last_child) - exit(error(1, "Fatal: inserting node >1 levels below")); + exit(error(1, + "Fatal: inserting node '%s' >1" + " levels below '%s'", + label, (*root)->label)); insert_node(&(*root)->last_child, level, label); } @@ -189,7 +199,7 @@ insert_node(struct Node** root, const int level, const char* label) } } -void +static void print_indent(void) { const u32* ppindent = indent; @@ -205,7 +215,7 @@ print_indent(void) } } -void +static void print_node(const struct Node* node) { if (!node) @@ -248,55 +258,32 @@ print_node(const struct Node* node) print_node(node->next); } -void +static void set_node(struct Node* node, const int level, const char* label) { size_t size; size = strlen(label) + 1; if (!node->label) { - node->label = (char*)calloc(1, size); + node->label = malloc(size); if (!node->label) { - perror("calloc"); - exit(error(1, "Cannot alloc")); + perror(PROGRAMNAME ": malloc"); + exit(1); } } - if (strlcpy(node->label, label, size) >= size) - exit(error(1, "strlcpy:%d: Overflow", __LINE__)); + if (!memccpy(node->label, label, 0, size)) + node->label[size - 1] = 0; node->level = level; } -int -string_to_symbol_set(const char* s) -{ - int ret; - - switch (*s) - { - case 'a': - ret = TREE_SYMBOLS_ASCII; - break; - case 's': - ret = TREE_SYMBOLS_SINGLE; - break; - case 'd': - ret = TREE_SYMBOLS_DOUBLE; - break; - default: - ret = -1; - } - - return ret; -} - -void +static void usage(void) { printf("Usage: %s [-hVv] | [-s a|d|s] file\n", PROGRAMNAME); } -void +static void version(const int full) { printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE); @@ -304,10 +291,10 @@ version(const int full) puts(COPYRIGHT); } -void +static void warning(char* fmt, ...) { - char buf[BUFSIZE]; + char buf[LINE_DEFAULT]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof buf, (const char*)fmt, args); @@ -320,6 +307,13 @@ main(int argc, char** argv) { int ch; FILE* input = NULL; + char* line = NULL; + ssize_t line_delta; + ssize_t line_size; + char* pline = NULL; + char* tpline = NULL; /* Temporary */ + char* eol = NULL; + int level = 0; #ifdef __OpenBSD__ if (pledge("stdio rpath unveil", NULL) < 0) @@ -337,7 +331,8 @@ main(int argc, char** argv) usage(); exit(0); case 's': - if ((symbol_set = string_to_symbol_set(optarg)) == -1) + if ((symbol_set = string_to_symbol_set[(int)*optarg]) + == -1) exit(error(1, "Invalid symbol set: '%s'", optarg)); break; @@ -376,53 +371,79 @@ main(int argc, char** argv) #endif if (!(input = fopen(argv[optind], "r"))) { - perror("fopen"); + perror(PROGRAMNAME ": fopen"); exit(error(1, "Can't open '%s'", argv[optind])); } } - while (!feof(input)) + errno = 0; + line_delta = sysconf(_SC_LINE_MAX); + if (line_delta == -1) { - char line[BUFSIZE]; - char* pline = NULL; - char* eol = NULL; - int level = 0; - - if (!fgets(line, BUFSIZE, input)) - break; - - eol = strchr(line, '\n'); - if (eol) - *eol = 0; + if (errno) + { + perror("sysconf"); + exit(1); + } + line_delta = _POSIX2_LINE_MAX; + } + line_size = line_delta; + line = malloc(line_size); + pline = line; +do_input: + if (feof(input)) + goto done_input; + if (!fgets(pline, line_size - (pline - line), input)) + goto done_input; + + eol = strchr(line, '\n'); + if (eol) + *eol = 0; + else + { + line_size += line_delta; + if (!(tpline = realloc(line, line_size))) + { + perror(PROGRAMNAME ": realloc"); + exit(1); + } + line = tpline; + pline = line + (line_size - line_delta - 1); + goto do_input; + } - pline = line; - while (*pline) + pline = line; + level = 0; + while (*pline) + { + if (*pline == '\t') + level++; + else { - if (*pline == '\t') - level++; - else + if (!root) { - if (!root) - { - root = alloc_node(); - set_node(root, level, pline); - } - else - insert_node(&root, level, pline); - break; + root = alloc_node(); + set_node(root, level, pline); } - pline++; + else + insert_node(&root, level, pline); + break; } + pline++; } + pline = line; + goto do_input; +done_input: + free(line); if (input != stdin) fclose(input); - indent = (u32*)calloc(sizeof(u32), BUFSIZE); + indent = calloc(sizeof(u32), line_size); if (!indent) { - perror("calloc"); - exit(error(1, "Cannot alloc")); + perror(PROGRAMNAME ": calloc"); + exit(1); } pindent = indent; diff --git a/test/test-003.txt b/test/test-003.txt @@ -0,0 +1,6 @@ +This is the root node + This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of |a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word This is a test of a line longer than 85 characters. The characters should be loaded correctly and no word should be "glued" to the previous word + Another subnode + Subitem 1 + Subitem 2 + Yet another subnode