Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/coreutils/ls.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 983 by niro, Fri Apr 24 18:33:46 2009 UTC revision 984 by niro, Sun May 30 11:32:42 2010 UTC
# Line 6  Line 6 
6   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7   */   */
8    
9  /*  /* [date unknown. Perhaps before year 2000]
10   * To achieve a small memory footprint, this version of 'ls' doesn't do any   * To achieve a small memory footprint, this version of 'ls' doesn't do any
11   * file sorting, and only has the most essential command line switches   * file sorting, and only has the most essential command line switches
12   * (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
# Line 17  Line 17 
17   * it more portable.   * it more portable.
18   *   *
19   * KNOWN BUGS:   * KNOWN BUGS:
20   * 1. ls -l of a directory doesn't give "total <blocks>" header   * 1. hidden files can make column width too large
  * 2. ls of a symlink to a directory doesn't list directory contents  
  * 3. hidden files can make column width too large  
21   *   *
22   * NON-OPTIMAL BEHAVIOUR:   * NON-OPTIMAL BEHAVIOUR:
23   * 1. autowidth reads directories twice   * 1. autowidth reads directories twice
# Line 27  Line 25 
25   *    appended, there's no need to stat each one   *    appended, there's no need to stat each one
26   * PORTABILITY:   * PORTABILITY:
27   * 1. requires lstat (BSD) - how do you do it without?   * 1. requires lstat (BSD) - how do you do it without?
28     *
29     * [2009-03]
30     * ls sorts listing now, and supports almost all options.
31   */   */
   
32  #include "libbb.h"  #include "libbb.h"
33    #include "unicode.h"
34    
 #if ENABLE_FEATURE_ASSUME_UNICODE  
 #include <wchar.h>  
 #endif  
35    
36  /* This is a NOEXEC applet. Be very careful! */  /* This is a NOEXEC applet. Be very careful! */
37    
38    
39    #if ENABLE_FTPD
40    /* ftpd uses ls, and without timestamps Mozilla won't understand
41     * ftpd's LIST output.
42     */
43    # undef CONFIG_FEATURE_LS_TIMESTAMPS
44    # undef ENABLE_FEATURE_LS_TIMESTAMPS
45    # undef IF_FEATURE_LS_TIMESTAMPS
46    # undef IF_NOT_FEATURE_LS_TIMESTAMPS
47    # define CONFIG_FEATURE_LS_TIMESTAMPS 1
48    # define ENABLE_FEATURE_LS_TIMESTAMPS 1
49    # define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
50    # define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
51    #endif
52    
53    
54  enum {  enum {
55    
56  TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */  TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
# Line 59  LIST_ID_NAME    = 1 << 4, Line 72  LIST_ID_NAME    = 1 << 4,
72  LIST_ID_NUMERIC = 1 << 5,  LIST_ID_NUMERIC = 1 << 5,
73  LIST_CONTEXT    = 1 << 6,  LIST_CONTEXT    = 1 << 6,
74  LIST_SIZE       = 1 << 7,  LIST_SIZE       = 1 << 7,
75  LIST_DEV        = 1 << 8,  //LIST_DEV        = 1 << 8, - unused, synonym to LIST_SIZE
76  LIST_DATE_TIME  = 1 << 9,  LIST_DATE_TIME  = 1 << 9,
77  LIST_FULLTIME   = 1 << 10,  LIST_FULLTIME   = 1 << 10,
78  LIST_FILENAME   = 1 << 11,  LIST_FILENAME   = 1 << 11,
# Line 110  SPLIT_SUBDIR    = 2, Line 123  SPLIT_SUBDIR    = 2,
123    
124  };  };
125    
126  #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)  /* "[-]Cadil1", POSIX mandated options, busybox always supports */
127  #define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])  /* "[-]gnsx", POSIX non-mandated options, busybox always supports */
128  #define APPCHAR(mode)   ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])  /* "[-]Q" GNU option? busybox always supports */
129  #define COLOR(mode) ("\000\043\043\043\042\000\043\043"\  /* "[-]Ak" GNU options, busybox always supports */
130   "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])  /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
131  #define ATTR(mode) ("\00\00\01\00\01\00\01\00"\  /* "[-]p", POSIX non-mandated options, busybox optionally supports */
132   "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])  /* "[-]SXvThw", GNU options, busybox optionally supports */
133    /* "[-]K", SELinux mandated options, busybox optionally supports */
134    /* "[-]e", I think we made this one up */
135    static const char ls_options[] ALIGN1 =
136     "Cadil1gnsxQAk" /* 13 opts, total 13 */
137     IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
138     IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
139     IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
140     IF_FEATURE_LS_FOLLOWLINKS("L")   /* 1, 24 */
141     IF_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
142     IF_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
143     IF_SELINUX("KZ") /* 2, 28 */
144     IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
145     ;
146    enum {
147     //OPT_C = (1 << 0),
148     //OPT_a = (1 << 1),
149     //OPT_d = (1 << 2),
150     //OPT_i = (1 << 3),
151     //OPT_l = (1 << 4),
152     //OPT_1 = (1 << 5),
153     OPT_g = (1 << 6),
154     //OPT_n = (1 << 7),
155     //OPT_s = (1 << 8),
156     //OPT_x = (1 << 9),
157     OPT_Q = (1 << 10),
158     //OPT_A = (1 << 11),
159     //OPT_k = (1 << 12),
160     OPTBIT_color = 13
161     + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
162     + 4 * ENABLE_FEATURE_LS_SORTFILES
163     + 2 * ENABLE_FEATURE_LS_FILETYPES
164     + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
165     + 1 * ENABLE_FEATURE_LS_RECURSIVE
166     + 1 * ENABLE_FEATURE_HUMAN_READABLE
167     + 2 * ENABLE_SELINUX
168     + 2 * ENABLE_FEATURE_AUTOWIDTH,
169     OPT_color = 1 << OPTBIT_color,
170    };
171    
172    enum {
173     LIST_MASK_TRIGGER = 0,
174     STYLE_MASK_TRIGGER = STYLE_MASK,
175     DISP_MASK_TRIGGER = DISP_ROWS,
176     SORT_MASK_TRIGGER = SORT_MASK,
177    };
178    
179    /* TODO: simple toggles may be stored as OPT_xxx bits instead */
180    static const unsigned opt_flags[] = {
181     LIST_SHORT | STYLE_COLUMNS, /* C */
182     DISP_HIDDEN | DISP_DOT,     /* a */
183     DISP_NOLIST,                /* d */
184     LIST_INO,                   /* i */
185     LIST_LONG | STYLE_LONG,     /* l - remember LS_DISP_HR in mask! */
186     LIST_SHORT | STYLE_SINGLE,  /* 1 */
187     0,                          /* g (don't show group) - handled via OPT_g */
188     LIST_ID_NUMERIC,            /* n */
189     LIST_BLOCKS,                /* s */
190     DISP_ROWS,                  /* x */
191     0,                          /* Q (quote filename) - handled via OPT_Q */
192     DISP_HIDDEN,                /* A */
193     ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
194    #if ENABLE_FEATURE_LS_TIMESTAMPS
195     TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),   /* c */
196     LIST_FULLTIME,              /* e */
197     ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,   /* t */
198     TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),   /* u */
199    #endif
200    #if ENABLE_FEATURE_LS_SORTFILES
201     SORT_SIZE,                  /* S */
202     SORT_EXT,                   /* X */
203     SORT_REVERSE,               /* r */
204     SORT_VERSION,               /* v */
205    #endif
206    #if ENABLE_FEATURE_LS_FILETYPES
207     LIST_FILETYPE | LIST_EXEC,  /* F */
208     LIST_FILETYPE,              /* p */
209    #endif
210    #if ENABLE_FEATURE_LS_FOLLOWLINKS
211     FOLLOW_LINKS,               /* L */
212    #endif
213    #if ENABLE_FEATURE_LS_RECURSIVE
214     DISP_RECURSIVE,             /* R */
215    #endif
216    #if ENABLE_FEATURE_HUMAN_READABLE
217     LS_DISP_HR,                 /* h */
218    #endif
219    #if ENABLE_SELINUX
220     LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
221    #endif
222    #if ENABLE_SELINUX
223     LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
224    #endif
225     (1U<<31)
226     /* options after Z are not processed through opt_flags:
227     * T, w - ignored
228     */
229    };
230    
231    
232  /*  /*
233   * a directory entry and its stat info are stored here   * a directory entry and its stat info are stored here
234   */   */
235  struct dnode {                  /* the basic node */  struct dnode {
236   const char *name;             /* the dir entry name */   const char *name;       /* the dir entry name */
237   const char *fullname;         /* 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;)  
238   struct dnode *next;     /* point at the next node */   struct dnode *next;     /* point at the next node */
239     smallint fname_allocated;
240     struct stat dstat;      /* the file stat info */
241     IF_SELINUX(security_context_t sid;)
242  };  };
243    
244  static struct dnode **list_dir(const char *);  static struct dnode **list_dir(const char *, unsigned *);
245  static struct dnode **dnalloc(int);  static unsigned list_single(const struct dnode *);
 static int list_single(const struct dnode *);  
   
246    
247  struct globals {  struct globals {
248  #if ENABLE_FEATURE_LS_COLOR  #if ENABLE_FEATURE_LS_COLOR
# Line 152  struct globals { Line 261  struct globals {
261  };  };
262  #define G (*(struct globals*)&bb_common_bufsiz1)  #define G (*(struct globals*)&bb_common_bufsiz1)
263  #if ENABLE_FEATURE_LS_COLOR  #if ENABLE_FEATURE_LS_COLOR
264  #define show_color     (G.show_color    )  # define show_color     (G.show_color    )
265  #else  #else
266  enum { show_color = 0 };  enum { show_color = 0 };
267  #endif  #endif
268  #define exit_code      (G.exit_code     )  #define exit_code       (G.exit_code     )
269  #define all_fmt        (G.all_fmt       )  #define all_fmt         (G.all_fmt       )
270  #if ENABLE_FEATURE_AUTOWIDTH  #if ENABLE_FEATURE_AUTOWIDTH
271  #define tabstops       (G.tabstops      )  # define tabstops       (G.tabstops      )
272  #define terminal_width (G.terminal_width)  # define terminal_width (G.terminal_width)
273  #else  #else
274  enum {  enum {
275   tabstops = COLUMN_GAP,   tabstops = COLUMN_GAP,
# Line 168  enum { Line 277  enum {
277  };  };
278  #endif  #endif
279  #define current_time_t (G.current_time_t)  #define current_time_t (G.current_time_t)
 /* memset: we have to zero it out because of NOEXEC */  
280  #define INIT_G() do { \  #define INIT_G() do { \
281     /* we have to zero it out because of NOEXEC */ \
282   memset(&G, 0, sizeof(G)); \   memset(&G, 0, sizeof(G)); \
283   USE_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \   IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
284   USE_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \   IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
285   USE_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \   IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
286  } while (0)  } while (0)
287    
288    
 #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  
   
   
289  static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)  static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
290  {  {
291   struct stat dstat;   struct stat dstat;
292   struct dnode *cur;   struct dnode *cur;
293   USE_SELINUX(security_context_t sid = NULL;)   IF_SELINUX(security_context_t sid = NULL;)
294    
295   if ((all_fmt & FOLLOW_LINKS) || force_follow) {   if ((all_fmt & FOLLOW_LINKS) || force_follow) {
296  #if ENABLE_SELINUX  #if ENABLE_SELINUX
# Line 222  static struct dnode *my_stat(const char Line 316  static struct dnode *my_stat(const char
316   }   }
317   }   }
318    
319   cur = xmalloc(sizeof(struct dnode));   cur = xmalloc(sizeof(*cur));
320   cur->fullname = fullname;   cur->fullname = fullname;
321   cur->name = name;   cur->name = name;
322   cur->dstat = dstat;   cur->dstat = dstat;
323   USE_SELINUX(cur->sid = sid;)   IF_SELINUX(cur->sid = sid;)
324   return cur;   return cur;
325  }  }
326    
327    /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
328     * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
329     *  3/7:multiplexed char/block device)
330     * and we use 0 for unknown and 15 for executables (see below) */
331    #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
332    #define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
333    #define APPCHAR(mode)   ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
334    /* 036 black foreground              050 black background
335       037 red foreground                051 red background
336       040 green foreground              052 green background
337       041 brown foreground              053 brown background
338       042 blue foreground               054 blue background
339       043 magenta (purple) foreground   055 magenta background
340       044 cyan (light blue) foreground  056 cyan background
341       045 gray foreground               057 white background
342    */
343    #define COLOR(mode) ( \
344     /*un  fi  chr     dir     blk     file    link    sock        exe */ \
345     "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
346     [TYPEINDEX(mode)])
347    /* Select normal (0) [actually "reset all"] or bold (1)
348     * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
349     *  let's use 7 for "impossible" types, just for fun)
350     * Note: coreutils 6.9 uses inverted red for setuid binaries.
351     */
352    #define ATTR(mode) ( \
353     /*un fi chr   dir   blk   file  link  sock     exe */ \
354     "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
355     [TYPEINDEX(mode)])
356    
357  #if ENABLE_FEATURE_LS_COLOR  #if ENABLE_FEATURE_LS_COLOR
358    /* mode of zero is interpreted as "unknown" (stat failed) */
359  static char fgcolor(mode_t mode)  static char fgcolor(mode_t mode)
360  {  {
  /* Check wheter the file is existing (if so, color it red!) */  
  if (errno == ENOENT)  
  return '\037';  
361   if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))   if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
362   return COLOR(0xF000); /* File is executable ... */   return COLOR(0xF000); /* File is executable ... */
363   return COLOR(mode);   return COLOR(mode);
364  }  }
365    static char bold(mode_t mode)
 static char bgcolor(mode_t mode)  
366  {  {
367   if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))   if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
368   return ATTR(0xF000); /* File is executable ... */   return ATTR(0xF000); /* File is executable ... */
# Line 264  static char append_char(mode_t mode) Line 385  static char append_char(mode_t mode)
385  }  }
386  #endif  #endif
387    
388  #define countdirs(A, B) count_dirs((A), (B), 1)  static unsigned count_dirs(struct dnode **dn, int which)
 #define countsubdirs(A, B) count_dirs((A), (B), 0)  
 static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)  
389  {  {
390   int i, dirs;   unsigned dirs, all;
391    
392   if (!dn)   if (!dn)
393   return 0;   return 0;
394   dirs = 0;  
395   for (i = 0; i < nfiles; i++) {   dirs = all = 0;
396     for (; *dn; dn++) {
397   const char *name;   const char *name;
398   if (!S_ISDIR(dn[i]->dstat.st_mode))  
399     all++;
400     if (!S_ISDIR((*dn)->dstat.st_mode))
401   continue;   continue;
402   name = dn[i]->name;   name = (*dn)->name;
403   if (notsubdirs   if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
404   || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))   /* or if it's not . or .. */
405     || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
406   ) {   ) {
407   dirs++;   dirs++;
408   }   }
409   }   }
410   return dirs;   return which != SPLIT_FILE ? dirs : all - 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; cur = cur->next)  
  nfiles++;  
  nfiles++;  
  return nfiles;  
411  }  }
412    
413  /* get memory to hold an array of pointers */  /* get memory to hold an array of pointers */
414  static struct dnode **dnalloc(int num)  static struct dnode **dnalloc(unsigned num)
415  {  {
416   if (num < 1)   if (num < 1)
417   return NULL;   return NULL;
418    
419     num++; /* so that we have terminating NULL */
420   return xzalloc(num * sizeof(struct dnode *));   return xzalloc(num * sizeof(struct dnode *));
421  }  }
422    
423  #if ENABLE_FEATURE_LS_RECURSIVE  #if ENABLE_FEATURE_LS_RECURSIVE
424  static void dfree(struct dnode **dnp, int nfiles)  static void dfree(struct dnode **dnp)
425  {  {
426   int i;   unsigned i;
427    
428   if (dnp == NULL)   if (dnp == NULL)
429   return;   return;
430    
431   for (i = 0; i < nfiles; i++) {   for (i = 0; dnp[i]; i++) {
432   struct dnode *cur = dnp[i];   struct dnode *cur = dnp[i];
433   if (cur->allocated)   if (cur->fname_allocated)
434   free((char*)cur->fullname); /* free the filename */   free((char*)cur->fullname);
435   free(cur); /* free the dnode */   free(cur);
436   }   }
437   free(dnp); /* free the array holding the dnode pointers */   free(dnp);
438  }  }
439  #else  #else
440  #define dfree(...) ((void)0)  #define dfree(...) ((void)0)
441  #endif  #endif
442    
443  static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)  /* Returns NULL-terminated malloced vector of pointers (or NULL) */
444    static struct dnode **splitdnarray(struct dnode **dn, int which)
445  {  {
446   int dncnt, i, d;   unsigned dncnt, d;
447   struct dnode **dnp;   struct dnode **dnp;
448    
449   if (dn == NULL || nfiles < 1)   if (dn == NULL)
450   return NULL;   return NULL;
451    
452   /* count how many dirs and regular files there are */   /* count how many dirs or files there are */
453   if (which == SPLIT_SUBDIR)   dncnt = count_dirs(dn, which);
  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 */  
  }  
454    
455   /* allocate a file array and a dir array */   /* allocate a file array and a dir array */
456   dnp = dnalloc(dncnt);   dnp = dnalloc(dncnt);
457    
458   /* copy the entrys into the file or dir array */   /* copy the entrys into the file or dir array */
459   for (d = i = 0; i < nfiles; i++) {   for (d = 0; *dn; dn++) {
460   if (S_ISDIR(dn[i]->dstat.st_mode)) {   if (S_ISDIR((*dn)->dstat.st_mode)) {
461   const char *name;   const char *name;
462    
463   if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))   if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
464   continue;   continue;
465   name = dn[i]->name;   name = (*dn)->name;
466   if ((which & SPLIT_DIR)   if ((which & SPLIT_DIR)
467   || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))   || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
468   ) {   ) {
469   dnp[d++] = dn[i];   dnp[d++] = *dn;
470   }   }
471   } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {   } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
472   dnp[d++] = dn[i];   dnp[d++] = *dn;
473   }   }
474   }   }
475   return dnp;   return dnp;
# Line 375  static int sortcmp(const void *a, const Line 481  static int sortcmp(const void *a, const
481   struct dnode *d1 = *(struct dnode **)a;   struct dnode *d1 = *(struct dnode **)a;
482   struct dnode *d2 = *(struct dnode **)b;   struct dnode *d2 = *(struct dnode **)b;
483   unsigned sort_opts = all_fmt & SORT_MASK;   unsigned sort_opts = all_fmt & SORT_MASK;
484   int dif;   off_t dif;
485    
486   dif = 0; /* assume SORT_NAME */   dif = 0; /* assume SORT_NAME */
487   // TODO: use pre-initialized function pointer   // TODO: use pre-initialized function pointer
488   // instead of branch forest   // instead of branch forest
489   if (sort_opts == SORT_SIZE) {   if (sort_opts == SORT_SIZE) {
490   dif = (int) (d2->dstat.st_size - d1->dstat.st_size);   dif = (d2->dstat.st_size - d1->dstat.st_size);
491   } else if (sort_opts == SORT_ATIME) {   } else if (sort_opts == SORT_ATIME) {
492   dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);   dif = (d2->dstat.st_atime - d1->dstat.st_atime);
493   } else if (sort_opts == SORT_CTIME) {   } else if (sort_opts == SORT_CTIME) {
494   dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);   dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
495   } else if (sort_opts == SORT_MTIME) {   } else if (sort_opts == SORT_MTIME) {
496   dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);   dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
497   } else if (sort_opts == SORT_DIR) {   } else if (sort_opts == SORT_DIR) {
498   dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);   dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
499   /* } else if (sort_opts == SORT_VERSION) { */   /* } else if (sort_opts == SORT_VERSION) { */
500   /* } else if (sort_opts == SORT_EXT) { */   /* } else if (sort_opts == SORT_EXT) { */
501   }   }
   
502   if (dif == 0) {   if (dif == 0) {
503   /* sort by name - may be a tie_breaker for time or size cmp */   /* sort by name, or tie_breaker for other sorts */
504   if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);   if (ENABLE_LOCALE_SUPPORT)
505   else dif = strcmp(d1->name, d2->name);   dif = strcoll(d1->name, d2->name);
506     else
507     dif = strcmp(d1->name, d2->name);
508   }   }
509    
510   if (all_fmt & SORT_REVERSE) {   /* Make dif fit into an int */
511   dif = -dif;   if (sizeof(dif) > sizeof(int)) {
512     enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
513     /* shift leaving only "int" worth of bits */
514     if (dif != 0) {
515     dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
516     }
517   }   }
518   return dif;  
519     return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
520  }  }
521    
522  static void dnsort(struct dnode **dn, int size)  static void dnsort(struct dnode **dn, int size)
# Line 415  static void dnsort(struct dnode **dn, in Line 528  static void dnsort(struct dnode **dn, in
528  #endif  #endif
529    
530    
531  static void showfiles(struct dnode **dn, int nfiles)  static void showfiles(struct dnode **dn, unsigned nfiles)
532  {  {
533   int i, ncols, nrows, row, nc;   unsigned i, ncols, nrows, row, nc;
534   int column = 0;   unsigned column = 0;
535   int nexttab = 0;   unsigned nexttab = 0;
536   int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */   unsigned column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
537    
538     /* Never happens:
539   if (dn == NULL || nfiles < 1)   if (dn == NULL || nfiles < 1)
540   return;   return;
541     */
542    
543   if (all_fmt & STYLE_LONG) {   if (all_fmt & STYLE_LONG) {
544   ncols = 1;   ncols = 1;
545   } else {   } else {
546   /* find the longest file name, use that as the column width */   /* find the longest file name, use that as the column width */
547   for (i = 0; i < nfiles; i++) {   for (i = 0; dn[i]; i++) {
548   int len = mbstrlen(dn[i]->name);   int len = unicode_strlen(dn[i]->name);
549   if (column_width < len)   if (column_width < len)
550   column_width = len;   column_width = len;
551   }   }
552   column_width += tabstops +   column_width += tabstops +
553   USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )   IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
554               ((all_fmt & LIST_INO) ? 8 : 0) +               ((all_fmt & LIST_INO) ? 8 : 0) +
555               ((all_fmt & LIST_BLOCKS) ? 5 : 0);               ((all_fmt & LIST_BLOCKS) ? 5 : 0);
556   ncols = (int) (terminal_width / column_width);   ncols = (int) (terminal_width / column_width);
# Line 453  static void showfiles(struct dnode **dn, Line 568  static void showfiles(struct dnode **dn,
568   for (row = 0; row < nrows; row++) {   for (row = 0; row < nrows; row++) {
569   for (nc = 0; nc < ncols; nc++) {   for (nc = 0; nc < ncols; nc++) {
570   /* reach into the array based on the column and row */   /* reach into the array based on the column and row */
  i = (nc * nrows) + row; /* assume display by column */  
571   if (all_fmt & DISP_ROWS)   if (all_fmt & DISP_ROWS)
572   i = (row * ncols) + nc; /* display across row */   i = (row * ncols) + nc; /* display across row */
573     else
574     i = (nc * nrows) + row; /* display by column */
575   if (i < nfiles) {   if (i < nfiles) {
576   if (column > 0) {   if (column > 0) {
577   nexttab -= column;   nexttab -= column;
# Line 472  static void showfiles(struct dnode **dn, Line 588  static void showfiles(struct dnode **dn,
588  }  }
589    
590    
591  static void showdirs(struct dnode **dn, int ndirs, int first)  #if ENABLE_DESKTOP
592    /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
593     * If any of the -l, -n, -s options is specified, each list
594     * of files within the directory shall be preceded by a
595     * status line indicating the number of file system blocks
596     * occupied by files in the directory in 512-byte units if
597     * the -k option is not specified, or 1024-byte units if the
598     * -k option is specified, rounded up to the next integral
599     * number of units.
600     */
601    /* by Jorgen Overgaard (jorgen AT antistaten.se) */
602    static off_t calculate_blocks(struct dnode **dn)
603    {
604     uoff_t blocks = 1;
605     if (dn) {
606     while (*dn) {
607     /* st_blocks is in 512 byte blocks */
608     blocks += (*dn)->dstat.st_blocks;
609     dn++;
610     }
611     }
612    
613     /* Even though standard says use 512 byte blocks, coreutils use 1k */
614     /* Actually, we round up by calculating (blocks + 1) / 2,
615     * "+ 1" was done when we initialized blocks to 1 */
616     return blocks >> 1;
617    }
618    #endif
619    
620    
621    static void showdirs(struct dnode **dn, int first)
622  {  {
623   int i, nfiles;   unsigned nfiles;
624     unsigned dndirs;
625   struct dnode **subdnp;   struct dnode **subdnp;
  int dndirs;  
626   struct dnode **dnd;   struct dnode **dnd;
627    
628   if (dn == NULL || ndirs < 1)   /* Never happens:
629     if (dn == NULL || ndirs < 1) {
630   return;   return;
631     }
632     */
633    
634   for (i = 0; i < ndirs; i++) {   for (; *dn; dn++) {
635   if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {   if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
636   if (!first)   if (!first)
637   bb_putchar('\n');   bb_putchar('\n');
638   first = 0;   first = 0;
639   printf("%s:\n", dn[i]->fullname);   printf("%s:\n", (*dn)->fullname);
640   }   }
641   subdnp = list_dir(dn[i]->fullname);   subdnp = list_dir((*dn)->fullname, &nfiles);
642   nfiles = countfiles(subdnp);  #if ENABLE_DESKTOP
643     if ((all_fmt & STYLE_MASK) == STYLE_LONG)
644     printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
645    #endif
646   if (nfiles > 0) {   if (nfiles > 0) {
647   /* list all files at this level */   /* list all files at this level */
648   dnsort(subdnp, nfiles);   dnsort(subdnp, nfiles);
649   showfiles(subdnp, nfiles);   showfiles(subdnp, nfiles);
650   if (ENABLE_FEATURE_LS_RECURSIVE) {   if (ENABLE_FEATURE_LS_RECURSIVE
651   if (all_fmt & DISP_RECURSIVE) {   && (all_fmt & DISP_RECURSIVE)
652   /* recursive- list the sub-dirs */   ) {
653   dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);   /* recursive - list the sub-dirs */
654   dndirs = countsubdirs(subdnp, nfiles);   dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
655   if (dndirs > 0) {   dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
656   dnsort(dnd, dndirs);   if (dndirs > 0) {
657   showdirs(dnd, dndirs, 0);   dnsort(dnd, dndirs);
658   /* free the array of dnode pointers to the dirs */   showdirs(dnd, 0);
659   free(dnd);   /* free the array of dnode pointers to the dirs */
660   }   free(dnd);
661   }   }
  /* free the dnodes and the fullname mem */  
  dfree(subdnp, nfiles);  
662   }   }
663     /* free the dnodes and the fullname mem */
664     dfree(subdnp);
665   }   }
666   }   }
667  }  }
668    
669    
670  static struct dnode **list_dir(const char *path)  /* Returns NULL-terminated malloced vector of pointers (or NULL) */
671    static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
672  {  {
673   struct dnode *dn, *cur, **dnp;   struct dnode *dn, *cur, **dnp;
674   struct dirent *entry;   struct dirent *entry;
675   DIR *dir;   DIR *dir;
676   int i, nfiles;   unsigned i, nfiles;
677    
678     /* Never happens:
679   if (path == NULL)   if (path == NULL)
680   return NULL;   return NULL;
681     */
682    
683   dn = NULL;   *nfiles_p = 0;
  nfiles = 0;  
684   dir = warn_opendir(path);   dir = warn_opendir(path);
685   if (dir == NULL) {   if (dir == NULL) {
686   exit_code = EXIT_FAILURE;   exit_code = EXIT_FAILURE;
687   return NULL; /* could not open the dir */   return NULL; /* could not open the dir */
688   }   }
689     dn = NULL;
690     nfiles = 0;
691   while ((entry = readdir(dir)) != NULL) {   while ((entry = readdir(dir)) != NULL) {
692   char *fullname;   char *fullname;
693    
# Line 551  static struct dnode **list_dir(const cha Line 707  static struct dnode **list_dir(const cha
707   free(fullname);   free(fullname);
708   continue;   continue;
709   }   }
710   cur->allocated = 1;   cur->fname_allocated = 1;
711   cur->next = dn;   cur->next = dn;
712   dn = cur;   dn = cur;
713   nfiles++;   nfiles++;
714   }   }
715   closedir(dir);   closedir(dir);
716    
717     if (dn == NULL)
718     return NULL;
719    
720   /* now that we know how many files there are   /* now that we know how many files there are
721   * allocate memory for an array to hold dnode pointers   * allocate memory for an array to hold dnode pointers
722   */   */
723   if (dn == NULL)   *nfiles_p = nfiles;
  return NULL;  
724   dnp = dnalloc(nfiles);   dnp = dnalloc(nfiles);
725   for (i = 0, cur = dn; i < nfiles; i++) {   for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
726   dnp[i] = cur; /* save pointer to node in array */   dnp[i] = dn; /* save pointer to node in array */
727   cur = cur->next;   dn = dn->next;
728     if (!dn)
729     break;
730   }   }
731    
732   return dnp;   return dnp;
733  }  }
734    
735    
736  static int list_single(const struct dnode *dn)  static int print_name(const char *name)
737  {  {
738   int i, column = 0;   if (option_mask32 & OPT_Q) {
739    #if ENABLE_FEATURE_ASSUME_UNICODE
740  #if ENABLE_FEATURE_LS_TIMESTAMPS   unsigned len = 2 + unicode_strlen(name);
741   char *filetime;  #else
742   time_t ttime, age;   unsigned len = 2;
743  #endif  #endif
744     putchar('"');
745     while (*name) {
746     if (*name == '"') {
747     putchar('\\');
748     len++;
749     }
750     putchar(*name++);
751     if (!ENABLE_FEATURE_ASSUME_UNICODE)
752     len++;
753     }
754     putchar('"');
755     return len;
756     }
757     /* No -Q: */
758    #if ENABLE_FEATURE_ASSUME_UNICODE
759     fputs(name, stdout);
760     return unicode_strlen(name);
761    #else
762     return printf("%s", name);
763    #endif
764    }
765    
766    
767    static NOINLINE unsigned list_single(const struct dnode *dn)
768    {
769     unsigned column = 0;
770     char *lpath = lpath; /* for compiler */
771  #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR  #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
772   struct stat info;   struct stat info;
773   char append;   char append;
774  #endif  #endif
775    
776     /* Never happens:
777   if (dn->fullname == NULL)   if (dn->fullname == NULL)
778   return 0;   return 0;
779     */
780    
 #if ENABLE_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  
781  #if ENABLE_FEATURE_LS_FILETYPES  #if ENABLE_FEATURE_LS_FILETYPES
782   append = append_char(dn->dstat.st_mode);   append = append_char(dn->dstat.st_mode);
783  #endif  #endif
784    
785   for (i = 0; i <= 31; i++) {   /* Do readlink early, so that if it fails, error message
786   switch (all_fmt & (1 << i)) {   * does not appear *inside* the "ls -l" line */
787   case LIST_INO:   if (all_fmt & LIST_SYMLINK)
788   column += printf("%7ld ", (long) dn->dstat.st_ino);   if (S_ISLNK(dn->dstat.st_mode))
789   break;   lpath = xmalloc_readlink_or_warn(dn->fullname);
790   case LIST_BLOCKS:  
791   column += printf("%4"OFF_FMT"d ", (off_t) dn->dstat.st_blocks >> 1);   if (all_fmt & LIST_INO)
792   break;   column += printf("%7llu ", (long long) dn->dstat.st_ino);
793   case LIST_MODEBITS:   if (all_fmt & LIST_BLOCKS)
794   column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));   column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
795   break;   if (all_fmt & LIST_MODEBITS)
796   case LIST_NLINKS:   column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
797   column += printf("%4ld ", (long) dn->dstat.st_nlink);   if (all_fmt & LIST_NLINKS)
798   break;   column += printf("%4lu ", (long) dn->dstat.st_nlink);
  case LIST_ID_NAME:  
799  #if ENABLE_FEATURE_LS_USERNAME  #if ENABLE_FEATURE_LS_USERNAME
800   printf("%-8.8s %-8.8s",   if (all_fmt & LIST_ID_NAME) {
801     if (option_mask32 & OPT_g) {
802     column += printf("%-8.8s ",
803     get_cached_username(dn->dstat.st_uid));
804     } else {
805     column += printf("%-8.8s %-8.8s ",
806   get_cached_username(dn->dstat.st_uid),   get_cached_username(dn->dstat.st_uid),
807   get_cached_groupname(dn->dstat.st_gid));   get_cached_groupname(dn->dstat.st_gid));
808   column += 17;   }
809   break;   }
810  #endif  #endif
811   case LIST_ID_NUMERIC:   if (all_fmt & LIST_ID_NUMERIC) {
812   column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);   if (option_mask32 & OPT_g)
813   break;   column += printf("%-8u ", (int) dn->dstat.st_uid);
814   case LIST_SIZE:   else
815   case LIST_DEV:   column += printf("%-8u %-8u ",
816   if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {   (int) dn->dstat.st_uid,
817   column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev),   (int) dn->dstat.st_gid);
818     (int) minor(dn->dstat.st_rdev));   }
819     if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
820     if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
821     column += printf("%4u, %3u ",
822     (int) major(dn->dstat.st_rdev),
823     (int) minor(dn->dstat.st_rdev));
824     } else {
825     if (all_fmt & LS_DISP_HR) {
826     column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
827     /* print st_size, show one fractional, use suffixes */
828     make_human_readable_str(dn->dstat.st_size, 1, 0)
829     );
830   } else {   } else {
831   if (all_fmt & LS_DISP_HR) {   column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
  column += printf("%9s ",  
  make_human_readable_str(dn->dstat.st_size, 1, 0));  
  } else {  
  column += printf("%9"OFF_FMT"d ", (off_t) dn->dstat.st_size);  
  }  
832   }   }
833   break;   }
834     }
835  #if ENABLE_FEATURE_LS_TIMESTAMPS  #if ENABLE_FEATURE_LS_TIMESTAMPS
836   case LIST_FULLTIME:   if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
837   printf("%24.24s ", filetime);   char *filetime;
838   column += 25;   time_t ttime = dn->dstat.st_mtime;
839   break;   if (all_fmt & TIME_ACCESS)
840   case LIST_DATE_TIME:   ttime = dn->dstat.st_atime;
841   if ((all_fmt & LIST_FULLTIME) == 0) {   if (all_fmt & TIME_CHANGE)
842   /* current_time_t ~== time(NULL) */   ttime = dn->dstat.st_ctime;
843   age = current_time_t - ttime;   filetime = ctime(&ttime);
844   printf("%6.6s ", filetime + 4);   /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
845   if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {   if (all_fmt & LIST_FULLTIME)
846   /* hh:mm if less than 6 months old */   column += printf("%.24s ", filetime);
847   printf("%5.5s ", filetime + 11);   else { /* LIST_DATE_TIME */
848   } else {   /* current_time_t ~== time(NULL) */
849   printf(" %4.4s ", filetime + 20);   time_t age = current_time_t - ttime;
850   }   printf("%.6s ", filetime + 4); /* "Jun 30" */
851   column += 13;   if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
852     /* hh:mm if less than 6 months old */
853     printf("%.5s ", filetime + 11);
854     } else { /* year. buggy if year > 9999 ;) */
855     printf(" %.4s ", filetime + 20);
856   }   }
857   break;   column += 13;
858     }
859     }
860  #endif  #endif
861  #if ENABLE_SELINUX  #if ENABLE_SELINUX
862   case LIST_CONTEXT:   if (all_fmt & LIST_CONTEXT) {
863   {   column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
864   char context[80];   freecon(dn->sid);
865   int len = 0;   }
   
  if (dn->sid) {  
  /* I assume sid initilized with NULL */  
  len = strlen(dn->sid) + 1;  
  safe_strncpy(context, dn->sid, len);  
  freecon(dn->sid);  
  } else {  
  safe_strncpy(context, "unknown", 8);  
  }  
  printf("%-32s ", context);  
  column += MAX(33, len);  
  }  
  break;  
866  #endif  #endif
867   case LIST_FILENAME:   if (all_fmt & LIST_FILENAME) {
  errno = 0;  
868  #if ENABLE_FEATURE_LS_COLOR  #if ENABLE_FEATURE_LS_COLOR
869   if (show_color && !lstat(dn->fullname, &info)) {   if (show_color) {
870   printf("\033[%d;%dm", bgcolor(info.st_mode),   info.st_mode = 0; /* for fgcolor() */
871   fgcolor(info.st_mode));   lstat(dn->fullname, &info);
872   }   printf("\033[%u;%um", bold(info.st_mode),
873     fgcolor(info.st_mode));
874     }
875  #endif  #endif
876  #if ENABLE_FEATURE_ASSUME_UNICODE   column += print_name(dn->name);
877   printf("%s", dn->name);   if (show_color) {
878   column += mbstrlen(dn->name);   printf("\033[0m");
879  #else   }
880   column += printf("%s", dn->name);   }
881     if (all_fmt & LIST_SYMLINK) {
882     if (S_ISLNK(dn->dstat.st_mode) && lpath) {
883     printf(" -> ");
884    #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
885    #if ENABLE_FEATURE_LS_COLOR
886     info.st_mode = 0; /* for fgcolor() */
887  #endif  #endif
888   if (show_color) {   if (stat(dn->fullname, &info) == 0) {
889   printf("\033[0m");   append = append_char(info.st_mode);
890   }   }
  break;  
  case LIST_SYMLINK:  
  if (S_ISLNK(dn->dstat.st_mode)) {  
  char *lpath = xmalloc_readlink_or_warn(dn->fullname);  
  if (!lpath) break;  
  printf(" -> ");  
 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR  
  if (!stat(dn->fullname, &info)) {  
  append = append_char(info.st_mode);  
  }  
891  #endif  #endif
892  #if ENABLE_FEATURE_LS_COLOR  #if ENABLE_FEATURE_LS_COLOR
893   if (show_color) {   if (show_color) {
894   errno = 0;   printf("\033[%u;%um", bold(info.st_mode),
895   printf("\033[%d;%dm", bgcolor(info.st_mode),     fgcolor(info.st_mode));
896     fgcolor(info.st_mode));   }
  }  
897  #endif  #endif
898   column += printf("%s", lpath) + 4;   column += print_name(lpath) + 4;
899   if (show_color) {   if (show_color) {
900   printf("\033[0m");   printf("\033[0m");
  }  
  free(lpath);  
901   }   }
902   break;   free(lpath);
903     }
904     }
905  #if ENABLE_FEATURE_LS_FILETYPES  #if ENABLE_FEATURE_LS_FILETYPES
906   case LIST_FILETYPE:   if (all_fmt & LIST_FILETYPE) {
907   if (append) {   if (append) {
908   putchar(append);   putchar(append);
909   column++;   column++;
  }  
  break;  
 #endif  
910   }   }
911   }   }
912    #endif
913    
914   return column;   return column;
915  }  }
916    
917    
 /* "[-]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 */  
 static const char ls_options[] ALIGN1 =  
  "Cadil1gnsxAk"  
  USE_FEATURE_LS_TIMESTAMPS("cetu")  
  USE_FEATURE_LS_SORTFILES("SXrv")  
  USE_FEATURE_LS_FILETYPES("Fp")  
  USE_FEATURE_LS_FOLLOWLINKS("L")  
  USE_FEATURE_LS_RECURSIVE("R")  
  USE_FEATURE_HUMAN_READABLE("h")  
  USE_SELINUX("K")  
  USE_FEATURE_AUTOWIDTH("T:w:")  
  USE_SELINUX("Z");  
   
 enum {  
  LIST_MASK_TRIGGER = 0,  
  STYLE_MASK_TRIGGER = STYLE_MASK,  
  DISP_MASK_TRIGGER = DISP_ROWS,  
  SORT_MASK_TRIGGER = SORT_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 */  
  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_FEATURE_AUTOWIDTH  
  0, 0,                       /* T, w - ignored */  
 #endif  
 #if ENABLE_SELINUX  
  LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */  
 #endif  
  (1U<<31)  
 };  
   
   
 /* 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) MAIN_EXTERNALLY_VISIBLE;  
918  int ls_main(int argc UNUSED_PARAM, char **argv)  int ls_main(int argc UNUSED_PARAM, char **argv)
919  {  {
920   struct dnode **dnd;   struct dnode **dnd;
# Line 833  int ls_main(int argc UNUSED_PARAM, char Line 923  int ls_main(int argc UNUSED_PARAM, char
923   struct dnode *dn;   struct dnode *dn;
924   struct dnode *cur;   struct dnode *cur;
925   unsigned opt;   unsigned opt;
926   int nfiles;   unsigned nfiles;
927   int dnfiles;   unsigned dnfiles;
928   int dndirs;   unsigned dndirs;
929   int i;   unsigned i;
930    #if ENABLE_FEATURE_LS_COLOR
931     /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
932     /* coreutils 6.10:
933     * # ls --color=BOGUS
934     * ls: invalid argument 'BOGUS' for '--color'
935     * Valid arguments are:
936     * 'always', 'yes', 'force'
937     * 'never', 'no', 'none'
938     * 'auto', 'tty', 'if-tty'
939     * (and substrings: "--color=alwa" work too)
940     */
941     static const char ls_longopts[] ALIGN1 =
942     "color\0" Optional_argument "\xff"; /* no short equivalent */
943     static const char color_str[] ALIGN1 =
944     "always\0""yes\0""force\0"
945     "auto\0""tty\0""if-tty\0";
946   /* need to initialize since --color has _an optional_ argument */   /* need to initialize since --color has _an optional_ argument */
947   USE_FEATURE_LS_COLOR(const char *color_opt = "always";)   const char *color_opt = color_str; /* "always" */
948    #endif
949    
950   INIT_G();   INIT_G();
951    
952     init_unicode();
953    
954   all_fmt = LIST_SHORT |   all_fmt = LIST_SHORT |
955   (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));   (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
956    
957  #if ENABLE_FEATURE_AUTOWIDTH  #if ENABLE_FEATURE_AUTOWIDTH
958   /* Obtain the terminal width */   /* obtain the terminal width */
959   get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);   get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
960   /* Go one less... */   /* go one less... */
961   terminal_width--;   terminal_width--;
962  #endif  #endif
963    
964   /* process options */   /* process options */
965   USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)   IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
966  #if ENABLE_FEATURE_AUTOWIDTH  #if ENABLE_FEATURE_AUTOWIDTH
967   opt_complementary = "T+:w+"; /* -T N, -w N */   opt_complementary = "T+:w+"; /* -T N, -w N */
968   opt = getopt32(argv, ls_options, &tabstops, &terminal_width   opt = getopt32(argv, ls_options, &tabstops, &terminal_width
969   USE_FEATURE_LS_COLOR(, &color_opt));   IF_FEATURE_LS_COLOR(, &color_opt));
970  #else  #else
971   opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));   opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
972  #endif  #endif
973   for (i = 0; opt_flags[i] != (1U<<31); i++) {   for (i = 0; opt_flags[i] != (1U<<31); i++) {
974   if (opt & (1 << i)) {   if (opt & (1 << i)) {
# Line 892  int ls_main(int argc UNUSED_PARAM, char Line 1001  int ls_main(int argc UNUSED_PARAM, char
1001   if (!p || (p[0] && strcmp(p, "none") != 0))   if (!p || (p[0] && strcmp(p, "none") != 0))
1002   show_color = 1;   show_color = 1;
1003   }   }
1004   if (opt & (1 << i)) {  /* next flag after short options */   if (opt & OPT_color) {
1005   if (strcmp("always", color_opt) == 0)   if (color_opt[0] == 'n')
  show_color = 1;  
  else if (strcmp("never", color_opt) == 0)  
1006   show_color = 0;   show_color = 0;
1007   else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))   else switch (index_in_substrings(color_str, color_opt)) {
1008   show_color = 1;   case 3:
1009     case 4:
1010     case 5:
1011     if (isatty(STDOUT_FILENO)) {
1012     case 0:
1013     case 1:
1014     case 2:
1015     show_color = 1;
1016     }
1017     }
1018   }   }
1019  #endif  #endif
1020    
# Line 932  int ls_main(int argc UNUSED_PARAM, char Line 1048  int ls_main(int argc UNUSED_PARAM, char
1048   dn = NULL;   dn = NULL;
1049   nfiles = 0;   nfiles = 0;
1050   do {   do {
1051   /* ls w/o -l follows links on command line */   /* NB: follow links on command line unless -l or -s */
1052   cur = my_stat(*argv, *argv, !(all_fmt & STYLE_LONG));   cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
1053   argv++;   argv++;
1054   if (!cur)   if (!cur)
1055   continue;   continue;
1056   cur->allocated = 0;   cur->fname_allocated = 0;
1057   cur->next = dn;   cur->next = dn;
1058   dn = cur;   dn = cur;
1059   nfiles++;   nfiles++;
1060   } while (*argv);   } while (*argv);
1061    
1062     /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1063     if (nfiles == 0)
1064     return exit_code;
1065    
1066   /* now that we know how many files there are   /* now that we know how many files there are
1067   * allocate memory for an array to hold dnode pointers   * allocate memory for an array to hold dnode pointers
1068   */   */
1069   dnp = dnalloc(nfiles);   dnp = dnalloc(nfiles);
1070   for (i = 0, cur = dn; i < nfiles; i++) {   for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1071   dnp[i] = cur; /* save pointer to node in array */   dnp[i] = dn; /* save pointer to node in array */
1072   cur = cur->next;   dn = dn->next;
1073     if (!dn)
1074     break;
1075   }   }
1076    
1077   if (all_fmt & DISP_NOLIST) {   if (all_fmt & DISP_NOLIST) {
1078   dnsort(dnp, nfiles);   dnsort(dnp, nfiles);
1079   if (nfiles > 0)   showfiles(dnp, nfiles);
  showfiles(dnp, nfiles);  
1080   } else {   } else {
1081   dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);   dnd = splitdnarray(dnp, SPLIT_DIR);
1082   dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);   dnf = splitdnarray(dnp, SPLIT_FILE);
1083   dndirs = countdirs(dnp, nfiles);   dndirs = count_dirs(dnp, SPLIT_DIR);
1084   dnfiles = nfiles - dndirs;   dnfiles = nfiles - dndirs;
1085   if (dnfiles > 0) {   if (dnfiles > 0) {
1086   dnsort(dnf, dnfiles);   dnsort(dnf, dnfiles);
# Line 969  int ls_main(int argc UNUSED_PARAM, char Line 1090  int ls_main(int argc UNUSED_PARAM, char
1090   }   }
1091   if (dndirs > 0) {   if (dndirs > 0) {
1092   dnsort(dnd, dndirs);   dnsort(dnd, dndirs);
1093   showdirs(dnd, dndirs, dnfiles == 0);   showdirs(dnd, dnfiles == 0);
1094   if (ENABLE_FEATURE_CLEAN_UP)   if (ENABLE_FEATURE_CLEAN_UP)
1095   free(dnd);   free(dnd);
1096   }   }
1097   }   }
1098   if (ENABLE_FEATURE_CLEAN_UP)   if (ENABLE_FEATURE_CLEAN_UP)
1099   dfree(dnp, nfiles);   dfree(dnp);
1100   return exit_code;   return exit_code;
1101  }  }

Legend:
Removed from v.983  
changed lines
  Added in v.984