X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=coreutils%2Fls.c;h=4e21454cef9af6d5e4dd91b38b2843c85d20a968;hb=5cfa5ef6f358bb979b76f8927899b920074f698d;hp=207c617636cb81c48d1077fd0eb1c1e6af6f9249;hpb=3a0f4baf2fda25aafdd43589265980c5dbb19fc1;p=oweals%2Fbusybox.git diff --git a/coreutils/ls.c b/coreutils/ls.c index 207c61763..4e21454ce 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -2,7 +2,7 @@ /* * 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 @@ -21,7 +21,7 @@ /* * 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 + * (i.e., the ones I couldn't live without :-) All features which involve * linking in substantial chunks of libc can be disabled. * * Although I don't really want to add new features to this program to @@ -41,607 +41,1094 @@ * 1. requires lstat (BSD) - how do you do it without? */ -#define TERMINAL_WIDTH 80 /* use 79 if your terminal has linefold bug */ -#define COLUMN_WIDTH 14 /* default if AUTOWIDTH not defined */ -#define COLUMN_GAP 2 /* includes the file type char, if present */ -#define HAS_REWINDDIR +enum { + TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ + COLUMN_GAP = 2, /* includes the file type char */ +}; /************************************************************************/ -#include "internal.h" -# include +#include +#include #include #include #include #include #include -#ifdef BB_FEATURE_LS_TIMESTAMPS +#include +#include +#include +#include +#include +#include +#include /* major() and minor() */ +#include "busybox.h" +#ifdef CONFIG_SELINUX +#include +#include +#include +#endif + +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS #include #endif -#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) -#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) -#ifdef BB_FEATURE_LS_FILETYPES -#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) +/* 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 + +/* 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) + +/* 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 + +#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) + +#define TIME_MASK (3U<<23) +#endif + +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS +#define FOLLOW_LINKS (1U<<25) +#endif +#ifdef CONFIG_FEATURE_HUMAN_READABLE +#define LS_DISP_HR (1U<<26) #endif -#define FMT_AUTO 0 -#define FMT_LONG 1 /* one record per line, extended info */ -#define FMT_SINGLE 2 /* one record per line */ -#define FMT_ROWS 3 /* print across rows */ -#define FMT_COLUMNS 3 /* fill columns (same, since we don't sort) */ +#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) -#define TIME_MOD 0 -#define TIME_CHANGE 1 -#define TIME_ACCESS 2 +#define SPLIT_DIR 1 +#define SPLIT_FILE 0 +#define SPLIT_SUBDIR 2 -#define DISP_FTYPE 1 /* show character for file type */ -#define DISP_EXEC 2 /* show '*' if regular executable file */ -#define DISP_HIDDEN 4 /* show files starting . (except . and ..) */ -#define DISP_DOT 8 /* show . and .. */ -#define DISP_NUMERIC 16 /* numeric uid and gid */ -#define DISP_FULLTIME 32 /* show extended time display */ -#define DIR_NOLIST 64 /* show directory as itself, not contents */ -#define DISP_DIRNAME 128 /* show directory name (for internal use) */ -#define DISP_RECURSIVE 256 /* Do a recursive listing */ +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) -#ifndef MAJOR -#define MAJOR(dev) (((dev)>>8)&0xff) -#define MINOR(dev) ((dev)&0xff) +#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 -#ifdef BB_FEATURE_LS_SORTFILES -struct dnode { /* the basic node */ - char *name; /* the dir entry name */ - char *fullname; /* the dir entry name */ - struct stat dstat; /* the file stat info */ +/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ +#ifdef CONFIG_FEATURE_LS_COLOR +static int show_color = 0; + +#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)]) +#endif + +/* + * 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 */ }; typedef struct dnode dnode_t; -#endif -static unsigned char display_fmt = FMT_AUTO; -static unsigned short opts = 0; -static unsigned short column = 0; - -#ifdef BB_FEATURE_AUTOWIDTH -static unsigned short terminal_width = 0; -static unsigned short column_width = 0; -static unsigned short toplevel_column_width = 0; -#else -#define terminal_width TERMINAL_WIDTH -#define column_width COLUMN_WIDTH + +static struct dnode **list_dir(const char *); +static struct dnode **dnalloc(int); +static int list_single(struct dnode *); + +static unsigned int all_fmt; + +#ifdef CONFIG_SELINUX +static int is_flask_enabled_flag; #endif -#ifdef BB_FEATURE_LS_TIMESTAMPS -static unsigned char time_fmt = TIME_MOD; +#ifdef CONFIG_FEATURE_AUTOWIDTH +static int terminal_width = TERMINAL_WIDTH; +static unsigned short tabstops = COLUMN_GAP; +#else +#define tabstops COLUMN_GAP +#define terminal_width TERMINAL_WIDTH #endif -#define wr(data,len) fwrite(data, 1, len, stdout) +static int status = EXIT_SUCCESS; -static void writenum(long val, short minwidth) +static struct dnode *my_stat(char *fullname, char *name) { - char scratch[128]; - - char *p = scratch + sizeof(scratch); - short len = 0; - short neg = (val < 0); - - if (neg) - val = -val; - do - *--p = (val % 10) + '0', len++, val /= 10; - while (val); - if (neg) - *--p = '-', len++; - while (len < minwidth) - *--p = ' ', len++; - wr(p, len); - column += len; + struct stat dstat; + struct dnode *cur; +#ifdef CONFIG_SELINUX + security_id_t sid; +#endif + int rc; + +#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 +#endif + rc = stat(fullname, &dstat); + if(rc) + { + bb_perror_msg("%s", fullname); + status = EXIT_FAILURE; + return 0; + } + } else +#endif + { +#ifdef CONFIG_SELINUX + if(is_flask_enabled_flag) + rc = lstat_secure(fullname, &dstat, &sid); + else +#endif + rc = lstat(fullname, &dstat); + if(rc) + { + bb_perror_msg("%s", fullname); + status = EXIT_FAILURE; + return 0; + } + } + + cur = (struct dnode *) xmalloc(sizeof(struct dnode)); + cur->fullname = fullname; + cur->name = name; + cur->dstat = dstat; +#ifdef CONFIG_SELINUX + cur->sid = sid; +#endif + return cur; } -static void newline(void) +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_FEATURE_LS_COLOR +static char fgcolor(mode_t mode) { - if (column > 0) { - wr("\n", 1); - column = 0; + /* 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))) + return COLOR(0xF000); /* File is executable ... */ + return COLOR(mode); } -static void tab(short col) +/*----------------------------------------------------------------------*/ +static char bgcolor(mode_t mode) { - static const char spaces[] = " "; - -#define nspaces ((sizeof spaces)-1) /* null terminator! */ - - short n = col - column; - - if (n > 0) { - column = col; - while (n > nspaces) { - wr(spaces, nspaces); - n -= nspaces; - } - /* must be 1...(sizeof spaces) left */ - wr(spaces, n); - } -#undef nspaces + if (LIST_EXEC && S_ISREG(mode) + && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return ATTR(0xF000); /* File is executable ... */ + return ATTR(mode); } +#endif -#ifdef BB_FEATURE_LS_FILETYPES +/*----------------------------------------------------------------------*/ +#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR) static char append_char(mode_t mode) { - if (!(opts & DISP_FTYPE)) + if (!(all_fmt & LIST_FILETYPE)) return '\0'; - if ((opts & DISP_EXEC) && S_ISREG(mode) - && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; + if ((all_fmt & LIST_EXEC) && S_ISREG(mode) + && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return '*'; return APPCHAR(mode); } #endif -/** - ** - ** Display a file or directory as a single item - ** (in either long or short format) - ** - **/ +/*----------------------------------------------------------------------*/ + +#define countdirs(A,B) count_dirs((A), (B), 1) +#define countsubdirs(A,B) count_dirs((A), (B), 0) -static void list_single(const char *name, struct stat *info, - const char *fullname) +static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs) { - char scratch[BUFSIZ + 1]; - short len = strlen(name); - -#ifdef BB_FEATURE_LS_FILETYPES - char append = append_char(info->st_mode); -#endif - - if (display_fmt == FMT_LONG) { - mode_t mode = info->st_mode; - - newline(); - wr(modeString(mode), 10); - column = 10; - writenum((long) info->st_nlink, (short) 5); - fputs(" ", stdout); -#ifdef BB_FEATURE_LS_USERNAME - if (!(opts & DISP_NUMERIC)) { - memset(scratch, 0, sizeof(scratch)); - my_getpwuid(scratch, info->st_uid); - if (*scratch) { - fputs(scratch, stdout); - if (strlen(scratch) <= 8) - wr(" ", 9 - strlen(scratch)); - } else { - writenum((long) info->st_uid, (short) 8); - fputs(" ", stdout); - } - } else + int i, dirs; + + if (dn == NULL || nfiles < 1) + 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]))))) + dirs++; + } + return (dirs); +} + +static int countfiles(struct dnode **dnp) +{ + int nfiles; + struct dnode *cur; + + if (dnp == NULL) + return (0); + nfiles = 0; + for (cur = dnp[0]; cur->next != NULL; cur = cur->next) + nfiles++; + 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); + + p = (struct dnode **) xcalloc((size_t) num, + (size_t) (sizeof(struct dnode *))); + return (p); +} + +#ifdef CONFIG_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp) +{ + struct dnode *cur, *next; + + if (dnp == NULL) + return; + + cur = dnp[0]; + while (cur != NULL) { + free(cur->fullname); /* free the filename */ + next = cur->next; + free(cur); /* free the dnode */ + cur = next; + } + free(dnp); /* free the array holding the dnode pointers */ +} #endif - { - writenum((long) info->st_uid, (short) 8); - fputs(" ", stdout); - } -#ifdef BB_FEATURE_LS_USERNAME - if (!(opts & DISP_NUMERIC)) { - memset(scratch, 0, sizeof(scratch)); - my_getgrgid(scratch, info->st_gid); - if (*scratch) { - fputs(scratch, stdout); - if (strlen(scratch) <= 8) - wr(" ", 8 - strlen(scratch)); - } else - writenum((long) info->st_gid, (short) 8); - } else -#endif - writenum((long) info->st_gid, (short) 8); - //tab(26); - if (S_ISBLK(mode) || S_ISCHR(mode)) { - writenum((long) MAJOR(info->st_rdev), (short) 3); - fputs(", ", stdout); - writenum((long) MINOR(info->st_rdev), (short) 3); - } else - writenum((long) info->st_size, (short) 8); - fputs(" ", stdout); - //tab(32); -#ifdef BB_FEATURE_LS_TIMESTAMPS - { - time_t cal; - char *string; - - switch (time_fmt) { - case TIME_CHANGE: - cal = info->st_ctime; - break; - case TIME_ACCESS: - cal = info->st_atime; - break; - default: - cal = info->st_mtime; - break; - } - string = ctime(&cal); - if (opts & DISP_FULLTIME) - wr(string, 24); - else { - time_t age = time(NULL) - cal; - - wr(string + 4, 7); /* mmm_dd_ */ - if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) - /* hh:mm if less than 6 months old */ - wr(string + 11, 5); - else - /* _yyyy otherwise */ - wr(string + 19, 5); - } - wr(" ", 1); - } -#else - fputs("--- -- ----- ", stdout); -#endif - wr(name, len); - if (S_ISLNK(mode)) { - wr(" -> ", 4); - len = readlink(fullname, scratch, sizeof scratch); - if (len > 0) - fwrite(scratch, 1, len, stdout); -#ifdef BB_FEATURE_LS_FILETYPES - /* show type of destination */ - if (opts & DISP_FTYPE) { - if (!stat(fullname, info)) { - append = append_char(info->st_mode); - if (append) - fputc(append, stdout); - } + +static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) +{ + int dncnt, i, d; + struct dnode **dnp; + + if (dn == NULL || nfiles < 1) + return (NULL); + + /* count how many dirs and regular files there are */ + if (which == SPLIT_SUBDIR) + dncnt = countsubdirs(dn, nfiles); + else { + dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */ + if (which == SPLIT_FILE) + dncnt = nfiles - dncnt; /* looking for files */ + } + + /* allocate a file array and a dir array */ + dnp = dnalloc(dncnt); + + /* 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]; + } } -#endif + } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { + dnp[d++] = dn[i]; } -#ifdef BB_FEATURE_LS_FILETYPES - else if (append) - wr(&append, 1); -#endif - } else { - static short nexttab = 0; - - /* sort out column alignment */ - if (column == 0); /* nothing to do */ - else if (display_fmt == FMT_SINGLE) - newline(); - else { - if (nexttab + column_width > terminal_width -#ifndef BB_FEATURE_AUTOWIDTH - || nexttab + len >= terminal_width -#endif - ) - newline(); - else - tab(nexttab); - } - /* work out where next column starts */ -#ifdef BB_FEATURE_AUTOWIDTH - /* we know the calculated width is big enough */ - nexttab = column + column_width + COLUMN_GAP; + } + return (dnp); +} + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_FEATURE_LS_SORTFILES +static int sortcmp(struct dnode *d1, struct dnode *d2) +{ + unsigned int sort_opts = all_fmt & SORT_MASK; + int dif; + + dif = 0; /* assume SORT_NAME */ + if (sort_opts == SORT_SIZE) { + dif = (int) (d2->dstat.st_size - d1->dstat.st_size); + } else if (sort_opts == SORT_ATIME) { + dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime); + } else if (sort_opts == SORT_CTIME) { + dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime); + } else if (sort_opts == SORT_MTIME) { + dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime); + } else if (sort_opts == SORT_DIR) { + dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode); + /* } else if (sort_opts == SORT_VERSION) { */ + /* } else if (sort_opts == SORT_EXT) { */ + } + + 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 - /* might cover more than one fixed-width column */ - nexttab = column; - do - nexttab += column_width + COLUMN_GAP; - while (nexttab < (column + len + COLUMN_GAP)); -#endif - /* now write the data */ - wr(name, len); - column = column + len; -#ifdef BB_FEATURE_LS_FILETYPES - if (append) - wr(&append, 1), column++; + dif = strcmp(d1->name, d2->name); #endif } + + if (all_fmt & SORT_ORDER_REVERSE) { + dif = -dif; + } + return (dif); } -#ifdef BB_FEATURE_LS_SORTFILES -void shellsort(struct dnode *dn[], int size) +/*----------------------------------------------------------------------*/ +static void shellsort(struct dnode **dn, int size) { - struct dnode *temp; - int gap, i, j; - - /* shell short the array */ - for (gap= size/2; gap>0; gap /=2) { - for (i=gap; i=0; j-=gap) { - if (strcmp(dn[j]->name, dn[j+gap]->name) <= 0) - break; - temp= dn[j]; - dn[j]= dn[j+gap]; - dn[j+gap]= temp; - } - } - } + 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; + } + } + } } +#endif -void showdnodes(struct dnode *dn[], int nfiles) +/*----------------------------------------------------------------------*/ +static void showfiles(struct dnode **dn, int nfiles) { - int nf, nc; - int ncols, fpc, i; - - ncols= (int)(terminal_width / (column_width + COLUMN_GAP)); - /* files per column. The +1 means the last col is shorter than others */ - fpc= (nfiles / ncols) + 1; - for (nf=0; nfname) + +#ifdef CONFIG_SELINUX + ((all_fmt & LIST_CONTEXT) ? 33 : 0) + +#endif + ((all_fmt & LIST_INO) ? 8 : 0) + + ((all_fmt & LIST_BLOCKS) ? 5 : 0); + if (column_width < len) + column_width = len; + } + column_width += tabstops; + ncols = (int) (terminal_width / column_width); + } + + if (ncols > 1) { + nrows = nfiles / ncols; + if ((nrows * ncols) < nfiles) + nrows++; /* round up fractionals */ + } else { + nrows = nfiles; + ncols = 1; + } + + for (row = 0; row < nrows; row++) { + for (nc = 0; nc < ncols; nc++) { /* reach into the array based on the column and row */ - i= (nc * fpc) + nf; - if (i >= nfiles) { - newline(); - } else { - list_single(dn[i]->name, &dn[i]->dstat, dn[i]->fullname); + i = (nc * nrows) + row; /* assume display by column */ + if (all_fmt & DISP_ROWS) + i = (row * ncols) + nc; /* display across row */ + if (i < nfiles) { + if (column > 0) { + nexttab -= column; + while (nexttab--) { + putchar(' '); + column++; + } } + nexttab = column + column_width; + column += list_single(dn[i]); + } } + putchar('\n'); + column = 0; } } -#endif -/** - ** - ** List the given file or directory, expanding a directory - ** to show its contents if required - ** - **/ - -static int list_item(const char *name) +/*----------------------------------------------------------------------*/ +static void showdirs(struct dnode **dn, int ndirs, int first) { - struct stat info; - DIR *dir; - struct dirent *entry; - char fullname[BUFSIZ + 1], *fnend; -#ifdef BB_FEATURE_LS_SORTFILES - int ni=0, nfiles=0; - struct dnode **dnp; - dnode_t *cur; + int i, nfiles; + struct dnode **subdnp; + +#ifdef CONFIG_FEATURE_LS_RECURSIVE + int dndirs; + struct dnode **dnd; #endif - if (lstat(name, &info)) - goto listerr; + if (dn == NULL || ndirs < 1) + return; - if (!S_ISDIR(info.st_mode) || (opts & DIR_NOLIST)) { -#ifdef BB_FEATURE_AUTOWIDTH - column_width = toplevel_column_width; + for (i = 0; i < ndirs; i++) { + if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { + if (!first) + printf("\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 - list_single(name, &info, name); - return 0; + 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, 0); + free(dnd); /* free the array of dnode pointers to the dirs */ + } + } + dfree(subdnp); /* free the dnodes and the fullname mem */ +#endif + } } +} - /* Otherwise, it's a directory we want to list the contents of */ +/*----------------------------------------------------------------------*/ +static struct dnode **list_dir(const char *path) +{ + struct dnode *dn, *cur, **dnp; + struct dirent *entry; + DIR *dir; + int i, nfiles; - if (opts & DISP_DIRNAME) { /* identify the directory */ - if (column) - wr("\n\n", 2), column = 0; - wr(name, strlen(name)); - wr(":\n", 2); - } + if (path == NULL) + return (NULL); - dir = opendir(name); - if (!dir) - goto listerr; -#ifdef BB_FEATURE_AUTOWIDTH - column_width = 0; + dn = NULL; + nfiles = 0; + dir = opendir(path); + if (dir == NULL) { + bb_perror_msg("%s", path); + status = EXIT_FAILURE; + return (NULL); /* could not open the dir */ + } while ((entry = readdir(dir)) != NULL) { - short w = strlen(entry->d_name); -#ifdef BB_FEATURE_LS_SORTFILES - const char *en = entry->d_name; - - if (en[0] == '.') { - if (!en[1] || (en[1] == '.' && !en[2])) { /* . or .. */ - if (!(opts & DISP_DOT)) - continue; - } else if (!(opts & DISP_HIDDEN)) - continue; - } - nfiles++; /* count how many files there will be */ -#endif + char *fullname; - if (column_width < w) - column_width = w; + /* 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 (!(all_fmt & DISP_HIDDEN)) + continue; + } + fullname = concat_path_file(path, entry->d_name); + cur = my_stat(fullname, strrchr(fullname, '/') + 1); + if (!cur) + continue; + cur->next = dn; + dn = cur; + nfiles++; } -#ifdef HAS_REWINDDIR - rewinddir(dir); -#else closedir(dir); - dir = opendir(name); - if (!dir) - goto listerr; -#endif -#endif -#ifdef BB_FEATURE_LS_SORTFILES + /* 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= (struct dnode **)calloc((size_t)nfiles, (size_t)(sizeof(struct dnode *))); + if (dn == 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); +} + +/*----------------------------------------------------------------------*/ +static int list_single(struct dnode *dn) +{ + int i, column = 0; + +#ifdef CONFIG_FEATURE_LS_USERNAME + char scratch[16]; +#endif +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + char *filetime; + time_t ttime, age; +#endif +#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR) + struct stat info; + char append; #endif - /* List the contents */ + if (dn->fullname == NULL) + return (0); - strcpy(fullname, name); /* *** ignore '.' by itself */ - fnend = fullname + strlen(fullname); - if (fnend[-1] != '/') - *fnend++ = '/'; +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + ttime = dn->dstat.st_mtime; /* the default time */ + if (all_fmt & TIME_ACCESS) + ttime = dn->dstat.st_atime; + if (all_fmt & TIME_CHANGE) + ttime = dn->dstat.st_ctime; + filetime = ctime(&ttime); +#endif +#ifdef CONFIG_FEATURE_LS_FILETYPES + append = append_char(dn->dstat.st_mode); +#endif - while ((entry = readdir(dir)) != NULL) { - const char *en = entry->d_name; - - if (en[0] == '.') { - if (!en[1] || (en[1] == '.' && !en[2])) { /* . or .. */ - if (!(opts & DISP_DOT)) - continue; - } else if (!(opts & DISP_HIDDEN)) - continue; - } - /* FIXME: avoid stat if not required */ - strcpy(fnend, entry->d_name); -#ifdef BB_FEATURE_LS_SORTFILES - /* allocate memory for a node and memory for the file name */ - cur= (struct dnode *)malloc(sizeof(struct dnode)); - cur->fullname= strcpy((char *)malloc(strlen(fullname)+1), fullname); - cur->name= cur->fullname + (int)(fnend - fullname) ; - lstat(fullname, &cur->dstat); /* get file stat info into node */ - dnp[ni++]= cur; /* save pointer to node in array */ + 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 - if (lstat(fullname, &info)) { - closedir(dir); - goto listerr; /* (shouldn't fail) */ - } - list_single(entry->d_name, &info, fullname); + column += printf("%4ld ", dn->dstat.st_blocks >> 1); #endif - } - closedir(dir); -#ifdef BB_FEATURE_LS_SORTFILES - shellsort(dnp, nfiles); - showdnodes(dnp, nfiles); + 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, sizeof(scratch)); + printf("%-8.8s ", scratch); + my_getgrgid(scratch, dn->dstat.st_gid, sizeof(scratch)); + printf("%-8.8s", scratch); + column += 17; + break; +#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)); + } 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: + printf("%24.24s ", filetime); + column += 25; + break; + case LIST_DATE_TIME: + if ((all_fmt & LIST_FULLTIME) == 0) { + age = time(NULL) - ttime; + printf("%6.6s ", filetime + 4); + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { + /* hh:mm if less than 6 months old */ + printf("%5.5s ", filetime + 11); + } else { + 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); + } + 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), + fgcolor(info.st_mode)); + } #endif + column += printf("%s", dn->name); +#ifdef CONFIG_FEATURE_LS_COLOR + if (show_color) { + printf("\033[0m"); + } +#endif + break; + case LIST_SYMLINK: + if (S_ISLNK(dn->dstat.st_mode)) { + char *lpath = xreadlink(dn->fullname); - if (opts & DISP_DIRNAME) { /* separate the directory */ - if (column) { - wr("\n", 1); + 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 } - wr("\n", 1); - column = 0; } - return 0; - - listerr: - newline(); - perror(name); - return 1; + return column; } -#ifdef BB_FEATURE_LS_RECURSIVE -static int dirAction(const char *fileName, struct stat *statbuf, void* junk) -{ - int i; - fprintf(stdout, "\n%s:\n", fileName); - i = list_item(fileName); - newline(); - return (i); -} +/*----------------------------------------------------------------------*/ + +/* "[-]Cadil1", POSIX mandated options, busybox always supports */ +/* "[-]gnsx", POSIX non-mandated options, 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 */ + +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS +# define LS_STR_TIMESTAMPS "cetu" +#else +# define LS_STR_TIMESTAMPS "" +#endif + +#ifdef CONFIG_FEATURE_LS_SORTFILES +# define LS_STR_SORTFILES "SXrv" +#else +# define LS_STR_SORTFILES "" +#endif + +#ifdef CONFIG_FEATURE_LS_FILETYPES +# define LS_STR_FILETYPES "Fp" +#else +# define LS_STR_FILETYPES "" +#endif + +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS +# define LS_STR_FOLLOW_LINKS "L" +#else +# define LS_STR_FOLLOW_LINKS "" +#endif + +#ifdef CONFIG_FEATURE_LS_RECURSIVE +# define LS_STR_RECURSIVE "R" +#else +# define LS_STR_RECURSIVE "" +#endif + +#ifdef CONFIG_FEATURE_HUMAN_READABLE +# define LS_STR_HUMAN_READABLE "h" +#else +# define LS_STR_HUMAN_READABLE "" +#endif + +#ifdef CONFIG_SELINUX +# define LS_STR_SELINUX "K" +#else +# define LS_STR_SELINUX "" #endif +#ifdef CONFIG_FEATURE_AUTOWIDTH +# define LS_STR_AUTOWIDTH "T:w:" +#else +# define LS_STR_AUTOWIDTH "" +#endif + +static const char ls_options[]="Cadil1gnsxAk" \ + LS_STR_TIMESTAMPS \ + LS_STR_SORTFILES \ + LS_STR_FILETYPES \ + LS_STR_FOLLOW_LINKS \ + LS_STR_RECURSIVE \ + LS_STR_HUMAN_READABLE \ + LS_STR_SELINUX \ + LS_STR_AUTOWIDTH; + +#define LIST_MASK_TRIGGER 0 +#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_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 - ingored */ + LIST_ID_NUMERIC, /* n */ + LIST_BLOCKS, /* s */ + DISP_ROWS, /* x */ + DISP_HIDDEN, /* A */ +#ifdef CONFIG_SELINUX + LIST_CONTEXT, /* k */ +#else + 0, /* k - ingored */ +#endif +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS +# ifdef CONFIG_FEATURE_LS_SORTFILES + TIME_CHANGE | SORT_CTIME, /* c */ +# else + TIME_CHANGE, /* c */ +# endif + LIST_FULLTIME, /* e */ +# ifdef CONFIG_FEATURE_LS_SORTFILES + SORT_MTIME, /* t */ +# else + 0, /* t - ignored -- is this correct? */ +# endif +# ifdef CONFIG_FEATURE_LS_SORTFILES + TIME_ACCESS | SORT_ATIME, /* u */ +# else + TIME_ACCESS, /* u */ +# endif +#endif +#ifdef CONFIG_FEATURE_LS_SORTFILES + SORT_SIZE, /* S */ + SORT_EXT, /* X */ + SORT_ORDER_REVERSE, /* r */ + SORT_VERSION, /* v */ +#endif +#ifdef CONFIG_FEATURE_LS_FILETYPES + LIST_FILETYPE | LIST_EXEC, /* F */ + LIST_FILETYPE, /* p */ +#endif +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS + FOLLOW_LINKS, /* L */ +#endif +#ifdef CONFIG_FEATURE_LS_RECURSIVE + DISP_RECURSIVE, /* R */ +#endif +#ifdef CONFIG_FEATURE_HUMAN_READABLE + LS_DISP_HR, /* h */ +#endif +#ifdef CONFIG_SELINUX + LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */ +#endif + (1U<<31) +}; + + +/*----------------------------------------------------------------------*/ + extern int ls_main(int argc, char **argv) { - int argi = 1, i; + struct dnode **dnd; + struct dnode **dnf; + struct dnode **dnp; + struct dnode *dn; + struct dnode *cur; + long opt; + int nfiles = 0; + int dnfiles; + int dndirs; + int oi; + int ac; + int i; + char **av; +#ifdef CONFIG_FEATURE_AUTOWIDTH + char *tabstops_str = NULL; + char *terminal_width_str = NULL; +#endif + +#ifdef CONFIG_SELINUX + is_flask_enabled_flag = is_flask_enabled(); +#endif + + 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 + /* Obtain the terminal width. */ + get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL); + /* Go one less... */ + terminal_width--; +#endif + +#ifdef CONFIG_FEATURE_LS_COLOR + if (isatty(STDOUT_FILENO)) + show_color = 1; +#endif /* process options */ - while (argi < argc && argv[argi][0] == '-') { - const char *p = &argv[argi][1]; - - if (!*p) - goto print_usage_message; /* "-" by itself not allowed */ - if (*p == '-') { - if (!p[1]) { /* "--" forces end of options */ - argi++; - break; +#ifdef CONFIG_FEATURE_AUTOWIDTH + opt = bb_getopt_ulflags(argc, argv, ls_options, &tabstops_str, &terminal_width_str); + if (tabstops_str) { + tabstops = atoi(tabstops_str); + } + if (terminal_width_str) { + terminal_width = atoi(terminal_width_str); + } +#else + opt = bb_getopt_ulflags(argc, argv, ls_options); +#endif + for (i = 0; opt_flags[i] != (1U<<31); i++) { + if (opt & (1 << i)) { + unsigned int flags = opt_flags[i]; + if (flags & LIST_MASK_TRIGGER) { + all_fmt &= ~LIST_MASK; } - /* it's a long option name - we don't support them */ - goto print_usage_message; - } - - while (*p) - switch (*p++) { - case 'l': - display_fmt = FMT_LONG; - break; - case '1': - display_fmt = FMT_SINGLE; - break; - case 'x': - display_fmt = FMT_ROWS; - break; - case 'C': - display_fmt = FMT_COLUMNS; - break; -#ifdef BB_FEATURE_LS_FILETYPES - case 'p': - opts |= DISP_FTYPE; - break; - case 'F': - opts |= DISP_FTYPE | DISP_EXEC; - break; -#endif - case 'A': - opts |= DISP_HIDDEN; - break; - case 'a': - opts |= DISP_HIDDEN | DISP_DOT; - break; - case 'n': - opts |= DISP_NUMERIC; - break; - case 'd': - opts |= DIR_NOLIST; - break; -#ifdef BB_FEATURE_LS_TIMESTAMPS - case 'u': - time_fmt = TIME_ACCESS; - break; - case 'c': - time_fmt = TIME_CHANGE; - break; - case 'e': - opts |= DISP_FULLTIME; - break; -#endif -#ifdef BB_FEATURE_LS_RECURSIVE - case 'R': - opts |= DISP_RECURSIVE; - break; -#endif - case 'g': /* ignore -- for ftp servers */ - break; - default: - goto print_usage_message; + if (flags & STYLE_MASK_TRIGGER) { + all_fmt &= ~STYLE_MASK; } - - argi++; +#ifdef CONFIG_FEATURE_LS_SORTFILES + if (flags & SORT_MASK_TRIGGER) { + all_fmt &= ~SORT_MASK; + } +#endif + if (flags & DISP_MASK_TRIGGER) { + all_fmt &= ~DISP_MASK; + } +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + if (flags & TIME_MASK_TRIGGER) { + all_fmt &= ~TIME_MASK; + } +#endif + if (flags & LIST_CONTEXT) { + all_fmt |= STYLE_SINGLE; + } +#ifdef CONFIG_FEATURE_HUMAN_READABLE + if (opt == 'l') { + all_fmt &= ~LS_DISP_HR; + } +#endif + all_fmt |= flags; + } } + /* sort out which command line options take precedence */ +#ifdef CONFIG_FEATURE_LS_RECURSIVE + if (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 ((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 + /* choose a display format */ - if (display_fmt == FMT_AUTO) - display_fmt = isatty(fileno(stdout)) ? FMT_COLUMNS : FMT_SINGLE; - if (argi < argc - 1) - opts |= DISP_DIRNAME; /* 2 or more items? label directories */ -#ifdef BB_FEATURE_AUTOWIDTH - /* could add a -w option and/or TIOCGWINSZ call */ - if (terminal_width < 1) - terminal_width = TERMINAL_WIDTH; - - for (i = argi; i < argc; i++) { - int len = strlen(argv[i]); - - if (toplevel_column_width < len) - toplevel_column_width = len; - } + if ((all_fmt & STYLE_MASK) == STYLE_AUTO) +#if STYLE_AUTO != 0 + all_fmt = (all_fmt & ~STYLE_MASK) + | (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE); +#else + all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE); #endif - /* process files specified, or current directory if none */ -#ifdef BB_FEATURE_LS_RECURSIVE - if (opts & DISP_RECURSIVE) { - i = 0; - if (argi == argc) { - i = recursiveAction(".", TRUE, FALSE, FALSE, NULL, dirAction, NULL); + /* + * 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 */ } - while (argi < argc) { - i |= recursiveAction(argv[argi++], TRUE, FALSE, FALSE, NULL, dirAction, NULL); + } + + /* now, everything is in the av array */ + if (ac > 1) + all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ + + /* stuff the command line file names into an dnode array */ + dn = NULL; + for (oi = 0; oi < ac; oi++) { + char *fullname = bb_xstrdup(av[oi]); + + cur = my_stat(fullname, fullname); + if (!cur) + continue; + cur->next = dn; + dn = cur; + nfiles++; + } + + /* now that we know how many files there are + ** allocate memory for an array to hold dnode pointers + */ + dnp = dnalloc(nfiles); + for (i = 0, cur = dn; i < nfiles; i++) { + dnp[i] = cur; /* save pointer to node in array */ + cur = cur->next; + } + + if (all_fmt & DISP_NOLIST) { +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(dnp, nfiles); +#endif + if (nfiles > 0) + showfiles(dnp, nfiles); + } else { + dnd = splitdnarray(dnp, nfiles, SPLIT_DIR); + dnf = splitdnarray(dnp, nfiles, SPLIT_FILE); + dndirs = countdirs(dnp, nfiles); + dnfiles = nfiles - dndirs; + if (dnfiles > 0) { +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(dnf, dnfiles); +#endif + showfiles(dnf, dnfiles); } - } else + if (dndirs > 0) { +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(dnd, dndirs); #endif - { - i = 0; - if (argi == argc) - i = list_item("."); - while (argi < argc) - i |= list_item(argv[argi++]); - newline(); + showdirs(dnd, dndirs, dnfiles == 0); + } } - exit(i); - - print_usage_message: - usage(ls_usage); - exit(FALSE); + return (status); }