On Sat, 16 Apr 2022 16:54:19 +0000 Roger Knecht <rkne...@pm.me> wrote:
> Adds the tree program to list directories and files in a tree structure. > > function old new delta > static.tree_print - 352 +352 > .rodata 95677 95756 +79 > tree_main - 69 +69 > globals - 24 +24 > applet_main 3192 3200 +8 > applet_names 2747 2752 +5 > packed_usage 34414 34396 -18 > ------------------------------------------------------------------------------ > (add/remove: 4/0 grow/shrink: 3/1 up/down: 537/-18) Total: 519 bytes > --- > As Ron pointed out V1 was overwriting the find help text and therefore > invalidating > the bloatcheck. V1 added 1171 bytes (not 349 bytes as originally mentioned). > > V2 is reducing the size by using scandir(), chdir() and avoiding string > concatenations. > > Changelog: > > V2: > - Fixed tree help text > - Reduced size by 652 bytes Hi, just out of curiosity, for fun and to refresh my C skills I wrote an alternative version of tree that is somewhat similar to yours in using scandir. One problem I see in your V2 is that unlike real tree it does not handle multiple paths as command line args: tree dir dir2 dirn Also the use of DT_DIR is problematic as only some filesystems have full support. Bloatcheck is: function old new delta tree - 501 +501 tree_main - 125 +125 .rodata 99024 99106 +82 packed_usage 34414 34448 +34 applet_main 3192 3200 +8 applet_names 2747 2752 +5 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 4/0 up/down: 755/0) Total: 755 bytes text data bss dec hex filename 1002271 16403 1848 1020522 f926a busybox_old 1003180 16427 1848 1021455 f960f busybox_unstripped Features are: 1) correct link handling: ./busybox tree ../../test/ ../../test/ ├── test1 │ └── a ├── test2 │ ├── b │ ├── c │ └── test3 ├── test4 -> test2 └── test5 2) Uses scandir and passes the testsuite. 3) It is worth mentioning that by using alphasort as sorting algorithm the output of complex trees is somewhat different in the ordering of files ( for example uppercase , lowercase) as the real tree program. For easier review code is attached as patch and also in the mail body. #include "libbb.h" #define HIDDEN(s) ((s)[0] == '.') #define CHILD "├── " #define LAST_CHILD "└── " #define GRAND_CHILD "│ " #define LAST_GRAND_CHILD " " #define MAX_CHILD_SIZE (MAX(MAX(MAX((sizeof(CHILD)), \ (sizeof(LAST_CHILD))), \ (sizeof(GRAND_CHILD))), \ (sizeof(LAST_GRAND_CHILD)))) static void tree(char *path, int *dcount, int *fcount, char **list) { int i = 0; int n; int l; char *fullpath = NULL; char *linkname = NULL; struct dirent **namelist; if ((n = scandir(path, &namelist, NULL, alphasort)) < 0) { printf("%s [error opening dir]\n", path); return; } else if (!**list) { printf("%s\n", path); } l = strlen(*list); while (i < n) { if (!DOT_OR_DOTDOT(namelist[i]->d_name) && !HIDDEN(namelist[i]->d_name)) { *list = xrealloc(*list, l + MAX_CHILD_SIZE); strcpy(*list + l, (i == (n - 1)) ? LAST_CHILD : CHILD); printf("%s%s", *list, namelist[i]->d_name); fullpath = concat_path_file(path, namelist[i]->d_name); if((linkname = xmalloc_readlink(fullpath)) != NULL) { printf(" -> %s", linkname); } bb_putchar('\n'); if (is_directory(fullpath, /*followLinks*/ 1)) { (*dcount)++; strcpy(*list + l, (i == (n - 1)) ? LAST_GRAND_CHILD : GRAND_CHILD); if (linkname == NULL) { tree(fullpath, dcount, fcount, list); } } else { (*fcount)++; } free(linkname); free(fullpath); } free(namelist[i]); i++; } strcpy(*list + l, ""); free(namelist); } int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int dircount = 0; int filecount = 0; char *list = xzalloc(1); if (argc == 1) { *argv = (char *) "."; } else { argv++; } do { tree(*argv, &dircount, &filecount, &list); } while (*++argv); printf("\n%d directories, %d files\n", dircount, filecount); if (ENABLE_FEATURE_CLEAN_UP) { free(list); } return EXIT_SUCCESS; } Hints on how to shrink it even more, improvements and critics are welcome! Enjoy. Ciao, Tito
Busybox version of tree, lists contents of directories in a tree-like format. Tests written by Roger Knecht <rkne...@pm.me>. Signed-off-by: Tito Ragusa <farmat...@tiscali.it> --- /dev/null 2022-04-16 12:45:08.784170006 +0200 +++ busybox/miscutils/tree.c 2022-04-16 18:27:07.242306963 +0200 @@ -0,0 +1,110 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2022 Tito Ragusa <farmat...@tiscali.it> + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config TREE +//config: bool "tree" +//config: default n +//config: help +//config: List files and directories in a tree structure. +//config: + +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TREE) += tree.o + +//usage:#define tree_trivial_usage NOUSAGE_STR +//usage:#define tree_full_usage "" + +//usage:#define tree_trivial_usage +//usage: "[PATH]" +//usage:#define tree_full_usage "\n\n" +//usage: "Print files and directories in a tree structure." +//usage: "Defaults: PATH is current directory\n" + +#include "libbb.h" + +#define HIDDEN(s) ((s)[0] == '.') + +#define CHILD "âââ " +#define LAST_CHILD "âââ " +#define GRAND_CHILD "â  " +#define LAST_GRAND_CHILD " " + +#define MAX_CHILD_SIZE (MAX(MAX(MAX((sizeof(CHILD)), \ + (sizeof(LAST_CHILD))), \ + (sizeof(GRAND_CHILD))), \ + (sizeof(LAST_GRAND_CHILD)))) + +static void tree(char *path, int *dcount, int *fcount, char **list) +{ + int i = 0; + int n; + int l; + char *fullpath = NULL; + char *linkname = NULL; + struct dirent **namelist; + + if ((n = scandir(path, &namelist, NULL, alphasort)) < 0) { + printf("%s [error opening dir]\n", path); + return; + } else if (!**list) { + printf("%s\n", path); + } + + l = strlen(*list); + + while (i < n) { + if (!DOT_OR_DOTDOT(namelist[i]->d_name) && !HIDDEN(namelist[i]->d_name)) { + *list = xrealloc(*list, l + MAX_CHILD_SIZE); + strcpy(*list + l, (i == (n - 1)) ? LAST_CHILD : CHILD); + printf("%s%s", *list, namelist[i]->d_name); + fullpath = concat_path_file(path, namelist[i]->d_name); + if((linkname = xmalloc_readlink(fullpath)) != NULL) { + printf(" -> %s", linkname); + } + bb_putchar('\n'); + if (is_directory(fullpath, /*followLinks*/ 1)) { + (*dcount)++; + strcpy(*list + l, (i == (n - 1)) ? LAST_GRAND_CHILD : GRAND_CHILD); + if (linkname == NULL) { + tree(fullpath, dcount, fcount, list); + } + } else { + (*fcount)++; + } + free(linkname); + free(fullpath); + } + free(namelist[i]); + i++; + } + strcpy(*list + l, ""); + free(namelist); +} + +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + int dircount = 0; + int filecount = 0; + char *list = xzalloc(1); + + if (argc == 1) { + *argv = (char *) "."; + } else { + argv++; + } + + do { + tree(*argv, &dircount, &filecount, &list); + } while (*++argv); + printf("\n%d directories, %d files\n", dircount, filecount); + if (ENABLE_FEATURE_CLEAN_UP) { + free(list); + } + return EXIT_SUCCESS; +} + --- /dev/null 2022-04-16 12:45:08.784170006 +0200 +++ busybox/testsuite/tree.tests 2022-04-10 21:05:22.643361410 +0200 @@ -0,0 +1,68 @@ +#!/bin/sh + +# Copyright 2022 by Roger Knecht <rkne...@pm.me> +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh -v + +# testing "description" "command" "result" "infile" "stdin" + +testing "tree error opening dir" \ + "tree tree.tempdir" \ + "\ +tree.tempdir [error opening dir]\n\ +\n\ +0 directories, 0 files\n" \ + "" "" + +mkdir -p tree2.tempdir +touch tree2.tempdir/testfile + +testing "tree single file" \ + "cd tree2.tempdir && tree" \ + "\ +.\n\ +âââ testfile\n\ +\n\ +0 directories, 1 files\n" \ + "" "" + +mkdir -p tree3.tempdir/test1 \ + tree3.tempdir/test2/a \ + tree3.tempdir/test2/b \ + tree3.tempdir/test3/c \ + tree3.tempdir/test3/d + +touch tree3.tempdir/test2/a/testfile1 \ + tree3.tempdir/test2/a/testfile2 \ + tree3.tempdir/test2/a/testfile3 \ + tree3.tempdir/test2/b/testfile4 \ + tree3.tempdir/test3/c/testfile5 \ + tree3.tempdir/test3/d/testfile6 \ + tree3.tempdir/test3/d/testfile7 + +testing "tree nested directories and files" \ + "cd tree3.tempdir && tree" \ + "\ +.\n\ +âââ test1\n\ +âââ test2\n\ +â  âââ a\n\ +â  â  âââ testfile1\n\ +â  â  âââ testfile2\n\ +â  â  âââ testfile3\n\ +â  âââ b\n\ +â  âââ testfile4\n\ +âââ test3\n\ + âââ c\n\ + â  âââ testfile5\n\ + âââ d\n\ + âââ testfile6\n\ + âââ testfile7\n\ +\n\ +7 directories, 7 files\n" \ + "" "" + +rm -rf tree.tempdir tree2.tempdir tree3.tempdir + +exit $FAILCOUNT
_______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox