* 1. requires lstat (BSD) - how do you do it without?
*/
-#include <getopt.h>
#include "libbb.h"
+#if ENABLE_FEATURE_ASSUME_UNICODE
+#include <wchar.h>
+#endif
+
/* This is a NOEXEC applet. Be very careful! */
#define ATTR(mode) ("\00\00\01\00\01\00\01\00"\
"\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
-/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
-#if ENABLE_FEATURE_LS_COLOR
-static int show_color;
-/* long option entry used only for --color, which has no short option
- * equivalent */
-static const struct option ls_color_opt[] = {
- { "color", optional_argument, NULL, 1 },
- { NULL, 0, NULL, 0 }
-};
-#else
-enum { show_color = 0 };
-#endif
-
/*
* a directory entry and its stat info are stored here
*/
USE_SELINUX(security_context_t sid;)
struct dnode *next; /* point at the next node */
};
-typedef struct dnode dnode_t;
static struct dnode **list_dir(const char *);
static struct dnode **dnalloc(int);
-static int list_single(struct dnode *);
+static int list_single(const struct dnode *);
-static unsigned all_fmt;
+struct globals {
+#if ENABLE_FEATURE_LS_COLOR
+ smallint show_color;
+#endif
+ smallint exit_code;
+ unsigned all_fmt;
#if ENABLE_FEATURE_AUTOWIDTH
-static unsigned tabstops = COLUMN_GAP;
-static unsigned terminal_width = TERMINAL_WIDTH;
+ unsigned tabstops; // = COLUMN_GAP;
+ unsigned terminal_width; // = TERMINAL_WIDTH;
+#endif
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+ /* Do time() just once. Saves one syscall per file for "ls -l" */
+ time_t current_time_t;
+#endif
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#if ENABLE_FEATURE_LS_COLOR
+#define show_color (G.show_color )
+#else
+enum { show_color = 0 };
+#endif
+#define exit_code (G.exit_code )
+#define all_fmt (G.all_fmt )
+#if ENABLE_FEATURE_AUTOWIDTH
+#define tabstops (G.tabstops )
+#define terminal_width (G.terminal_width)
#else
enum {
tabstops = COLUMN_GAP,
terminal_width = TERMINAL_WIDTH,
};
#endif
+#define current_time_t (G.current_time_t)
+/* memset: we have to zero it out because of NOEXEC */
+#define INIT_G() do { \
+ memset(&G, 0, sizeof(G)); \
+ USE_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
+ USE_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
+ USE_FEATURE_LS_TIMESTAMPS(time(¤t_time_t);) \
+} while (0)
+
+
+#if ENABLE_FEATURE_ASSUME_UNICODE
+/* libbb candidate */
+static size_t mbstrlen(const char *string)
+{
+ size_t width = mbsrtowcs(NULL /*dest*/, &string,
+ MAXINT(size_t) /*len*/, NULL /*state*/);
+ if (width == (size_t)-1)
+ return strlen(string);
+ return width;
+}
+#else
+#define mbstrlen(string) strlen(string)
+#endif
-static int status = EXIT_SUCCESS;
static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
{
}
#endif
if (stat(fullname, &dstat)) {
- bb_perror_msg("%s", fullname);
- status = EXIT_FAILURE;
+ bb_simple_perror_msg(fullname);
+ exit_code = EXIT_FAILURE;
return 0;
}
} else {
}
#endif
if (lstat(fullname, &dstat)) {
- bb_perror_msg("%s", fullname);
- status = EXIT_FAILURE;
+ bb_simple_perror_msg(fullname);
+ exit_code = EXIT_FAILURE;
return 0;
}
}
} else {
/* find the longest file name, use that as the column width */
for (i = 0; i < nfiles; i++) {
- int len = strlen(dn[i]->name);
+ int len = mbstrlen(dn[i]->name);
if (column_width < len)
column_width = len;
}
for (i = 0; i < ndirs; i++) {
if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
if (!first)
- puts("");
+ bb_putchar('\n');
first = 0;
printf("%s:\n", dn[i]->fullname);
}
nfiles = 0;
dir = warn_opendir(path);
if (dir == NULL) {
- status = EXIT_FAILURE;
+ exit_code = EXIT_FAILURE;
return NULL; /* could not open the dir */
}
while ((entry = readdir(dir)) != NULL) {
}
-#if ENABLE_FEATURE_LS_TIMESTAMPS
-/* Do time() just once. Saves one syscall per file for "ls -l" */
-/* Initialized in main() */
-static time_t current_time_t;
-#endif
-
-static int list_single(struct dnode *dn)
+static int list_single(const struct dnode *dn)
{
int i, column = 0;
fgcolor(info.st_mode));
}
#endif
+#if ENABLE_FEATURE_ASSUME_UNICODE
+ printf("%s", dn->name);
+ column += mbstrlen(dn->name);
+#else
column += printf("%s", dn->name);
+#endif
if (show_color) {
printf("\033[0m");
}
return column;
}
+
/* "[-]Cadil1", POSIX mandated options, busybox always supports */
/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
/* "[-]Ak" GNU options, busybox always supports */
/* "[-]SXvThw", GNU options, busybox optionally supports */
/* "[-]K", SELinux mandated options, busybox optionally supports */
/* "[-]e", I think we made this one up */
-static const char ls_options[] = "Cadil1gnsxAk"
+static const char ls_options[] ALIGN1 =
+ "Cadil1gnsxAk"
USE_FEATURE_LS_TIMESTAMPS("cetu")
USE_FEATURE_LS_SORTFILES("SXrv")
USE_FEATURE_LS_FILETYPES("Fp")
};
-/* THIS IS A "SAFE" APPLET, main() MAY BE CALLED INTERNALLY FROM SHELL */
-/* BE CAREFUL! */
+/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
+#if ENABLE_FEATURE_LS_COLOR
+/* long option entry used only for --color, which has no short option
+ * equivalent */
+static const char ls_color_opt[] ALIGN1 =
+ "color\0" Optional_argument "\xff" /* no short equivalent */
+ ;
+#endif
+
-int ls_main(int argc, char **argv);
-int ls_main(int argc, char **argv)
+int ls_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ls_main(int argc UNUSED_PARAM, char **argv)
{
struct dnode **dnd;
struct dnode **dnf;
struct dnode *dn;
struct dnode *cur;
unsigned opt;
- int nfiles = 0;
+ int nfiles;
int dnfiles;
int dndirs;
- int oi;
- int ac;
int i;
- char **av;
- USE_FEATURE_AUTOWIDTH(char *tabstops_str = NULL;)
- USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;)
- USE_FEATURE_LS_COLOR(char *color_opt;)
+ /* need to initialize since --color has _an optional_ argument */
+ USE_FEATURE_LS_COLOR(const char *color_opt = "always";)
-#if ENABLE_FEATURE_LS_TIMESTAMPS
- time(¤t_time_t);
-#endif
+ INIT_G();
all_fmt = LIST_SHORT |
(ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
#if ENABLE_FEATURE_AUTOWIDTH
/* Obtain the terminal width */
- get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL);
+ get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
/* Go one less... */
terminal_width--;
#endif
/* process options */
USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
#if ENABLE_FEATURE_AUTOWIDTH
- opt = getopt32(argc, argv, ls_options, &tabstops_str, &terminal_width_str
+ opt_complementary = "T+:w+"; /* -T N, -w N */
+ opt = getopt32(argv, ls_options, &tabstops, &terminal_width
USE_FEATURE_LS_COLOR(, &color_opt));
- if (tabstops_str)
- tabstops = xatou(tabstops_str);
- if (terminal_width_str)
- terminal_width = xatou(terminal_width_str);
#else
- opt = getopt32(argc, argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
+ opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
#endif
for (i = 0; opt_flags[i] != (1U<<31); i++) {
if (opt & (1 << i)) {
if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
char *p = getenv("LS_COLORS");
/* LS_COLORS is unset, or (not empty && not "none") ? */
- if (!p || (p[0] && strcmp(p, "none")))
+ if (!p || (p[0] && strcmp(p, "none") != 0))
show_color = 1;
}
if (opt & (1 << i)) { /* next flag after short options */
- if (!color_opt || !strcmp("always", color_opt))
+ if (strcmp("always", color_opt) == 0)
show_color = 1;
- else if (color_opt && !strcmp("never", color_opt))
+ else if (strcmp("never", color_opt) == 0)
show_color = 0;
- else if (color_opt && !strcmp("auto", color_opt) && isatty(STDOUT_FILENO))
+ else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))
show_color = 1;
}
#endif
if (!(all_fmt & STYLE_MASK))
all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
- /*
- * when there are no cmd line args we have to supply a default "." arg.
- * we will create a second argv array, "av" that will hold either
- * our created "." arg, or the real cmd line args. The av array
- * just holds the pointers- we don't move the date the pointers
- * point to.
- */
- ac = argc - optind; /* how many cmd line args are left */
- if (ac < 1) {
- static const char *const dotdir[] = { "." };
-
- av = (char **) dotdir;
- ac = 1;
- } else {
- av = argv + optind;
- }
+ argv += optind;
+ if (!argv[0])
+ *--argv = (char*)".";
- /* now, everything is in the av array */
- if (ac > 1)
- all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
+ if (argv[1])
+ all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
/* stuff the command line file names into a dnode array */
dn = NULL;
- for (oi = 0; oi < ac; oi++) {
+ nfiles = 0;
+ do {
/* ls w/o -l follows links on command line */
- cur = my_stat(av[oi], av[oi], !(all_fmt & STYLE_LONG));
+ cur = my_stat(*argv, *argv, !(all_fmt & STYLE_LONG));
+ argv++;
if (!cur)
continue;
cur->allocated = 0;
cur->next = dn;
dn = cur;
nfiles++;
- }
+ } while (*argv);
/* now that we know how many files there are
* allocate memory for an array to hold dnode pointers
}
if (ENABLE_FEATURE_CLEAN_UP)
dfree(dnp, nfiles);
- return status;
+ return exit_code;
}