Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (hide annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 25495 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * tiny-ls.c version 0.1.0: A minimalist 'ls'
4     * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
5     *
6     * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7     */
8    
9     /*
10     * 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
12     * (i.e., the ones I couldn't live without :-) All features which involve
13     * linking in substantial chunks of libc can be disabled.
14     *
15     * Although I don't really want to add new features to this program to
16     * keep it small, I *am* interested to receive bug fixes and ways to make
17     * it more portable.
18     *
19     * KNOWN BUGS:
20     * 1. ls -l of a directory doesn't give "total <blocks>" header
21     * 2. ls of a symlink to a directory doesn't list directory contents
22     * 3. hidden files can make column width too large
23     *
24     * NON-OPTIMAL BEHAVIOUR:
25     * 1. autowidth reads directories twice
26     * 2. if you do a short directory listing without filetype characters
27     * appended, there's no need to stat each one
28     * PORTABILITY:
29     * 1. requires lstat (BSD) - how do you do it without?
30     */
31    
32     #include "busybox.h"
33     #include <getopt.h>
34    
35     enum {
36    
37     TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
38     COLUMN_GAP = 2, /* includes the file type char */
39    
40     /* what is the overall style of the listing */
41     STYLE_COLUMNS = 1 << 21, /* fill columns */
42     STYLE_LONG = 2 << 21, /* one record per line, extended info */
43     STYLE_SINGLE = 3 << 21, /* one record per line */
44     STYLE_MASK = STYLE_SINGLE,
45    
46     /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
47     /* what file information will be listed */
48     LIST_INO = 1 << 0,
49     LIST_BLOCKS = 1 << 1,
50     LIST_MODEBITS = 1 << 2,
51     LIST_NLINKS = 1 << 3,
52     LIST_ID_NAME = 1 << 4,
53     LIST_ID_NUMERIC = 1 << 5,
54     LIST_CONTEXT = 1 << 6,
55     LIST_SIZE = 1 << 7,
56     LIST_DEV = 1 << 8,
57     LIST_DATE_TIME = 1 << 9,
58     LIST_FULLTIME = 1 << 10,
59     LIST_FILENAME = 1 << 11,
60     LIST_SYMLINK = 1 << 12,
61     LIST_FILETYPE = 1 << 13,
62     LIST_EXEC = 1 << 14,
63     LIST_MASK = (LIST_EXEC << 1) - 1,
64    
65     /* what files will be displayed */
66     DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
67     DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
68     DISP_DOT = 1 << 17, /* show . and .. */
69     DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
70     DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
71     DISP_ROWS = 1 << 20, /* print across rows */
72     DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
73    
74     /* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
75     SORT_FORWARD = 0, /* sort in reverse order */
76     SORT_REVERSE = 1 << 27, /* sort in reverse order */
77    
78     SORT_NAME = 0, /* sort by file name */
79     SORT_SIZE = 1 << 28, /* sort by file size */
80     SORT_ATIME = 2 << 28, /* sort by last access time */
81     SORT_CTIME = 3 << 28, /* sort by last change time */
82     SORT_MTIME = 4 << 28, /* sort by last modification time */
83     SORT_VERSION = 5 << 28, /* sort by version */
84     SORT_EXT = 6 << 28, /* sort by file name extension */
85     SORT_DIR = 7 << 28, /* sort by file or directory */
86     SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
87    
88     /* which of the three times will be used */
89     TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
90     TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
91     TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
92    
93     FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
94    
95     LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
96    
97     LIST_SHORT = LIST_FILENAME,
98     LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
99     LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
100    
101     SPLIT_DIR = 1,
102     SPLIT_FILE = 0,
103     SPLIT_SUBDIR = 2,
104    
105     };
106    
107     #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
108     #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
109     #define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
110     #define COLOR(mode) ("\000\043\043\043\042\000\043\043"\
111     "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])
112     #define ATTR(mode) ("\00\00\01\00\01\00\01\00"\
113     "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
114    
115     /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
116     #if ENABLE_FEATURE_LS_COLOR
117     static int show_color;
118     /* long option entry used only for --color, which has no short option
119     * equivalent */
120     static const struct option ls_color_opt[] = {
121     { "color", optional_argument, NULL, 1 },
122     { NULL, 0, NULL, 0 }
123     };
124     #else
125     enum { show_color = 0 };
126     #endif
127    
128     /*
129     * a directory entry and its stat info are stored here
130     */
131     struct dnode { /* the basic node */
132     char *name; /* the dir entry name */
133     char *fullname; /* the dir entry name */
134     int allocated;
135     struct stat dstat; /* the file stat info */
136     USE_SELINUX(security_context_t sid;)
137     struct dnode *next; /* point at the next node */
138     };
139     typedef struct dnode dnode_t;
140    
141     static struct dnode **list_dir(const char *);
142     static struct dnode **dnalloc(int);
143     static int list_single(struct dnode *);
144    
145     static unsigned all_fmt;
146    
147     #if ENABLE_FEATURE_AUTOWIDTH
148     static unsigned tabstops = COLUMN_GAP;
149     static unsigned terminal_width = TERMINAL_WIDTH;
150     #else
151     enum {
152     tabstops = COLUMN_GAP,
153     terminal_width = TERMINAL_WIDTH,
154     };
155     #endif
156    
157     static int status = EXIT_SUCCESS;
158    
159     static struct dnode *my_stat(char *fullname, char *name)
160     {
161     struct stat dstat;
162     struct dnode *cur;
163     USE_SELINUX(security_context_t sid = NULL;)
164    
165     if (all_fmt & FOLLOW_LINKS) {
166     #if ENABLE_SELINUX
167     if (is_selinux_enabled()) {
168     getfilecon(fullname, &sid);
169     }
170     #endif
171     if (stat(fullname, &dstat)) {
172     bb_perror_msg("%s", fullname);
173     status = EXIT_FAILURE;
174     return 0;
175     }
176     } else {
177     #if ENABLE_SELINUX
178     if (is_selinux_enabled()) {
179     lgetfilecon(fullname,&sid);
180     }
181     #endif
182     if (lstat(fullname, &dstat)) {
183     bb_perror_msg("%s", fullname);
184     status = EXIT_FAILURE;
185     return 0;
186     }
187     }
188    
189     cur = xmalloc(sizeof(struct dnode));
190     cur->fullname = fullname;
191     cur->name = name;
192     cur->dstat = dstat;
193     USE_SELINUX(cur->sid = sid;)
194     return cur;
195     }
196    
197     #if ENABLE_FEATURE_LS_COLOR
198     static char fgcolor(mode_t mode)
199     {
200     /* Check wheter the file is existing (if so, color it red!) */
201     if (errno == ENOENT)
202     return '\037';
203     if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
204     return COLOR(0xF000); /* File is executable ... */
205     return COLOR(mode);
206     }
207    
208     static char bgcolor(mode_t mode)
209     {
210     if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
211     return ATTR(0xF000); /* File is executable ... */
212     return ATTR(mode);
213     }
214     #endif
215    
216     #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
217     static char append_char(mode_t mode)
218     {
219     if (!(all_fmt & LIST_FILETYPE))
220     return '\0';
221     if (S_ISDIR(mode))
222     return '/';
223     if (!(all_fmt & LIST_EXEC))
224     return '\0';
225     if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
226     return '*';
227     return APPCHAR(mode);
228     }
229     #endif
230    
231     #define countdirs(A, B) count_dirs((A), (B), 1)
232     #define countsubdirs(A, B) count_dirs((A), (B), 0)
233     static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
234     {
235     int i, dirs;
236    
237     if (!dn)
238     return 0;
239     dirs = 0;
240     for (i = 0; i < nfiles; i++) {
241     char *name;
242     if (!S_ISDIR(dn[i]->dstat.st_mode))
243     continue;
244     name = dn[i]->name;
245     if (notsubdirs
246     || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
247     ) {
248     dirs++;
249     }
250     }
251     return dirs;
252     }
253    
254     static int countfiles(struct dnode **dnp)
255     {
256     int nfiles;
257     struct dnode *cur;
258    
259     if (dnp == NULL)
260     return 0;
261     nfiles = 0;
262     for (cur = dnp[0]; cur->next; cur = cur->next)
263     nfiles++;
264     nfiles++;
265     return nfiles;
266     }
267    
268     /* get memory to hold an array of pointers */
269     static struct dnode **dnalloc(int num)
270     {
271     if (num < 1)
272     return NULL;
273    
274     return xzalloc(num * sizeof(struct dnode *));
275     }
276    
277     #if ENABLE_FEATURE_LS_RECURSIVE
278     static void dfree(struct dnode **dnp, int nfiles)
279     {
280     int i;
281    
282     if (dnp == NULL)
283     return;
284    
285     for (i = 0; i < nfiles; i++) {
286     struct dnode *cur = dnp[i];
287     if (cur->allocated)
288     free(cur->fullname); /* free the filename */
289     free(cur); /* free the dnode */
290     }
291     free(dnp); /* free the array holding the dnode pointers */
292     }
293     #else
294     #define dfree(...) ((void)0)
295     #endif
296    
297     static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
298     {
299     int dncnt, i, d;
300     struct dnode **dnp;
301    
302     if (dn == NULL || nfiles < 1)
303     return NULL;
304    
305     /* count how many dirs and regular files there are */
306     if (which == SPLIT_SUBDIR)
307     dncnt = countsubdirs(dn, nfiles);
308     else {
309     dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
310     if (which == SPLIT_FILE)
311     dncnt = nfiles - dncnt; /* looking for files */
312     }
313    
314     /* allocate a file array and a dir array */
315     dnp = dnalloc(dncnt);
316    
317     /* copy the entrys into the file or dir array */
318     for (d = i = 0; i < nfiles; i++) {
319     if (S_ISDIR(dn[i]->dstat.st_mode)) {
320     char *name;
321     if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
322     continue;
323     name = dn[i]->name;
324     if ((which & SPLIT_DIR)
325     || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
326     ) {
327     dnp[d++] = dn[i];
328     }
329     } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
330     dnp[d++] = dn[i];
331     }
332     }
333     return dnp;
334     }
335    
336     #if ENABLE_FEATURE_LS_SORTFILES
337     static int sortcmp(const void *a, const void *b)
338     {
339     struct dnode *d1 = *(struct dnode **)a;
340     struct dnode *d2 = *(struct dnode **)b;
341     unsigned sort_opts = all_fmt & SORT_MASK;
342     int dif;
343    
344     dif = 0; /* assume SORT_NAME */
345     // TODO: use pre-initialized function pointer
346     // instead of branch forest
347     if (sort_opts == SORT_SIZE) {
348     dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
349     } else if (sort_opts == SORT_ATIME) {
350     dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
351     } else if (sort_opts == SORT_CTIME) {
352     dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
353     } else if (sort_opts == SORT_MTIME) {
354     dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
355     } else if (sort_opts == SORT_DIR) {
356     dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
357     /* } else if (sort_opts == SORT_VERSION) { */
358     /* } else if (sort_opts == SORT_EXT) { */
359     }
360    
361     if (dif == 0) {
362     /* sort by name - may be a tie_breaker for time or size cmp */
363     if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
364     else dif = strcmp(d1->name, d2->name);
365     }
366    
367     if (all_fmt & SORT_REVERSE) {
368     dif = -dif;
369     }
370     return dif;
371     }
372    
373     static void dnsort(struct dnode **dn, int size)
374     {
375     qsort(dn, size, sizeof(*dn), sortcmp);
376     }
377     #else
378     #define dnsort(dn, size) ((void)0)
379     #endif
380    
381    
382     static void showfiles(struct dnode **dn, int nfiles)
383     {
384     int i, ncols, nrows, row, nc;
385     int column = 0;
386     int nexttab = 0;
387     int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
388    
389     if (dn == NULL || nfiles < 1)
390     return;
391    
392     if (all_fmt & STYLE_LONG) {
393     ncols = 1;
394     } else {
395     /* find the longest file name, use that as the column width */
396     for (i = 0; i < nfiles; i++) {
397     int len = strlen(dn[i]->name);
398     if (column_width < len)
399     column_width = len;
400     }
401     column_width += tabstops +
402     USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
403     ((all_fmt & LIST_INO) ? 8 : 0) +
404     ((all_fmt & LIST_BLOCKS) ? 5 : 0);
405     ncols = (int) (terminal_width / column_width);
406     }
407    
408     if (ncols > 1) {
409     nrows = nfiles / ncols;
410     if (nrows * ncols < nfiles)
411     nrows++; /* round up fractionals */
412     } else {
413     nrows = nfiles;
414     ncols = 1;
415     }
416    
417     for (row = 0; row < nrows; row++) {
418     for (nc = 0; nc < ncols; nc++) {
419     /* reach into the array based on the column and row */
420     i = (nc * nrows) + row; /* assume display by column */
421     if (all_fmt & DISP_ROWS)
422     i = (row * ncols) + nc; /* display across row */
423     if (i < nfiles) {
424     if (column > 0) {
425     nexttab -= column;
426     printf("%*s", nexttab, "");
427     column += nexttab;
428     }
429     nexttab = column + column_width;
430     column += list_single(dn[i]);
431     }
432     }
433     putchar('\n');
434     column = 0;
435     }
436     }
437    
438    
439     static void showdirs(struct dnode **dn, int ndirs, int first)
440     {
441     int i, nfiles;
442     struct dnode **subdnp;
443     int dndirs;
444     struct dnode **dnd;
445    
446     if (dn == NULL || ndirs < 1)
447     return;
448    
449     for (i = 0; i < ndirs; i++) {
450     if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
451     if (!first)
452     puts("");
453     first = 0;
454     printf("%s:\n", dn[i]->fullname);
455     }
456     subdnp = list_dir(dn[i]->fullname);
457     nfiles = countfiles(subdnp);
458     if (nfiles > 0) {
459     /* list all files at this level */
460     dnsort(subdnp, nfiles);
461     showfiles(subdnp, nfiles);
462     if (ENABLE_FEATURE_LS_RECURSIVE) {
463     if (all_fmt & DISP_RECURSIVE) {
464     /* recursive- list the sub-dirs */
465     dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
466     dndirs = countsubdirs(subdnp, nfiles);
467     if (dndirs > 0) {
468     dnsort(dnd, dndirs);
469     showdirs(dnd, dndirs, 0);
470     /* free the array of dnode pointers to the dirs */
471     free(dnd);
472     }
473     }
474     /* free the dnodes and the fullname mem */
475     dfree(subdnp, nfiles);
476     }
477     }
478     }
479     }
480    
481    
482     static struct dnode **list_dir(const char *path)
483     {
484     struct dnode *dn, *cur, **dnp;
485     struct dirent *entry;
486     DIR *dir;
487     int i, nfiles;
488    
489     if (path == NULL)
490     return NULL;
491    
492     dn = NULL;
493     nfiles = 0;
494     dir = warn_opendir(path);
495     if (dir == NULL) {
496     status = EXIT_FAILURE;
497     return NULL; /* could not open the dir */
498     }
499     while ((entry = readdir(dir)) != NULL) {
500     char *fullname;
501    
502     /* are we going to list the file- it may be . or .. or a hidden file */
503     if (entry->d_name[0] == '.') {
504     if ((entry->d_name[1] == 0 || (
505     entry->d_name[1] == '.'
506     && entry->d_name[2] == 0))
507     && !(all_fmt & DISP_DOT))
508     continue;
509     if (!(all_fmt & DISP_HIDDEN))
510     continue;
511     }
512     fullname = concat_path_file(path, entry->d_name);
513     cur = my_stat(fullname, strrchr(fullname, '/') + 1);
514     if (!cur) {
515     free(fullname);
516     continue;
517     }
518     cur->allocated = 1;
519     cur->next = dn;
520     dn = cur;
521     nfiles++;
522     }
523     closedir(dir);
524    
525     /* now that we know how many files there are
526     * allocate memory for an array to hold dnode pointers
527     */
528     if (dn == NULL)
529     return NULL;
530     dnp = dnalloc(nfiles);
531     for (i = 0, cur = dn; i < nfiles; i++) {
532     dnp[i] = cur; /* save pointer to node in array */
533     cur = cur->next;
534     }
535    
536     return dnp;
537     }
538    
539    
540     #if ENABLE_FEATURE_LS_TIMESTAMPS
541     /* Do time() just once. Saves one syscall per file for "ls -l" */
542     /* Initialized in main() */
543     static time_t current_time_t;
544     #endif
545    
546     static int list_single(struct dnode *dn)
547     {
548     int i, column = 0;
549    
550     #if ENABLE_FEATURE_LS_TIMESTAMPS
551     char *filetime;
552     time_t ttime, age;
553     #endif
554     #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
555     struct stat info;
556     char append;
557     #endif
558    
559     if (dn->fullname == NULL)
560     return 0;
561    
562     #if ENABLE_FEATURE_LS_TIMESTAMPS
563     ttime = dn->dstat.st_mtime; /* the default time */
564     if (all_fmt & TIME_ACCESS)
565     ttime = dn->dstat.st_atime;
566     if (all_fmt & TIME_CHANGE)
567     ttime = dn->dstat.st_ctime;
568     filetime = ctime(&ttime);
569     #endif
570     #if ENABLE_FEATURE_LS_FILETYPES
571     append = append_char(dn->dstat.st_mode);
572     #endif
573    
574     for (i = 0; i <= 31; i++) {
575     switch (all_fmt & (1 << i)) {
576     case LIST_INO:
577     column += printf("%7ld ", (long) dn->dstat.st_ino);
578     break;
579     case LIST_BLOCKS:
580     column += printf("%4"OFF_FMT"d ", (off_t) dn->dstat.st_blocks >> 1);
581     break;
582     case LIST_MODEBITS:
583     column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
584     break;
585     case LIST_NLINKS:
586     column += printf("%4ld ", (long) dn->dstat.st_nlink);
587     break;
588     case LIST_ID_NAME:
589     #if ENABLE_FEATURE_LS_USERNAME
590     printf("%-8.8s %-8.8s",
591     get_cached_username(dn->dstat.st_uid),
592     get_cached_groupname(dn->dstat.st_gid));
593     column += 17;
594     break;
595     #endif
596     case LIST_ID_NUMERIC:
597     column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
598     break;
599     case LIST_SIZE:
600     case LIST_DEV:
601     if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
602     column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev),
603     (int) minor(dn->dstat.st_rdev));
604     } else {
605     if (all_fmt & LS_DISP_HR) {
606     column += printf("%9s ",
607     make_human_readable_str(dn->dstat.st_size, 1, 0));
608     } else {
609     column += printf("%9"OFF_FMT"d ", (off_t) dn->dstat.st_size);
610     }
611     }
612     break;
613     #if ENABLE_FEATURE_LS_TIMESTAMPS
614     case LIST_FULLTIME:
615     printf("%24.24s ", filetime);
616     column += 25;
617     break;
618     case LIST_DATE_TIME:
619     if ((all_fmt & LIST_FULLTIME) == 0) {
620     /* current_time_t ~== time(NULL) */
621     age = current_time_t - ttime;
622     printf("%6.6s ", filetime + 4);
623     if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
624     /* hh:mm if less than 6 months old */
625     printf("%5.5s ", filetime + 11);
626     } else {
627     printf(" %4.4s ", filetime + 20);
628     }
629     column += 13;
630     }
631     break;
632     #endif
633     #if ENABLE_SELINUX
634     case LIST_CONTEXT:
635     {
636     char context[80];
637     int len = 0;
638    
639     if (dn->sid) {
640     /* I assume sid initilized with NULL */
641     len = strlen(dn->sid) + 1;
642     safe_strncpy(context, dn->sid, len);
643     freecon(dn->sid);
644     } else {
645     safe_strncpy(context, "unknown", 8);
646     }
647     printf("%-32s ", context);
648     column += MAX(33, len);
649     }
650     break;
651     #endif
652     case LIST_FILENAME:
653     errno = 0;
654     #if ENABLE_FEATURE_LS_COLOR
655     if (show_color && !lstat(dn->fullname, &info)) {
656     printf("\033[%d;%dm", bgcolor(info.st_mode),
657     fgcolor(info.st_mode));
658     }
659     #endif
660     column += printf("%s", dn->name);
661     if (show_color) {
662     printf("\033[0m");
663     }
664     break;
665     case LIST_SYMLINK:
666     if (S_ISLNK(dn->dstat.st_mode)) {
667     char *lpath = xreadlink(dn->fullname);
668     if (!lpath) break;
669     printf(" -> ");
670     #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
671     if (!stat(dn->fullname, &info)) {
672     append = append_char(info.st_mode);
673     }
674     #endif
675     #if ENABLE_FEATURE_LS_COLOR
676     if (show_color) {
677     errno = 0;
678     printf("\033[%d;%dm", bgcolor(info.st_mode),
679     fgcolor(info.st_mode));
680     }
681     #endif
682     column += printf("%s", lpath) + 4;
683     if (show_color) {
684     printf("\033[0m");
685     }
686     free(lpath);
687     }
688     break;
689     #if ENABLE_FEATURE_LS_FILETYPES
690     case LIST_FILETYPE:
691     if (append) {
692     putchar(append);
693     column++;
694     }
695     break;
696     #endif
697     }
698     }
699    
700     return column;
701     }
702    
703     /* "[-]Cadil1", POSIX mandated options, busybox always supports */
704     /* "[-]gnsx", POSIX non-mandated options, busybox always supports */
705     /* "[-]Ak" GNU options, busybox always supports */
706     /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
707     /* "[-]p", POSIX non-mandated options, busybox optionally supports */
708     /* "[-]SXvThw", GNU options, busybox optionally supports */
709     /* "[-]K", SELinux mandated options, busybox optionally supports */
710     /* "[-]e", I think we made this one up */
711     static const char ls_options[] = "Cadil1gnsxAk"
712     USE_FEATURE_LS_TIMESTAMPS("cetu")
713     USE_FEATURE_LS_SORTFILES("SXrv")
714     USE_FEATURE_LS_FILETYPES("Fp")
715     USE_FEATURE_LS_FOLLOWLINKS("L")
716     USE_FEATURE_LS_RECURSIVE("R")
717     USE_FEATURE_HUMAN_READABLE("h")
718     USE_SELINUX("K")
719     USE_FEATURE_AUTOWIDTH("T:w:");
720    
721     enum {
722     LIST_MASK_TRIGGER = 0,
723     STYLE_MASK_TRIGGER = STYLE_MASK,
724     DISP_MASK_TRIGGER = DISP_ROWS,
725     SORT_MASK_TRIGGER = SORT_MASK,
726     };
727    
728     static const unsigned opt_flags[] = {
729     LIST_SHORT | STYLE_COLUMNS, /* C */
730     DISP_HIDDEN | DISP_DOT, /* a */
731     DISP_NOLIST, /* d */
732     LIST_INO, /* i */
733     LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
734     LIST_SHORT | STYLE_SINGLE, /* 1 */
735     0, /* g - ingored */
736     LIST_ID_NUMERIC, /* n */
737     LIST_BLOCKS, /* s */
738     DISP_ROWS, /* x */
739     DISP_HIDDEN, /* A */
740     ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
741     #if ENABLE_FEATURE_LS_TIMESTAMPS
742     TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
743     LIST_FULLTIME, /* e */
744     ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
745     TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
746     #endif
747     #if ENABLE_FEATURE_LS_SORTFILES
748     SORT_SIZE, /* S */
749     SORT_EXT, /* X */
750     SORT_REVERSE, /* r */
751     SORT_VERSION, /* v */
752     #endif
753     #if ENABLE_FEATURE_LS_FILETYPES
754     LIST_FILETYPE | LIST_EXEC, /* F */
755     LIST_FILETYPE, /* p */
756     #endif
757     #if ENABLE_FEATURE_LS_FOLLOWLINKS
758     FOLLOW_LINKS, /* L */
759     #endif
760     #if ENABLE_FEATURE_LS_RECURSIVE
761     DISP_RECURSIVE, /* R */
762     #endif
763     #if ENABLE_FEATURE_HUMAN_READABLE
764     LS_DISP_HR, /* h */
765     #endif
766     #if ENABLE_SELINUX
767     LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
768     #endif
769     #if ENABLE_FEATURE_AUTOWIDTH
770     0, 0, /* T, w - ignored */
771     #endif
772     (1U<<31)
773     };
774    
775    
776     int ls_main(int argc, char **argv)
777     {
778     struct dnode **dnd;
779     struct dnode **dnf;
780     struct dnode **dnp;
781     struct dnode *dn;
782     struct dnode *cur;
783     unsigned opt;
784     int nfiles = 0;
785     int dnfiles;
786     int dndirs;
787     int oi;
788     int ac;
789     int i;
790     char **av;
791     USE_FEATURE_AUTOWIDTH(char *tabstops_str = NULL;)
792     USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;)
793     USE_FEATURE_LS_COLOR(char *color_opt;)
794    
795     setvbuf(stdout, bb_common_bufsiz1, _IOFBF, BUFSIZ);
796    
797     #if ENABLE_FEATURE_LS_TIMESTAMPS
798     time(&current_time_t);
799     #endif
800    
801     all_fmt = LIST_SHORT |
802     (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
803    
804     #if ENABLE_FEATURE_AUTOWIDTH
805     /* Obtain the terminal width */
806     get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL);
807     /* Go one less... */
808     terminal_width--;
809     #endif
810    
811     /* process options */
812     USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
813     #if ENABLE_FEATURE_AUTOWIDTH
814     opt = getopt32(argc, argv, ls_options, &tabstops_str, &terminal_width_str
815     USE_FEATURE_LS_COLOR(, &color_opt));
816     if (tabstops_str)
817     tabstops = xatou(tabstops_str);
818     if (terminal_width_str)
819     terminal_width = xatou(terminal_width_str);
820     #else
821     opt = getopt32(argc, argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
822     #endif
823     for (i = 0; opt_flags[i] != (1U<<31); i++) {
824     if (opt & (1 << i)) {
825     unsigned flags = opt_flags[i];
826    
827     if (flags & LIST_MASK_TRIGGER)
828     all_fmt &= ~LIST_MASK;
829     if (flags & STYLE_MASK_TRIGGER)
830     all_fmt &= ~STYLE_MASK;
831     if (flags & SORT_MASK_TRIGGER)
832     all_fmt &= ~SORT_MASK;
833     if (flags & DISP_MASK_TRIGGER)
834     all_fmt &= ~DISP_MASK;
835     if (flags & TIME_MASK)
836     all_fmt &= ~TIME_MASK;
837     if (flags & LIST_CONTEXT)
838     all_fmt |= STYLE_SINGLE;
839     if (LS_DISP_HR && opt == 'l')
840     all_fmt &= ~LS_DISP_HR;
841     all_fmt |= flags;
842     }
843     }
844    
845     #if ENABLE_FEATURE_LS_COLOR
846     /* find color bit value - last position for short getopt */
847     if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
848     char *p = getenv("LS_COLORS");
849     /* LS_COLORS is unset, or (not empty && not "none") ? */
850     if (!p || (p[0] && strcmp(p, "none")))
851     show_color = 1;
852     }
853     if (opt & (1 << i)) { /* next flag after short options */
854     if (!color_opt || !strcmp("always", color_opt))
855     show_color = 1;
856     else if (color_opt && !strcmp("never", color_opt))
857     show_color = 0;
858     else if (color_opt && !strcmp("auto", color_opt) && isatty(STDOUT_FILENO))
859     show_color = 1;
860     }
861     #endif
862    
863     /* sort out which command line options take precedence */
864     if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
865     all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
866     if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
867     if (all_fmt & TIME_CHANGE)
868     all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
869     if (all_fmt & TIME_ACCESS)
870     all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
871     }
872     if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
873     all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
874     if (ENABLE_FEATURE_LS_USERNAME)
875     if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
876     all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
877    
878     /* choose a display format */
879     if (!(all_fmt & STYLE_MASK))
880     all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
881    
882     /*
883     * when there are no cmd line args we have to supply a default "." arg.
884     * we will create a second argv array, "av" that will hold either
885     * our created "." arg, or the real cmd line args. The av array
886     * just holds the pointers- we don't move the date the pointers
887     * point to.
888     */
889     ac = argc - optind; /* how many cmd line args are left */
890     if (ac < 1) {
891     static const char *const dotdir[] = { "." };
892    
893     av = (char **) dotdir;
894     ac = 1;
895     } else {
896     av = argv + optind;
897     }
898    
899     /* now, everything is in the av array */
900     if (ac > 1)
901     all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
902    
903     /* stuff the command line file names into a dnode array */
904     dn = NULL;
905     for (oi = 0; oi < ac; oi++) {
906     cur = my_stat(av[oi], av[oi]);
907     if (!cur)
908     continue;
909     cur->allocated = 0;
910     cur->next = dn;
911     dn = cur;
912     nfiles++;
913     }
914    
915     /* now that we know how many files there are
916     * allocate memory for an array to hold dnode pointers
917     */
918     dnp = dnalloc(nfiles);
919     for (i = 0, cur = dn; i < nfiles; i++) {
920     dnp[i] = cur; /* save pointer to node in array */
921     cur = cur->next;
922     }
923    
924     if (all_fmt & DISP_NOLIST) {
925     dnsort(dnp, nfiles);
926     if (nfiles > 0)
927     showfiles(dnp, nfiles);
928     } else {
929     dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
930     dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
931     dndirs = countdirs(dnp, nfiles);
932     dnfiles = nfiles - dndirs;
933     if (dnfiles > 0) {
934     dnsort(dnf, dnfiles);
935     showfiles(dnf, dnfiles);
936     if (ENABLE_FEATURE_CLEAN_UP)
937     free(dnf);
938     }
939     if (dndirs > 0) {
940     dnsort(dnd, dndirs);
941     showdirs(dnd, dndirs, dnfiles == 0);
942     if (ENABLE_FEATURE_CLEAN_UP)
943     free(dnd);
944     }
945     }
946     if (ENABLE_FEATURE_CLEAN_UP)
947     dfree(dnp, nfiles);
948     return status;
949     }