On Sun, 01 May 2022 12:51:16 +0000
Roger Knecht <rkne...@pm.me> wrote:

> Add new applet which resembles the MS-DOS tree program to list directories 
> and files in a tree structure.
> 
> function                                             old     new   delta
> tree_print                                             -     388    +388
> .rodata                                            95678   95767     +89
> tree_main                                              -      73     +73
> tree_print_prefix                                      -      28     +28
> packed_usage                                       34417   34429     +12
> globals                                                -       8      +8
> applet_main                                         3192    3200      +8
> applet_names                                        2747    2752      +5
> ------------------------------------------------------------------------------
> (add/remove: 5/0 grow/shrink: 4/0 up/down: 611/0)             Total: 611 bytes
> 
> Signed-off-by: Roger Knecht <rkne...@pm.me>
> ---
> Changelog:
> 
> V4:
> - Rephrase commit message
> - Updated bloatcheck to latest master
> 
> V3:
> - Fixed symlink handling
> - Handle multiple directories in command line arguments
> - Extended tests for symlink and multiple directories
> - Reduced size by using libbb functions
> 
> V2:
> - Fixed tree help text
> - Reduced size by 644 bytes
> 
>  AUTHORS              |   3 +
>  miscutils/tree.c     | 135 +++++++++++++++++++++++++++++++++++++++++++
>  testsuite/tree.tests |  97 +++++++++++++++++++++++++++++++
>  3 files changed, 235 insertions(+)
>  create mode 100644 miscutils/tree.c
>  create mode 100755 testsuite/tree.tests
> 
> diff --git a/AUTHORS b/AUTHORS
> index 5c9a634c9..9ec0e2ee4 100644
> --- a/AUTHORS
> +++ b/AUTHORS
> @@ -181,3 +181,6 @@ Jie Zhang <jie.zh...@analog.com>
> 
>  Maxime Coste <ma...@kakoune.org>
>      paste implementation
> +
> +Roger Knecht <rkne...@pm.me>
> +    tree
> diff --git a/miscutils/tree.c b/miscutils/tree.c
> new file mode 100644
> index 000000000..e053e8483
> --- /dev/null
> +++ b/miscutils/tree.c
> @@ -0,0 +1,135 @@
> +/* vi: set sw=4 ts=4: */
> +/*
> + * Copyright (C) 2022 Roger Knecht <rkne...@pm.me>
> + *
> + * Licensed under GPLv2, see file LICENSE in this source tree.
> + */
> +//config:config TREE
> +//config:    bool "tree (0.6 kb)"
> +//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 ""
> +
> +#include "libbb.h"
> +
> +#define PREFIX_CHILD "├── "
> +#define PREFIX_LAST_CHILD "└── "
> +#define PREFIX_GRAND_CHILD "│   "
> +#define PREFIX_LAST_GRAND_CHILD "    "
> +#define DEFAULT_PATH "."
> +
> +struct directory {
> +     struct directory* parent;
> +     const char* prefix;
> +};
> +
> +static struct globals {
> +     int directories;
> +     int files;
> +} globals;
> +
> +static void tree_print_prefix(struct directory* directory) {
> +     if (directory) {
> +             tree_print_prefix(directory->parent);
> +             fputs_stdout(directory->prefix);
> +     }
> +}
> +
> +static void tree_print(const char* directory_name, struct directory* 
> directory) {
> +     struct dirent **entries, *dirent;
> +     struct directory child_directory;
> +     char* symlink_path;
> +     int index, size;
//> +   bool is_not_last, is_file;
> +
> +     // read directory entries
> +     size = scandir(directory_name, &entries, NULL, alphasort);
> +
> +     if (size < 0) {
> +             fputs_stdout(directory_name);
> +             puts(" [error opening dir]");
> +             return;
> +     }
> +
> +     // print directory name
> +     puts(directory_name);
> +
> +     // switch to sub directory
> +     xchdir(directory_name);
> +
> +     child_directory.parent = directory;
> +
> +     // print all directory entries
> +     for (index = 0; index < size; index++) {
> +             dirent = entries[index];
> +
> +             // filter hidden files and directories
Hi,
                        if (dirent->d_name[0] != '.') {
> +             if (strncmp(dirent->d_name, ".", 1) != 0) {

twisted logic?

> +                     is_file = !is_directory(dirent->d_name, 1);

is_not_last is used only once

> +                     is_not_last = (index + 1) < size;
> +                     symlink_path = xmalloc_readlink(dirent->d_name);
> +
> +                     // print tree line prefix
> +                     tree_print_prefix(directory);
> +
                                if ((index + 1) < size) {
> +                     if (is_not_last) {
> +                             child_directory.prefix = PREFIX_GRAND_CHILD;
> +                             fputs_stdout(PREFIX_CHILD);
> +                     } else {
> +                             child_directory.prefix = 
> PREFIX_LAST_GRAND_CHILD;
> +                             fputs_stdout(PREFIX_LAST_CHILD);
> +                     }
> +

Less lines maybe less size (untested)

                                if (is_directory(dirent->d_name, 1)) {
                                        globals.directories++;
                                        tree_print(dirent->d_name, 
&child_directory);
                                } else {
                                        globals.files++;
                                        printf((symlink) ? "%s -> %s\n" :  
"%s\n", dirent->d_name, symlink_path);
                                }
                                free(symlink_path);

Ciao,
Tito
                                        

> +                     // count directories and files
> +                     if (is_file)
> +                             globals.files++;
> +                     else
> +                             globals.directories++;
> +
> +                     if (symlink_path) {
> +                             // handle symlink
> +                             printf("%s -> %s\n", dirent->d_name, 
> symlink_path);
> +                             free(symlink_path);
> +                     } else if (is_file)
> +                             // handle file
> +                             puts(dirent->d_name);
> +                     else
> +                             // handle directory
> +                             tree_print(dirent->d_name, &child_directory);
> +             }
> +
> +             // release directory entry
> +             free(dirent);
> +     }
> +
> +     // release directory array
> +     free(entries);
> +
> +     // switch to parent directory
> +     xchdir("..");
> +}
> +
> +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +int tree_main(int argc, char **argv)
> +{
> +     if (argc == 1)
> +             // list current working directory
> +             tree_print(DEFAULT_PATH, NULL);
> +
> +     // list directories given as command line arguments
> +     while (*(++argv))
> +             tree_print(*argv, NULL);
> +
> +     // print statistic
> +     printf("\n%d directories, %d files\n", globals.directories, 
> globals.files);
> +
> +     return EXIT_SUCCESS;
> +}
> diff --git a/testsuite/tree.tests b/testsuite/tree.tests
> new file mode 100755
> index 000000000..bad28d46c
> --- /dev/null
> +++ b/testsuite/tree.tests
> @@ -0,0 +1,97 @@
> +#!/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
> +
> +(cd tree3.tempdir/test2/a && ln -s ../b/testfile4 .)
> +(cd tree3.tempdir/test2/b && ln -s ../../test3 .)
> +
> +testing "tree nested directories and files" \
> +     "cd tree3.tempdir && tree" \
> +     "\
> +.\n\
> +├── test1\n\
> +├── test2\n\
> +│   ├── a\n\
> +│   │   ├── testfile1\n\
> +│   │   ├── testfile2\n\
> +│   │   ├── testfile3\n\
> +│   │   └── testfile4 -> ../b/testfile4\n\
> +│   └── b\n\
> +│       ├── test3 -> ../../test3\n\
> +│       └── testfile4\n\
> +└── test3\n\
> +    ├── c\n\
> +    │   └── testfile5\n\
> +    └── d\n\
> +        └── testfile6\n\
> +\n\
> +8 directories, 7 files\n" \
> +     "" ""
> +
> +testing "tree multiple directories" \
> +     "tree tree2.tempdir tree3.tempdir" \
> +     "\
> +tree2.tempdir\n\
> +└── testfile\n\
> +tree3.tempdir\n\
> +├── test1\n\
> +├── test2\n\
> +│   ├── a\n\
> +│   │   ├── testfile1\n\
> +│   │   ├── testfile2\n\
> +│   │   ├── testfile3\n\
> +│   │   └── testfile4 -> ../b/testfile4\n\
> +│   └── b\n\
> +│       ├── test3 -> ../../test3\n\
> +│       └── testfile4\n\
> +└── test3\n\
> +    ├── c\n\
> +    │   └── testfile5\n\
> +    └── d\n\
> +        └── testfile6\n\
> +\n\
> +8 directories, 8 files\n" \
> +     "" ""
> +
> +rm -rf tree.tempdir tree2.tempdir tree3.tempdir
> +
> +exit $FAILCOUNT
> --
> 2.17.1
> 
> 

_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to