[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    [PATCH v5 RESEND] tree: new applet
From:       Roger Knecht <rknecht () pm ! me>
Date:       2022-05-21 9:59:04
Message-ID: 20220521095844.10261-1-rknecht () pm ! me
[Download RAW message or body]

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
tree_main                                              -      73     +73
.rodata                                            95703   95776     +73
tree_print_prefix                                      -      28     +28
globals                                                -       8      +8
applet_main                                         3200    3208      +8
packed_usage                                       34431   34438      +7
applet_names                                        2753    2758      +5
applet_suid                                          100     101      +1
applet_install_loc                                   200     201      +1
------------------------------------------------------------------------------
(add/remove: 5/0 grow/shrink: 6/0 up/down: 592/0)             Total: 592 bytes

Signed-off-by: Roger Knecht <rknecht@pm.me>
---
Changelog:

V5:
- Added ASCII only build option (new default)
- Simplified code

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     | 148 +++++++++++++++++++++++++++++++++++++++++++
 testsuite/tree.tests |  97 ++++++++++++++++++++++++++++
 3 files changed, 248 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.zhang@analog.com>

 Maxime Coste <mawww@kakoune.org>
     paste implementation
+
+Roger Knecht <rknecht@pm.me>
+    tree
diff --git a/miscutils/tree.c b/miscutils/tree.c
new file mode 100644
index 000000000..cd1cf2c25
--- /dev/null
+++ b/miscutils/tree.c
@@ -0,0 +1,148 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2022 Roger Knecht <rknecht@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:
+//config:config FEATURE_TREE_UNICODE
+//config:       bool "Print tree with unicode characters"
+//config:       default n
+//config:       depends on TREE
+//config:       help
+//config:       Print tree with unicode characters
+
+//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 DEFAULT_PATH "."
+
+#if ENABLE_FEATURE_TREE_UNICODE
+#define PREFIX_CHILD "├── "
+#define PREFIX_LAST_CHILD "└── "
+#define PREFIX_GRAND_CHILD "│   "
+#define PREFIX_LAST_GRAND_CHILD "    "
+#else
+#define PREFIX_CHILD "|-- "
+#define PREFIX_LAST_CHILD "`-- "
+#define PREFIX_GRAND_CHILD "|   "
+#define PREFIX_LAST_GRAND_CHILD "    "
+#endif
+
+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_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
+		if (dirent->d_name[0] != '.') {
+			is_file = !is_directory(dirent->d_name, 1);
+			symlink_path = xmalloc_readlink(dirent->d_name);
+
+			// print tree line prefix
+			tree_print_prefix(directory);
+
+			if (index + 1 < size) {
+				child_directory.prefix = PREFIX_GRAND_CHILD;
+				fputs_stdout(PREFIX_CHILD);
+			} else {
+				child_directory.prefix = PREFIX_LAST_GRAND_CHILD;
+				fputs_stdout(PREFIX_LAST_CHILD);
+			}
+
+			// 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..dead5f9a1
--- /dev/null
+++ b/testsuite/tree.tests
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+# Copyright 2022 by Roger Knecht <rknecht@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

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic