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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 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.