From 4aca87515a5083ae0e31ce3177189fd43b6d05ac Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 3 Jan 2015 13:58:15 +0100 Subject: patch to Vanilla Tomato 1.28 --- release/src/router/busybox/coreutils/ls.c | 1348 ++++++++++++++--------------- 1 file changed, 664 insertions(+), 684 deletions(-) (limited to 'release/src/router/busybox/coreutils/ls.c') diff --git a/release/src/router/busybox/coreutils/ls.c b/release/src/router/busybox/coreutils/ls.c index a7f036b6..38957e93 100644 --- a/release/src/router/busybox/coreutils/ls.c +++ b/release/src/router/busybox/coreutils/ls.c @@ -3,22 +3,10 @@ * tiny-ls.c version 0.1.0: A minimalist 'ls' * Copyright (C) 1996 Brian Candler * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ -/* +/* [date unknown. Perhaps before year 2000] * To achieve a small memory footprint, this version of 'ls' doesn't do any * file sorting, and only has the most essential command line switches * (i.e., the ones I couldn't live without :-) All features which involve @@ -30,8 +18,7 @@ * * KNOWN BUGS: * 1. ls -l of a directory doesn't give "total " header - * 2. ls of a symlink to a directory doesn't list directory contents - * 3. hidden files can make column width too large + * 2. hidden files can make column width too large * * NON-OPTIMAL BEHAVIOUR: * 1. autowidth reads directories twice @@ -39,288 +26,409 @@ * appended, there's no need to stat each one * PORTABILITY: * 1. requires lstat (BSD) - how do you do it without? + * + * [2009-03] + * ls sorts listing now, and supports almost all options. */ -enum { - TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ - COLUMN_GAP = 2, /* includes the file type char */ -}; +#include "libbb.h" -/************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "busybox.h" -#ifdef CONFIG_SELINUX -#include -#include -#include +#if ENABLE_FEATURE_ASSUME_UNICODE +#include #endif -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS -#include -#endif +/* This is a NOEXEC applet. Be very careful! */ + -#ifndef MAJOR -#define MAJOR(dev) (((dev)>>8)&0xff) -#define MINOR(dev) ((dev)&0xff) +#if ENABLE_FTPD +/* ftpd uses ls, and without timestamps Mozilla won't understand + * ftpd's LIST output. + */ +# undef CONFIG_FEATURE_LS_TIMESTAMPS +# undef ENABLE_FEATURE_LS_TIMESTAMPS +# undef USE_FEATURE_LS_TIMESTAMPS +# undef SKIP_FEATURE_LS_TIMESTAMPS +# define CONFIG_FEATURE_LS_TIMESTAMPS 1 +# define ENABLE_FEATURE_LS_TIMESTAMPS 1 +# define USE_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__ +# define SKIP_FEATURE_LS_TIMESTAMPS(...) #endif -/* what is the overall style of the listing */ -#define STYLE_AUTO (0) -#define STYLE_COLUMNS (1U<<21) /* fill columns */ -#define STYLE_LONG (2U<<21) /* one record per line, extended info */ -#define STYLE_SINGLE (3U<<21) /* one record per line */ -#define STYLE_MASK STYLE_SINGLE -#define STYLE_ONE_RECORD_FLAG STYLE_LONG +enum { + +TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ +COLUMN_GAP = 2, /* includes the file type char */ + +/* what is the overall style of the listing */ +STYLE_COLUMNS = 1 << 21, /* fill columns */ +STYLE_LONG = 2 << 21, /* one record per line, extended info */ +STYLE_SINGLE = 3 << 21, /* one record per line */ +STYLE_MASK = STYLE_SINGLE, /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ /* what file information will be listed */ -#define LIST_INO (1U<<0) -#define LIST_BLOCKS (1U<<1) -#define LIST_MODEBITS (1U<<2) -#define LIST_NLINKS (1U<<3) -#define LIST_ID_NAME (1U<<4) -#define LIST_ID_NUMERIC (1U<<5) -#define LIST_CONTEXT (1U<<6) -#define LIST_SIZE (1U<<7) -#define LIST_DEV (1U<<8) -#define LIST_DATE_TIME (1U<<9) -#define LIST_FULLTIME (1U<<10) -#define LIST_FILENAME (1U<<11) -#define LIST_SYMLINK (1U<<12) -#define LIST_FILETYPE (1U<<13) -#define LIST_EXEC (1U<<14) - -#define LIST_MASK ((LIST_EXEC << 1) - 1) +LIST_INO = 1 << 0, +LIST_BLOCKS = 1 << 1, +LIST_MODEBITS = 1 << 2, +LIST_NLINKS = 1 << 3, +LIST_ID_NAME = 1 << 4, +LIST_ID_NUMERIC = 1 << 5, +LIST_CONTEXT = 1 << 6, +LIST_SIZE = 1 << 7, +//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE +LIST_DATE_TIME = 1 << 9, +LIST_FULLTIME = 1 << 10, +LIST_FILENAME = 1 << 11, +LIST_SYMLINK = 1 << 12, +LIST_FILETYPE = 1 << 13, +LIST_EXEC = 1 << 14, +LIST_MASK = (LIST_EXEC << 1) - 1, /* what files will be displayed */ -/* TODO -- We may be able to make DISP_NORMAL 0 to save a bit slot. */ -#define DISP_NORMAL (1U<<14) /* show normal filenames */ -#define DISP_DIRNAME (1U<<15) /* 2 or more items? label directories */ -#define DISP_HIDDEN (1U<<16) /* show filenames starting with . */ -#define DISP_DOT (1U<<17) /* show . and .. */ -#define DISP_NOLIST (1U<<18) /* show directory as itself, not contents */ -#define DISP_RECURSIVE (1U<<19) /* show directory and everything below it */ -#define DISP_ROWS (1U<<20) /* print across rows */ - -#define DISP_MASK (((DISP_ROWS << 1) - 1) & ~(DISP_NORMAL - 1)) - -#ifdef CONFIG_FEATURE_LS_SORTFILES -/* how will the files be sorted */ -#define SORT_ORDER_FORWARD 0 /* sort in reverse order */ -#define SORT_ORDER_REVERSE (1U<<27) /* sort in reverse order */ - -#define SORT_NAME 0 /* sort by file name */ -#define SORT_SIZE (1U<<28) /* sort by file size */ -#define SORT_ATIME (2U<<28) /* sort by last access time */ -#define SORT_CTIME (3U<<28) /* sort by last change time */ -#define SORT_MTIME (4U<<28) /* sort by last modification time */ -#define SORT_VERSION (5U<<28) /* sort by version */ -#define SORT_EXT (6U<<28) /* sort by file name extension */ -#define SORT_DIR (7U<<28) /* sort by file or directory */ - -#define SORT_MASK (7U<<28) -#endif +DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */ +DISP_HIDDEN = 1 << 16, /* show filenames starting with . */ +DISP_DOT = 1 << 17, /* show . and .. */ +DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */ +DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */ +DISP_ROWS = 1 << 20, /* print across rows */ +DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1), + +/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */ +SORT_FORWARD = 0, /* sort in reverse order */ +SORT_REVERSE = 1 << 27, /* sort in reverse order */ + +SORT_NAME = 0, /* sort by file name */ +SORT_SIZE = 1 << 28, /* sort by file size */ +SORT_ATIME = 2 << 28, /* sort by last access time */ +SORT_CTIME = 3 << 28, /* sort by last change time */ +SORT_MTIME = 4 << 28, /* sort by last modification time */ +SORT_VERSION = 5 << 28, /* sort by version */ +SORT_EXT = 6 << 28, /* sort by file name extension */ +SORT_DIR = 7 << 28, /* sort by file or directory */ +SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES, -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS /* which of the three times will be used */ -#define TIME_MOD 0 -#define TIME_CHANGE (1U<<23) -#define TIME_ACCESS (1U<<24) +TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS, +TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS, +TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS, -#define TIME_MASK (3U<<23) -#endif +FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS, -#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS -#define FOLLOW_LINKS (1U<<25) -#endif -#ifdef CONFIG_FEATURE_HUMAN_READABLE -#define LS_DISP_HR (1U<<26) -#endif +LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE, -#define LIST_SHORT (LIST_FILENAME) -#define LIST_ISHORT (LIST_INO | LIST_FILENAME) -#define LIST_LONG (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \ - LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK) -#define LIST_ILONG (LIST_INO | LIST_LONG) +LIST_SHORT = LIST_FILENAME, +LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \ + LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK, -#define SPLIT_DIR 1 -#define SPLIT_FILE 0 -#define SPLIT_SUBDIR 2 +SPLIT_DIR = 1, +SPLIT_FILE = 0, +SPLIT_SUBDIR = 2, -#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) -#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) +}; -#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR) -# define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) -#endif +/* "[-]Cadil1", POSIX mandated options, busybox always supports */ +/* "[-]gnsx", POSIX non-mandated options, busybox always supports */ +/* "[-]Q" GNU option? busybox always supports */ +/* "[-]Ak" GNU options, busybox always supports */ +/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */ +/* "[-]p", POSIX non-mandated options, busybox optionally 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[] ALIGN1 = + "Cadil1gnsxQAk" /* 13 opts, total 13 */ + USE_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */ + USE_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */ + USE_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */ + USE_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */ + USE_FEATURE_LS_RECURSIVE("R") /* 1, 25 */ + USE_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ + USE_SELINUX("KZ") /* 2, 28 */ + USE_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */ + ; +enum { + //OPT_C = (1 << 0), + //OPT_a = (1 << 1), + //OPT_d = (1 << 2), + //OPT_i = (1 << 3), + //OPT_l = (1 << 4), + //OPT_1 = (1 << 5), + OPT_g = (1 << 6), + //OPT_n = (1 << 7), + //OPT_s = (1 << 8), + //OPT_x = (1 << 9), + OPT_Q = (1 << 10), + //OPT_A = (1 << 11), + //OPT_k = (1 << 12), + OPTBIT_color = 13 + + 4 * ENABLE_FEATURE_LS_TIMESTAMPS + + 4 * ENABLE_FEATURE_LS_SORTFILES + + 2 * ENABLE_FEATURE_LS_FILETYPES + + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS + + 1 * ENABLE_FEATURE_LS_RECURSIVE + + 1 * ENABLE_FEATURE_HUMAN_READABLE + + 2 * ENABLE_SELINUX + + 2 * ENABLE_FEATURE_AUTOWIDTH, + OPT_color = 1 << OPTBIT_color, +}; -/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ -#ifdef CONFIG_FEATURE_LS_COLOR -static int show_color = 0; +enum { + LIST_MASK_TRIGGER = 0, + STYLE_MASK_TRIGGER = STYLE_MASK, + DISP_MASK_TRIGGER = DISP_ROWS, + SORT_MASK_TRIGGER = SORT_MASK, +}; -#define COLOR(mode) ("\000\043\043\043\042\000\043\043"\ - "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)]) -#define ATTR(mode) ("\00\00\01\00\01\00\01\00"\ - "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)]) +/* TODO: simple toggles may be stored as OPT_xxx bits instead */ +static const unsigned opt_flags[] = { + LIST_SHORT | STYLE_COLUMNS, /* C */ + DISP_HIDDEN | DISP_DOT, /* a */ + DISP_NOLIST, /* d */ + LIST_INO, /* i */ + LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */ + LIST_SHORT | STYLE_SINGLE, /* 1 */ + 0, /* g (don't show group) - handled via OPT_g */ + LIST_ID_NUMERIC, /* n */ + LIST_BLOCKS, /* s */ + DISP_ROWS, /* x */ + 0, /* Q (quote filename) - handled via OPT_Q */ + DISP_HIDDEN, /* A */ + ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */ +#if ENABLE_FEATURE_LS_TIMESTAMPS + TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */ + LIST_FULLTIME, /* e */ + ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */ + TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */ +#endif +#if ENABLE_FEATURE_LS_SORTFILES + SORT_SIZE, /* S */ + SORT_EXT, /* X */ + SORT_REVERSE, /* r */ + SORT_VERSION, /* v */ +#endif +#if ENABLE_FEATURE_LS_FILETYPES + LIST_FILETYPE | LIST_EXEC, /* F */ + LIST_FILETYPE, /* p */ +#endif +#if ENABLE_FEATURE_LS_FOLLOWLINKS + FOLLOW_LINKS, /* L */ +#endif +#if ENABLE_FEATURE_LS_RECURSIVE + DISP_RECURSIVE, /* R */ +#endif +#if ENABLE_FEATURE_HUMAN_READABLE + LS_DISP_HR, /* h */ +#endif +#if ENABLE_SELINUX + LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */ +#endif +#if ENABLE_SELINUX + LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */ #endif + (1U<<31) + /* options after Z are not processed through opt_flags: + * T, w - ignored + */ +}; + /* * a directory entry and its stat info are stored here */ -struct dnode { /* the basic node */ - char *name; /* the dir entry name */ - char *fullname; /* the dir entry name */ - struct stat dstat; /* the file stat info */ -#ifdef CONFIG_SELINUX - security_id_t sid; -#endif - struct dnode *next; /* point at the next node */ +struct dnode { /* the basic node */ + const char *name; /* the dir entry name */ + const char *fullname; /* the dir entry name */ + int allocated; + struct stat dstat; /* the file stat info */ + 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 int all_fmt; -#ifdef CONFIG_SELINUX -static int is_flask_enabled_flag; +struct globals { +#if ENABLE_FEATURE_LS_COLOR + smallint show_color; #endif + smallint exit_code; + unsigned all_fmt; +#if ENABLE_FEATURE_AUTOWIDTH + 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) + -#ifdef CONFIG_FEATURE_AUTOWIDTH -static unsigned short terminal_width = TERMINAL_WIDTH; -static unsigned short tabstops = COLUMN_GAP; +#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 tabstops COLUMN_GAP -#define terminal_width TERMINAL_WIDTH +#define mbstrlen(string) strlen(string) #endif -static int status = EXIT_SUCCESS; -static struct dnode *my_stat(char *fullname, char *name) +static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) { struct stat dstat; struct dnode *cur; -#ifdef CONFIG_SELINUX - security_id_t sid; -#endif - int rc; + USE_SELINUX(security_context_t sid = NULL;) -#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS - if (all_fmt & FOLLOW_LINKS) { -#ifdef CONFIG_SELINUX - if(is_flask_enabled_flag) - rc = stat_secure(fullname, &dstat, &sid); - else + if ((all_fmt & FOLLOW_LINKS) || force_follow) { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + getfilecon(fullname, &sid); + } #endif - rc = stat(fullname, &dstat); - if(rc) - { - bb_perror_msg("%s", fullname); - status = EXIT_FAILURE; + if (stat(fullname, &dstat)) { + bb_simple_perror_msg(fullname); + exit_code = EXIT_FAILURE; return 0; } - } else -#endif - { -#ifdef CONFIG_SELINUX - if(is_flask_enabled_flag) - rc = lstat_secure(fullname, &dstat, &sid); - else + } else { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + lgetfilecon(fullname, &sid); + } #endif - rc = lstat(fullname, &dstat); - if(rc) - { - bb_perror_msg("%s", fullname); - status = EXIT_FAILURE; + if (lstat(fullname, &dstat)) { + bb_simple_perror_msg(fullname); + exit_code = EXIT_FAILURE; return 0; } } - cur = (struct dnode *) xmalloc(sizeof(struct dnode)); + cur = xmalloc(sizeof(struct dnode)); cur->fullname = fullname; cur->name = name; cur->dstat = dstat; -#ifdef CONFIG_SELINUX - cur->sid = sid; -#endif + USE_SELINUX(cur->sid = sid;) return cur; } -/*----------------------------------------------------------------------*/ -#ifdef CONFIG_FEATURE_LS_COLOR + +/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket + * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file + * 3/7:multiplexed char/block device) + * and we use 0 for unknown and 15 for executables (see below) */ +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) +#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) +/* 036 black foreground 050 black background + 037 red foreground 051 red background + 040 green foreground 052 green background + 041 brown foreground 053 brown background + 042 blue foreground 054 blue background + 043 magenta (purple) foreground 055 magenta background + 044 cyan (light blue) foreground 056 cyan background + 045 gray foreground 057 white background +*/ +#define COLOR(mode) ( \ + /*un fi chr dir blk file link sock exe */ \ + "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ + [TYPEINDEX(mode)]) +/* Select normal (0) [actually "reset all"] or bold (1) + * (other attributes are 2:dim 4:underline 5:blink 7:reverse, + * let's use 7 for "impossible" types, just for fun) + * Note: coreutils 6.9 uses inverted red for setuid binaries. + */ +#define ATTR(mode) ( \ + /*un fi chr dir blk file link sock exe */ \ + "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ + [TYPEINDEX(mode)]) + +#if ENABLE_FEATURE_LS_COLOR +/* mode of zero is interpreted as "unknown" (stat failed) */ static char fgcolor(mode_t mode) { - /* Check wheter the file is existing (if so, color it red!) */ - if (errno == ENOENT) { - return '\037'; - } - if (LIST_EXEC && S_ISREG(mode) - && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return COLOR(0xF000); /* File is executable ... */ return COLOR(mode); } - -/*----------------------------------------------------------------------*/ -static char bgcolor(mode_t mode) +static char bold(mode_t mode) { - if (LIST_EXEC && S_ISREG(mode) - && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return ATTR(0xF000); /* File is executable ... */ return ATTR(mode); } #endif -/*----------------------------------------------------------------------*/ -#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR) +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR static char append_char(mode_t mode) { if (!(all_fmt & LIST_FILETYPE)) return '\0'; - if ((all_fmt & LIST_EXEC) && S_ISREG(mode) - && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + if (S_ISDIR(mode)) + return '/'; + if (!(all_fmt & LIST_EXEC)) + return '\0'; + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; return APPCHAR(mode); } #endif -/*----------------------------------------------------------------------*/ - -#define countdirs(A,B) count_dirs((A), (B), 1) -#define countsubdirs(A,B) count_dirs((A), (B), 0) +#define countdirs(A, B) count_dirs((A), (B), 1) +#define countsubdirs(A, B) count_dirs((A), (B), 0) static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs) { int i, dirs; - if (dn == NULL || nfiles < 1) - return (0); + if (!dn) + return 0; dirs = 0; for (i = 0; i < nfiles; i++) { - if (S_ISDIR(dn[i]->dstat.st_mode) - && (notsubdirs - || ((dn[i]->name[0] != '.') - || (dn[i]->name[1] - && ((dn[i]->name[1] != '.') - || dn[i]->name[2]))))) + const char *name; + if (!S_ISDIR(dn[i]->dstat.st_mode)) + continue; + name = dn[i]->name; + if (notsubdirs + || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) + ) { dirs++; + } } - return (dirs); + return dirs; } static int countfiles(struct dnode **dnp) @@ -329,44 +437,41 @@ static int countfiles(struct dnode **dnp) struct dnode *cur; if (dnp == NULL) - return (0); + return 0; nfiles = 0; - for (cur = dnp[0]; cur->next != NULL; cur = cur->next) + for (cur = dnp[0]; cur->next; cur = cur->next) nfiles++; nfiles++; - return (nfiles); + return nfiles; } /* get memory to hold an array of pointers */ static struct dnode **dnalloc(int num) { - struct dnode **p; - if (num < 1) - return (NULL); + return NULL; - p = (struct dnode **) xcalloc((size_t) num, - (size_t) (sizeof(struct dnode *))); - return (p); + return xzalloc(num * sizeof(struct dnode *)); } -#ifdef CONFIG_FEATURE_LS_RECURSIVE -static void dfree(struct dnode **dnp) +#if ENABLE_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp, int nfiles) { - struct dnode *cur, *next; + int i; if (dnp == NULL) return; - cur = dnp[0]; - while (cur != NULL) { - free(cur->fullname); /* free the filename */ - next = cur->next; + for (i = 0; i < nfiles; i++) { + struct dnode *cur = dnp[i]; + if (cur->allocated) + free((char*)cur->fullname); /* free the filename */ free(cur); /* free the dnode */ - cur = next; } free(dnp); /* free the array holding the dnode pointers */ } +#else +#define dfree(...) ((void)0) #endif static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) @@ -375,7 +480,7 @@ static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) struct dnode **dnp; if (dn == NULL || nfiles < 1) - return (NULL); + return NULL; /* count how many dirs and regular files there are */ if (which == SPLIT_SUBDIR) @@ -392,30 +497,33 @@ static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) /* copy the entrys into the file or dir array */ for (d = i = 0; i < nfiles; i++) { if (S_ISDIR(dn[i]->dstat.st_mode)) { - if (which & (SPLIT_DIR|SPLIT_SUBDIR)) { - if ((which & SPLIT_DIR) - || ((dn[i]->name[0] != '.') - || (dn[i]->name[1] - && ((dn[i]->name[1] != '.') - || dn[i]->name[2])))) { - dnp[d++] = dn[i]; - } + const char *name; + if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) + continue; + name = dn[i]->name; + if ((which & SPLIT_DIR) + || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) + ) { + dnp[d++] = dn[i]; } } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { dnp[d++] = dn[i]; } } - return (dnp); + return dnp; } -/*----------------------------------------------------------------------*/ -#ifdef CONFIG_FEATURE_LS_SORTFILES -static int sortcmp(struct dnode *d1, struct dnode *d2) +#if ENABLE_FEATURE_LS_SORTFILES +static int sortcmp(const void *a, const void *b) { - unsigned int sort_opts = all_fmt & SORT_MASK; + struct dnode *d1 = *(struct dnode **)a; + struct dnode *d2 = *(struct dnode **)b; + unsigned sort_opts = all_fmt & SORT_MASK; int dif; - dif = 0; /* assume SORT_NAME */ + dif = 0; /* assume SORT_NAME */ + // TODO: use pre-initialized function pointer + // instead of branch forest if (sort_opts == SORT_SIZE) { dif = (int) (d2->dstat.st_size - d1->dstat.st_size); } else if (sort_opts == SORT_ATIME) { @@ -431,46 +539,26 @@ static int sortcmp(struct dnode *d1, struct dnode *d2) } if (dif == 0) { - /* sort by name- may be a tie_breaker for time or size cmp */ -#ifdef CONFIG_LOCALE_SUPPORT - dif = strcoll(d1->name, d2->name); -#else - dif = strcmp(d1->name, d2->name); -#endif + /* sort by name - may be a tie_breaker for time or size cmp */ + if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name); + else dif = strcmp(d1->name, d2->name); } - if (all_fmt & SORT_ORDER_REVERSE) { + if (all_fmt & SORT_REVERSE) { dif = -dif; } - return (dif); + return dif; } -/*----------------------------------------------------------------------*/ -static void shellsort(struct dnode **dn, int size) +static void dnsort(struct dnode **dn, int size) { - struct dnode *temp; - int gap, i, j; - - /* shell short the array */ - if (dn == NULL || size < 2) - return; - - for (gap = size / 2; gap > 0; gap /= 2) { - for (i = gap; i < size; i++) { - for (j = i - gap; j >= 0; j -= gap) { - if (sortcmp(dn[j], dn[j + gap]) <= 0) - break; - /* they are out of order, swap them */ - temp = dn[j]; - dn[j] = dn[j + gap]; - dn[j + gap] = temp; - } - } - } + qsort(dn, size, sizeof(*dn), sortcmp); } +#else +#define dnsort(dn, size) ((void)0) #endif -/*----------------------------------------------------------------------*/ + static void showfiles(struct dnode **dn, int nfiles) { int i, ncols, nrows, row, nc; @@ -481,27 +569,25 @@ static void showfiles(struct dnode **dn, int nfiles) if (dn == NULL || nfiles < 1) return; - if (all_fmt & STYLE_ONE_RECORD_FLAG) { + if (all_fmt & STYLE_LONG) { ncols = 1; } else { - /* find the longest file name- use that as the column width */ + /* find the longest file name, use that as the column width */ for (i = 0; i < nfiles; i++) { - int len = strlen(dn[i]->name) + -#ifdef CONFIG_SELINUX - ((all_fmt & LIST_CONTEXT) ? 33 : 0) + -#endif - ((all_fmt & LIST_INO) ? 8 : 0) + - ((all_fmt & LIST_BLOCKS) ? 5 : 0); + int len = mbstrlen(dn[i]->name); if (column_width < len) column_width = len; } - column_width += tabstops; + column_width += tabstops + + USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) + ((all_fmt & LIST_INO) ? 8 : 0) + + ((all_fmt & LIST_BLOCKS) ? 5 : 0); ncols = (int) (terminal_width / column_width); } if (ncols > 1) { nrows = nfiles / ncols; - if ((nrows * ncols) < nfiles) + if (nrows * ncols < nfiles) nrows++; /* round up fractionals */ } else { nrows = nfiles; @@ -517,66 +603,62 @@ static void showfiles(struct dnode **dn, int nfiles) if (i < nfiles) { if (column > 0) { nexttab -= column; - while (nexttab--) { - putchar(' '); - column++; - } - } + printf("%*s", nexttab, ""); + column += nexttab; + } nexttab = column + column_width; column += list_single(dn[i]); - } + } } putchar('\n'); column = 0; } } -/*----------------------------------------------------------------------*/ -static void showdirs(struct dnode **dn, int ndirs) + +static void showdirs(struct dnode **dn, int ndirs, int first) { int i, nfiles; struct dnode **subdnp; - -#ifdef CONFIG_FEATURE_LS_RECURSIVE int dndirs; struct dnode **dnd; -#endif if (dn == NULL || ndirs < 1) return; for (i = 0; i < ndirs; i++) { if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { - printf("\n%s:\n", dn[i]->fullname); + if (!first) + bb_putchar('\n'); + first = 0; + printf("%s:\n", dn[i]->fullname); } subdnp = list_dir(dn[i]->fullname); nfiles = countfiles(subdnp); if (nfiles > 0) { /* list all files at this level */ -#ifdef CONFIG_FEATURE_LS_SORTFILES - shellsort(subdnp, nfiles); -#endif + dnsort(subdnp, nfiles); showfiles(subdnp, nfiles); -#ifdef CONFIG_FEATURE_LS_RECURSIVE - if (all_fmt & DISP_RECURSIVE) { - /* recursive- list the sub-dirs */ - dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR); - dndirs = countsubdirs(subdnp, nfiles); - if (dndirs > 0) { -#ifdef CONFIG_FEATURE_LS_SORTFILES - shellsort(dnd, dndirs); -#endif - showdirs(dnd, dndirs); - free(dnd); /* free the array of dnode pointers to the dirs */ + if (ENABLE_FEATURE_LS_RECURSIVE) { + if (all_fmt & DISP_RECURSIVE) { + /* recursive- list the sub-dirs */ + dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR); + dndirs = countsubdirs(subdnp, nfiles); + if (dndirs > 0) { + dnsort(dnd, dndirs); + showdirs(dnd, dndirs, 0); + /* free the array of dnode pointers to the dirs */ + free(dnd); + } } + /* free the dnodes and the fullname mem */ + dfree(subdnp, nfiles); } - dfree(subdnp); /* free the dnodes and the fullname mem */ -#endif } } } -/*----------------------------------------------------------------------*/ + static struct dnode **list_dir(const char *path) { struct dnode *dn, *cur, **dnp; @@ -585,33 +667,35 @@ static struct dnode **list_dir(const char *path) int i, nfiles; if (path == NULL) - return (NULL); + return NULL; dn = NULL; nfiles = 0; - dir = opendir(path); + dir = warn_opendir(path); if (dir == NULL) { - bb_perror_msg("%s", path); - status = EXIT_FAILURE; - return (NULL); /* could not open the dir */ + exit_code = EXIT_FAILURE; + return NULL; /* could not open the dir */ } while ((entry = readdir(dir)) != NULL) { char *fullname; /* are we going to list the file- it may be . or .. or a hidden file */ if (entry->d_name[0] == '.') { - if ((entry->d_name[1] == 0 || ( - entry->d_name[1] == '.' - && entry->d_name[2] == 0)) - && !(all_fmt & DISP_DOT)) - continue; + if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) + && !(all_fmt & DISP_DOT) + ) { + continue; + } if (!(all_fmt & DISP_HIDDEN)) - continue; + continue; } fullname = concat_path_file(path, entry->d_name); - cur = my_stat(fullname, strrchr(fullname, '/') + 1); - if (!cur) + cur = my_stat(fullname, bb_basename(fullname), 0); + if (!cur) { + free(fullname); continue; + } + cur->allocated = 1; cur->next = dn; dn = cur; nfiles++; @@ -619,40 +703,68 @@ static struct dnode **list_dir(const char *path) closedir(dir); /* now that we know how many files there are - ** allocate memory for an array to hold dnode pointers + * allocate memory for an array to hold dnode pointers */ if (dn == NULL) - return (NULL); + return NULL; dnp = dnalloc(nfiles); for (i = 0, cur = dn; i < nfiles; i++) { dnp[i] = cur; /* save pointer to node in array */ cur = cur->next; } - return (dnp); + return dnp; } -/*----------------------------------------------------------------------*/ -static int list_single(struct dnode *dn) -{ - int i, column = 0; -#ifdef CONFIG_FEATURE_LS_USERNAME - char scratch[16]; +static int print_name(const char *name) +{ + if (option_mask32 & OPT_Q) { +#if ENABLE_FEATURE_ASSUME_UNICODE + int len = 2 + mbstrlen(name); +#else + int len = 2; #endif -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + putchar('"'); + while (*name) { + if (*name == '"') { + putchar('\\'); + len++; + } + putchar(*name++); + if (!ENABLE_FEATURE_ASSUME_UNICODE) + len++; + } + putchar('"'); + return len; + } + /* No -Q: */ +#if ENABLE_FEATURE_ASSUME_UNICODE + fputs(name, stdout); + return mbstrlen(name); +#else + return printf("%s", name); +#endif +} + + +static int list_single(const struct dnode *dn) +{ + int column = 0; + char *lpath = lpath; /* for compiler */ +#if ENABLE_FEATURE_LS_TIMESTAMPS char *filetime; time_t ttime, age; #endif -#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR) +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR struct stat info; char append; #endif if (dn->fullname == NULL) - return (0); + return 0; -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS +#if ENABLE_FEATURE_LS_TIMESTAMPS ttime = dn->dstat.st_mtime; /* the default time */ if (all_fmt & TIME_ACCESS) ttime = dn->dstat.st_atime; @@ -660,70 +772,65 @@ static int list_single(struct dnode *dn) ttime = dn->dstat.st_ctime; filetime = ctime(&ttime); #endif -#ifdef CONFIG_FEATURE_LS_FILETYPES +#if ENABLE_FEATURE_LS_FILETYPES append = append_char(dn->dstat.st_mode); #endif - for (i = 0; i <= 31; i++) { - switch (all_fmt & (1 << i)) { - case LIST_INO: - column += printf("%7ld ", (long int) dn->dstat.st_ino); - break; - case LIST_BLOCKS: -#if _FILE_OFFSET_BITS == 64 - column += printf("%4lld ", dn->dstat.st_blocks >> 1); -#else - column += printf("%4ld ", dn->dstat.st_blocks >> 1); -#endif - break; - case LIST_MODEBITS: - column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); - break; - case LIST_NLINKS: - column += printf("%4ld ", (long) dn->dstat.st_nlink); - break; - case LIST_ID_NAME: -#ifdef CONFIG_FEATURE_LS_USERNAME - my_getpwuid(scratch, dn->dstat.st_uid); - printf("%-8.8s ", scratch); - my_getgrgid(scratch, dn->dstat.st_gid); - printf("%-8.8s", scratch); - column += 17; - break; + /* Do readlink early, so that if it fails, error message + * does not appear *inside* of the "ls -l" line */ + if (all_fmt & LIST_SYMLINK) + if (S_ISLNK(dn->dstat.st_mode)) + lpath = xmalloc_readlink_or_warn(dn->fullname); + + if (all_fmt & LIST_INO) + column += printf("%7lu ", (long) dn->dstat.st_ino); + if (all_fmt & LIST_BLOCKS) + column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1); + if (all_fmt & LIST_MODEBITS) + column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); + if (all_fmt & LIST_NLINKS) + column += printf("%4lu ", (long) dn->dstat.st_nlink); +#if ENABLE_FEATURE_LS_USERNAME + if (all_fmt & LIST_ID_NAME) { + if (option_mask32 & OPT_g) { + column += printf("%-8.8s", + get_cached_username(dn->dstat.st_uid)); + } else { + column += printf("%-8.8s %-8.8s", + get_cached_username(dn->dstat.st_uid), + get_cached_groupname(dn->dstat.st_gid)); + } + } #endif - case LIST_ID_NUMERIC: - column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); - break; - case LIST_SIZE: - case LIST_DEV: - if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { - column += printf("%4d, %3d ", (int) MAJOR(dn->dstat.st_rdev), - (int) MINOR(dn->dstat.st_rdev)); + if (all_fmt & LIST_ID_NUMERIC) { + if (option_mask32 & OPT_g) + column += printf("%-8u", (int) dn->dstat.st_uid); + else + column += printf("%-8u %-8u", + (int) dn->dstat.st_uid, + (int) dn->dstat.st_gid); + } + if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) { + if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { + column += printf("%4u, %3u ", + (int) major(dn->dstat.st_rdev), + (int) minor(dn->dstat.st_rdev)); + } else { + if (all_fmt & LS_DISP_HR) { + column += printf("%9s ", + make_human_readable_str(dn->dstat.st_size, 1, 0)); } else { -#ifdef CONFIG_FEATURE_HUMAN_READABLE - if (all_fmt & LS_DISP_HR) { - column += printf("%9s ", - make_human_readable_str(dn->dstat.st_size, 1, 0)); - } else -#endif - { -#if _FILE_OFFSET_BITS == 64 - column += printf("%9lld ", (long long) dn->dstat.st_size); -#else - column += printf("%9ld ", dn->dstat.st_size); -#endif - } - } - break; -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS - case LIST_FULLTIME: - case LIST_DATE_TIME: - if (all_fmt & LIST_FULLTIME) { - printf("%24.24s ", filetime); - column += 25; - break; + column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); } - age = time(NULL) - ttime; + } + } +#if ENABLE_FEATURE_LS_TIMESTAMPS + if (all_fmt & LIST_FULLTIME) + column += printf("%24.24s ", filetime); + if (all_fmt & LIST_DATE_TIME) + if ((all_fmt & LIST_FULLTIME) == 0) { + /* current_time_t ~== time(NULL) */ + age = current_time_t - ttime; printf("%6.6s ", filetime + 4); if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { /* hh:mm if less than 6 months old */ @@ -732,332 +839,209 @@ static int list_single(struct dnode *dn) printf(" %4.4s ", filetime + 20); } column += 13; - break; + } #endif -#ifdef CONFIG_SELINUX - case LIST_CONTEXT: - { - char context[64]; - int len = sizeof(context); - if(security_sid_to_context(dn->sid, context, &len)) - { - strcpy(context, "unknown"); - len = 7; - } - printf("%-32s ", context); - column += MAX(33, len); +#if ENABLE_SELINUX + if (all_fmt & LIST_CONTEXT) { + column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); + freecon(dn->sid); + } +#endif + if (all_fmt & LIST_FILENAME) { +#if ENABLE_FEATURE_LS_COLOR + if (show_color) { + info.st_mode = 0; /* for fgcolor() */ + lstat(dn->fullname, &info); + printf("\033[%u;%um", bold(info.st_mode), + fgcolor(info.st_mode)); + } +#endif + column += print_name(dn->name); + if (show_color) { + printf("\033[0m"); + } + } + if (all_fmt & LIST_SYMLINK) { + if (S_ISLNK(dn->dstat.st_mode) && lpath) { + printf(" -> "); +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR +#if ENABLE_FEATURE_LS_COLOR + info.st_mode = 0; /* for fgcolor() */ +#endif + if (stat(dn->fullname, &info) == 0) { + append = append_char(info.st_mode); } - break; #endif - case LIST_FILENAME: -#ifdef CONFIG_FEATURE_LS_COLOR - errno = 0; - if (show_color && !lstat(dn->fullname, &info)) { - printf("\033[%d;%dm", bgcolor(info.st_mode), +#if ENABLE_FEATURE_LS_COLOR + if (show_color) { + printf("\033[%u;%um", bold(info.st_mode), fgcolor(info.st_mode)); } #endif - column += printf("%s", dn->name); -#ifdef CONFIG_FEATURE_LS_COLOR + column += print_name(lpath) + 4; if (show_color) { printf("\033[0m"); } -#endif - break; - case LIST_SYMLINK: - if (S_ISLNK(dn->dstat.st_mode)) { - char *lpath = xreadlink(dn->fullname); - - if (lpath) { - printf(" -> "); -#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR) - if (!stat(dn->fullname, &info)) { - append = append_char(info.st_mode); - } -#endif -#ifdef CONFIG_FEATURE_LS_COLOR - if (show_color) { - errno = 0; - printf("\033[%d;%dm", bgcolor(info.st_mode), - fgcolor(info.st_mode)); - } -#endif - column += printf("%s", lpath) + 4; -#ifdef CONFIG_FEATURE_LS_COLOR - if (show_color) { - printf("\033[0m"); - } -#endif - free(lpath); - } - } - break; -#ifdef CONFIG_FEATURE_LS_FILETYPES - case LIST_FILETYPE: - if (append != '\0') { - printf("%1c", append); - column++; - } - break; -#endif + free(lpath); } } +#if ENABLE_FEATURE_LS_FILETYPES + if (all_fmt & LIST_FILETYPE) { + if (append) { + putchar(append); + column++; + } + } +#endif return column; } -/*----------------------------------------------------------------------*/ - -static const char ls_opts[] = "1AaCdgilnsx" -#ifdef CONFIG_FEATURE_LS_FILETYPES - "Fp" -#endif -#ifdef CONFIG_FEATURE_LS_RECURSIVE - "R" -#endif -#ifdef CONFIG_FEATURE_LS_SORTFILES - "rSvX" -#endif -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS - "ecut" -#endif -#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS - "L" -#endif -#ifdef CONFIG_FEATURE_HUMAN_READABLE - "h" -#endif - "k" -#ifdef CONFIG_SELINUX - "K" -#endif -#ifdef CONFIG_FEATURE_AUTOWIDTH - "T:w:" -#endif - ; - -#define LIST_MASK_TRIGGER LIST_SHORT -#define STYLE_MASK_TRIGGER STYLE_MASK -#define SORT_MASK_TRIGGER SORT_MASK -#define DISP_MASK_TRIGGER DISP_ROWS -#define TIME_MASK_TRIGGER TIME_MASK - -static const unsigned opt_flags[] = { - LIST_SHORT | STYLE_SINGLE, /* 1 */ - DISP_HIDDEN, /* A */ - DISP_HIDDEN | DISP_DOT, /* a */ - LIST_SHORT | STYLE_COLUMNS, /* C */ - DISP_NOLIST, /* d */ - 0, /* g - ingored */ - LIST_INO, /* i */ - LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */ - LIST_ID_NUMERIC, /* n */ - LIST_BLOCKS, /* s */ - DISP_ROWS, /* x */ -#ifdef CONFIG_FEATURE_LS_FILETYPES - LIST_FILETYPE | LIST_EXEC, /* F */ - LIST_FILETYPE, /* p */ -#endif -#ifdef CONFIG_FEATURE_LS_RECURSIVE - DISP_RECURSIVE, /* R */ -#endif -#ifdef CONFIG_FEATURE_LS_SORTFILES - SORT_ORDER_REVERSE, /* r */ - SORT_SIZE, /* S */ - SORT_VERSION, /* v */ - SORT_EXT, /* v */ -#endif -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS - LIST_FULLTIME, /* e */ -#ifdef CONFIG_FEATURE_LS_SORTFILES - TIME_CHANGE | SORT_CTIME, /* c */ -#else - TIME_CHANGE, /* c */ -#endif -#ifdef CONFIG_FEATURE_LS_SORTFILES - TIME_ACCESS | SORT_ATIME, /* u */ -#else - TIME_ACCESS, /* u */ -#endif -#ifdef CONFIG_FEATURE_LS_SORTFILES - SORT_MTIME, /* t */ -#else - 0, /* t - ignored -- is this correct? */ -#endif -#endif -#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS - FOLLOW_LINKS, /* L */ -#endif -#ifdef CONFIG_FEATURE_HUMAN_READABLE -LS_DISP_HR, /* h */ -#endif -#ifndef CONFIG_SELINUX - 0, /* k - ingored */ -#else - LIST_CONTEXT, /* k */ - LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */ -#endif -}; - -/*----------------------------------------------------------------------*/ - -extern int ls_main(int argc, char **argv) +int ls_main(int argc UNUSED_PARAM, char **argv) { - struct dnode **dnf, **dnd; - int dnfiles, dndirs; - struct dnode *dn, *cur, **dnp; - int i, nfiles; - int opt; - int oi, ac; - char **av; -#ifdef CONFIG_SELINUX - is_flask_enabled_flag = is_flask_enabled(); + struct dnode **dnd; + struct dnode **dnf; + struct dnode **dnp; + struct dnode *dn; + struct dnode *cur; + unsigned opt; + int nfiles; + int dnfiles; + int dndirs; + int i; +#if ENABLE_FEATURE_LS_COLOR + /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ + /* coreutils 6.10: + * # ls --color=BOGUS + * ls: invalid argument 'BOGUS' for '--color' + * Valid arguments are: + * 'always', 'yes', 'force' + * 'never', 'no', 'none' + * 'auto', 'tty', 'if-tty' + * (and substrings: "--color=alwa" work too) + */ + static const char ls_longopts[] ALIGN1 = + "color\0" Optional_argument "\xff"; /* no short equivalent */ + static const char color_str[] ALIGN1 = + "always\0""yes\0""force\0" + "auto\0""tty\0""if-tty\0"; + /* need to initialize since --color has _an optional_ argument */ + const char *color_opt = color_str; /* "always" */ #endif -#ifdef CONFIG_FEATURE_AUTOWIDTH - struct winsize win = { 0, 0, 0, 0 }; -#endif + INIT_G(); - all_fmt = LIST_SHORT | DISP_NORMAL | STYLE_AUTO -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS - | TIME_MOD -#endif -#ifdef CONFIG_FEATURE_LS_SORTFILES - | SORT_NAME | SORT_ORDER_FORWARD -#endif - ; -#ifdef CONFIG_FEATURE_AUTOWIDTH - ioctl(fileno(stdout), TIOCGWINSZ, &win); - if (win.ws_col > 0) - terminal_width = win.ws_col - 1; -#endif - nfiles = 0; + all_fmt = LIST_SHORT | + (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD)); -#ifdef CONFIG_FEATURE_LS_COLOR - if (isatty(fileno(stdout))) - show_color = 1; +#if ENABLE_FEATURE_AUTOWIDTH + /* Obtain the terminal width */ + get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL); + /* Go one less... */ + terminal_width--; #endif /* process options */ - while ((opt = getopt(argc, argv, ls_opts)) > 0) { -#ifdef CONFIG_FEATURE_AUTOWIDTH - if (opt == 'T') { - tabstops = atoi(optarg); - continue; - } - if (opt == 'w') { - terminal_width = atoi(optarg); - continue; - } - if (opt == ':') { - goto print_usage_message; - } + USE_FEATURE_LS_COLOR(applet_long_options = ls_longopts;) +#if ENABLE_FEATURE_AUTOWIDTH + opt_complementary = "T+:w+"; /* -T N, -w N */ + opt = getopt32(argv, ls_options, &tabstops, &terminal_width + USE_FEATURE_LS_COLOR(, &color_opt)); +#else + opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt)); #endif - { - unsigned int flags; - const char *p = strchr(ls_opts, opt); - if (!p) { /* shouldn't be necessary */ - goto print_usage_message; - } - flags = opt_flags[(int)(p - ls_opts)]; - if (flags & LIST_MASK_TRIGGER) { + for (i = 0; opt_flags[i] != (1U<<31); i++) { + if (opt & (1 << i)) { + unsigned flags = opt_flags[i]; + + if (flags & LIST_MASK_TRIGGER) all_fmt &= ~LIST_MASK; - } - if (flags & STYLE_MASK_TRIGGER) { + if (flags & STYLE_MASK_TRIGGER) all_fmt &= ~STYLE_MASK; - } -#ifdef CONFIG_FEATURE_LS_SORTFILES - if (flags & SORT_MASK_TRIGGER) { + if (flags & SORT_MASK_TRIGGER) all_fmt &= ~SORT_MASK; - } -#endif - if (flags & DISP_MASK_TRIGGER) { + if (flags & DISP_MASK_TRIGGER) all_fmt &= ~DISP_MASK; - } -#ifdef CONFIG_FEATURE_LS_TIMESTAMPS - if (flags & TIME_MASK_TRIGGER) { + if (flags & TIME_MASK) all_fmt &= ~TIME_MASK; - } -#endif - if (flags & LIST_CONTEXT) { + if (flags & LIST_CONTEXT) all_fmt |= STYLE_SINGLE; - } -#ifdef CONFIG_FEATURE_HUMAN_READABLE - if (opt == 'l') { - all_fmt &= ~LS_DISP_HR; - } -#endif + /* huh?? opt cannot be 'l' */ + //if (LS_DISP_HR && opt == 'l') + // all_fmt &= ~LS_DISP_HR; all_fmt |= flags; } } +#if ENABLE_FEATURE_LS_COLOR + /* find color bit value - last position for short getopt */ + 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") != 0)) + show_color = 1; + } + if (opt & OPT_color) { + if (color_opt[0] == 'n') + show_color = 0; + else switch (index_in_substrings(color_str, color_opt)) { + case 3: + case 4: + case 5: + if (isatty(STDOUT_FILENO)) { + case 0: + case 1: + case 2: + show_color = 1; + } + } + } +#endif /* sort out which command line options take precedence */ -#ifdef CONFIG_FEATURE_LS_RECURSIVE - if (all_fmt & DISP_NOLIST) + if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST)) all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ -#endif -#if defined (CONFIG_FEATURE_LS_TIMESTAMPS) && defined (CONFIG_FEATURE_LS_SORTFILES) - if (all_fmt & TIME_CHANGE) - all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; - if (all_fmt & TIME_ACCESS) - all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; -#endif + if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { + if (all_fmt & TIME_CHANGE) + all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; + if (all_fmt & TIME_ACCESS) + all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; + } if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */ all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC); -#ifdef CONFIG_FEATURE_LS_USERNAME - if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC)) - all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ -#endif - + if (ENABLE_FEATURE_LS_USERNAME) + if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC)) + all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ + /* choose a display format */ - if ((all_fmt & STYLE_MASK) == STYLE_AUTO) -#if STYLE_AUTO != 0 - all_fmt = (all_fmt & ~STYLE_MASK) - | (isatty(fileno(stdout)) ? STYLE_COLUMNS : STYLE_SINGLE); -#else - all_fmt |= (isatty(fileno(stdout)) ? STYLE_COLUMNS : STYLE_SINGLE); -#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) { - av = (char **) xcalloc((size_t) 1, (size_t) (sizeof(char *))); - av[0] = bb_xstrdup("."); - ac = 1; - } else { - av = (char **) xcalloc((size_t) ac, (size_t) (sizeof(char *))); - for (oi = 0; oi < ac; oi++) { - av[oi] = argv[optind++]; /* copy pointer to real cmd line arg */ - } - } + 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 an dnode array */ + /* stuff the command line file names into a dnode array */ dn = NULL; - for (oi = 0; oi < ac; oi++) { - char *fullname = bb_xstrdup(av[oi]); - - cur = my_stat(fullname, fullname); + nfiles = 0; + do { + /* NB: follow links on command line unless -l or -s */ + cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS))); + 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 + * allocate memory for an array to hold dnode pointers */ dnp = dnalloc(nfiles); for (i = 0, cur = dn; i < nfiles; i++) { @@ -1065,11 +1049,8 @@ extern int ls_main(int argc, char **argv) cur = cur->next; } - if (all_fmt & DISP_NOLIST) { -#ifdef CONFIG_FEATURE_LS_SORTFILES - shellsort(dnp, nfiles); -#endif + dnsort(dnp, nfiles); if (nfiles > 0) showfiles(dnp, nfiles); } else { @@ -1078,20 +1059,19 @@ extern int ls_main(int argc, char **argv) dndirs = countdirs(dnp, nfiles); dnfiles = nfiles - dndirs; if (dnfiles > 0) { -#ifdef CONFIG_FEATURE_LS_SORTFILES - shellsort(dnf, dnfiles); -#endif + dnsort(dnf, dnfiles); showfiles(dnf, dnfiles); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnf); } if (dndirs > 0) { -#ifdef CONFIG_FEATURE_LS_SORTFILES - shellsort(dnd, dndirs); -#endif - showdirs(dnd, dndirs); + dnsort(dnd, dndirs); + showdirs(dnd, dndirs, dnfiles == 0); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnd); } } - return (status); - - print_usage_message: - bb_show_usage(); + if (ENABLE_FEATURE_CLEAN_UP) + dfree(dnp, nfiles); + return exit_code; } -- cgit v1.2.3-54-g00ecf