source: branches/stable/mindi-busybox/coreutils/ls.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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