source: MondoRescue/branches/3.3/mindi-busybox/coreutils/ls.c@ 3621

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 35.5 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
4 *
5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6 */
7
8/* [date unknown. Perhaps before year 2000]
9 * To achieve a small memory footprint, this version of 'ls' doesn't do any
10 * file sorting, and only has the most essential command line switches
11 * (i.e., the ones I couldn't live without :-) All features which involve
12 * linking in substantial chunks of libc can be disabled.
13 *
14 * Although I don't really want to add new features to this program to
15 * keep it small, I *am* interested to receive bug fixes and ways to make
16 * it more portable.
17 *
18 * KNOWN BUGS:
19 * 1. hidden files can make column width too large
20 *
21 * NON-OPTIMAL BEHAVIOUR:
22 * 1. autowidth reads directories twice
23 * 2. if you do a short directory listing without filetype characters
24 * appended, there's no need to stat each one
25 * PORTABILITY:
26 * 1. requires lstat (BSD) - how do you do it without?
27 *
28 * [2009-03]
29 * ls sorts listing now, and supports almost all options.
30 */
31
32//usage:#define ls_trivial_usage
33//usage: "[-1AaCxd"
34//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
35//usage: IF_FEATURE_LS_RECURSIVE("R")
36//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
37//usage: IF_FEATURE_LS_TIMESTAMPS("e")
38//usage: IF_FEATURE_HUMAN_READABLE("h")
39//usage: IF_FEATURE_LS_SORTFILES("rSXv")
40//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
41//usage: IF_SELINUX("kKZ") "]"
42//usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
43//usage:#define ls_full_usage "\n\n"
44//usage: "List directory contents\n"
45//usage: "\n -1 One column output"
46//usage: "\n -a Include entries which start with ."
47//usage: "\n -A Like -a, but exclude . and .."
48//usage: "\n -C List by columns"
49//usage: "\n -x List by lines"
50//usage: "\n -d List directory entries instead of contents"
51//usage: IF_FEATURE_LS_FOLLOWLINKS(
52//usage: "\n -L Follow symlinks"
53//usage: "\n -H Follow symlinks on command line"
54//usage: )
55//usage: IF_FEATURE_LS_RECURSIVE(
56//usage: "\n -R Recurse"
57//usage: )
58//usage: IF_FEATURE_LS_FILETYPES(
59//usage: "\n -p Append / to dir entries"
60//usage: "\n -F Append indicator (one of */=@|) to entries"
61//usage: )
62//usage: "\n -l Long listing format"
63//usage: "\n -i List inode numbers"
64//usage: "\n -n List numeric UIDs and GIDs instead of names"
65//usage: "\n -s List allocated blocks"
66//usage: IF_FEATURE_LS_TIMESTAMPS(
67//usage: "\n -e List full date and time"
68//usage: )
69//usage: IF_FEATURE_HUMAN_READABLE(
70//usage: "\n -h List sizes in human readable format (1K 243M 2G)"
71//usage: )
72//usage: IF_FEATURE_LS_SORTFILES(
73//usage: "\n -r Sort in reverse order"
74//usage: "\n -S Sort by size"
75//usage: "\n -X Sort by extension"
76//usage: "\n -v Sort by version"
77//usage: )
78//usage: IF_FEATURE_LS_TIMESTAMPS(
79//usage: "\n -c With -l: sort by ctime"
80//usage: "\n -t With -l: sort by mtime"
81//usage: "\n -u With -l: sort by atime"
82//usage: )
83//usage: IF_SELINUX(
84//usage: "\n -k List security context"
85//usage: "\n -K List security context in long format"
86//usage: "\n -Z List security context and permission"
87//usage: )
88//usage: IF_FEATURE_AUTOWIDTH(
89//usage: "\n -w N Assume the terminal is N columns wide"
90//usage: )
91//usage: IF_FEATURE_LS_COLOR(
92//usage: "\n --color[={always,never,auto}] Control coloring"
93//usage: )
94
95#include "libbb.h"
96#include "common_bufsiz.h"
97#include "unicode.h"
98
99
100/* This is a NOEXEC applet. Be very careful! */
101
102
103#if ENABLE_FTPD
104/* ftpd uses ls, and without timestamps Mozilla won't understand
105 * ftpd's LIST output.
106 */
107# undef CONFIG_FEATURE_LS_TIMESTAMPS
108# undef ENABLE_FEATURE_LS_TIMESTAMPS
109# undef IF_FEATURE_LS_TIMESTAMPS
110# undef IF_NOT_FEATURE_LS_TIMESTAMPS
111# define CONFIG_FEATURE_LS_TIMESTAMPS 1
112# define ENABLE_FEATURE_LS_TIMESTAMPS 1
113# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
114# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
115#endif
116
117
118enum {
119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
120
121SPLIT_FILE = 0,
122SPLIT_DIR = 1,
123SPLIT_SUBDIR = 2,
124
125/* Bits in G.all_fmt: */
126
127/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
128/* what file information will be listed */
129LIST_INO = 1 << 0,
130LIST_BLOCKS = 1 << 1,
131LIST_MODEBITS = 1 << 2,
132LIST_NLINKS = 1 << 3,
133LIST_ID_NAME = 1 << 4,
134LIST_ID_NUMERIC = 1 << 5,
135LIST_CONTEXT = 1 << 6,
136LIST_SIZE = 1 << 7,
137LIST_DATE_TIME = 1 << 8,
138LIST_FULLTIME = 1 << 9,
139LIST_SYMLINK = 1 << 10,
140LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */
141LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
142LIST_MASK = (LIST_CLASSIFY << 1) - 1,
143
144/* what files will be displayed */
145DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */
146DISP_HIDDEN = 1 << 14, /* show filenames starting with . */
147DISP_DOT = 1 << 15, /* show . and .. */
148DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */
149DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */
150DISP_ROWS = 1 << 18, /* print across rows */
151DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
152
153/* what is the overall style of the listing */
154STYLE_COLUMNAR = 1 << 19, /* many records per line */
155STYLE_LONG = 2 << 19, /* one record per line, extended info */
156STYLE_SINGLE = 3 << 19, /* one record per line */
157STYLE_MASK = STYLE_SINGLE,
158
159/* which of the three times will be used */
160TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
161TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
162TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
163
164/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
165SORT_REVERSE = 1 << 23,
166
167SORT_NAME = 0, /* sort by file name */
168SORT_SIZE = 1 << 24, /* sort by file size */
169SORT_ATIME = 2 << 24, /* sort by last access time */
170SORT_CTIME = 3 << 24, /* sort by last change time */
171SORT_MTIME = 4 << 24, /* sort by last modification time */
172SORT_VERSION = 5 << 24, /* sort by version */
173SORT_EXT = 6 << 24, /* sort by file name extension */
174SORT_DIR = 7 << 24, /* sort by file or directory */
175SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
176
177LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
178 LIST_DATE_TIME | LIST_SYMLINK,
179};
180
181/* -Cadil1 Std options, busybox always supports */
182/* -gnsxA Std options, busybox always supports */
183/* -Q GNU option, busybox always supports */
184/* -k SELinux option, busybox always supports (ignores if !SELinux) */
185/* Std has -k which means "show sizes in kbytes" */
186/* -LHRctur Std options, busybox optionally supports */
187/* -Fp Std options, busybox optionally supports */
188/* -SXvhTw GNU options, busybox optionally supports */
189/* -T WIDTH Ignored (we don't use tabs on output) */
190/* -KZ SELinux mandated options, busybox optionally supports */
191/* (coreutils 8.4 has no -K, remove it?) */
192/* -e I think we made this one up (looks similar to GNU --full-time) */
193/* We already used up all 32 bits, if we need to add more, candidates for removal: */
194/* -K, -T, -e (add --full-time instead) */
195static const char ls_options[] ALIGN1 =
196 "Cadil1gnsxQAk" /* 13 opts, total 13 */
197 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
198 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
199 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
200 IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */
201 IF_SELINUX("KZ") /* 2, 26 */
202 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */
203 IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */
204 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */
205 /* with --color, we use all 32 bits */;
206enum {
207 //OPT_C = (1 << 0),
208 //OPT_a = (1 << 1),
209 //OPT_d = (1 << 2),
210 //OPT_i = (1 << 3),
211 //OPT_l = (1 << 4),
212 //OPT_1 = (1 << 5),
213 OPT_g = (1 << 6),
214 //OPT_n = (1 << 7),
215 //OPT_s = (1 << 8),
216 //OPT_x = (1 << 9),
217 OPT_Q = (1 << 10),
218 //OPT_A = (1 << 11),
219 //OPT_k = (1 << 12),
220
221 OPTBIT_c = 13,
222 OPTBIT_e,
223 OPTBIT_t,
224 OPTBIT_u,
225 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
226 OPTBIT_X, /* 18 */
227 OPTBIT_r,
228 OPTBIT_v,
229 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
230 OPTBIT_p, /* 22 */
231 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
232 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
233 OPTBIT_Z, /* 25 */
234 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
235 OPTBIT_H, /* 27 */
236 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
237 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
238 OPTBIT_w, /* 30 */
239 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
240
241 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
242 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
243 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
245 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
246 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
247 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
248 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
249 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
250 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
251 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
252 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
253 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
254 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
255 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
256 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
257 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
258 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
259 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
260};
261
262/* TODO: simple toggles may be stored as OPT_xxx bits instead */
263static const uint32_t opt_flags[] = {
264 STYLE_COLUMNAR, /* C */
265 DISP_HIDDEN | DISP_DOT, /* a */
266 DISP_NOLIST, /* d */
267 LIST_INO, /* i */
268 LIST_LONG | STYLE_LONG, /* l */
269 STYLE_SINGLE, /* 1 */
270 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
271 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
272 LIST_BLOCKS, /* s */
273 DISP_ROWS | STYLE_COLUMNAR, /* x */
274 0, /* Q (quote filename) - handled via OPT_Q */
275 DISP_HIDDEN, /* A */
276 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
277#if ENABLE_FEATURE_LS_TIMESTAMPS
278 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
279 LIST_FULLTIME, /* e */
280 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
281 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
282#endif
283#if ENABLE_FEATURE_LS_SORTFILES
284 SORT_SIZE, /* S */
285 SORT_EXT, /* X */
286 SORT_REVERSE, /* r */
287 SORT_VERSION, /* v */
288#endif
289#if ENABLE_FEATURE_LS_FILETYPES
290 LIST_FILETYPE | LIST_CLASSIFY, /* F */
291 LIST_FILETYPE, /* p */
292#endif
293#if ENABLE_FEATURE_LS_RECURSIVE
294 DISP_RECURSIVE, /* R */
295#endif
296#if ENABLE_SELINUX
297 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
298 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
299#endif
300 (1U << 31)
301 /* options after Z are not processed through opt_flags */
302};
303
304
305/*
306 * a directory entry and its stat info
307 */
308struct dnode {
309 const char *name; /* usually basename, but think "ls -l dir/file" */
310 const char *fullname; /* full name (usable for stat etc) */
311 struct dnode *dn_next; /* for linked list */
312 IF_SELINUX(security_context_t sid;)
313 smallint fname_allocated;
314
315 /* Used to avoid re-doing [l]stat at printout stage
316 * if we already collected needed data in scan stage:
317 */
318 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
319 mode_t dn_mode_stat; /* obtained with stat, or 0 */
320
321// struct stat dstat;
322// struct stat is huge. We don't need it in full.
323// At least we don't need st_dev and st_blksize,
324// but there are invisible fields as well
325// (such as nanosecond-resolution timespamps)
326// and padding, which we also don't want to store.
327// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
328// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
329//
330 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
331 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
332 off_t dn_size;
333#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
334 time_t dn_atime;
335 time_t dn_mtime;
336 time_t dn_ctime;
337#endif
338 ino_t dn_ino;
339 blkcnt_t dn_blocks;
340 nlink_t dn_nlink;
341 uid_t dn_uid;
342 gid_t dn_gid;
343 int dn_rdev_maj;
344 int dn_rdev_min;
345// dev_t dn_dev;
346// blksize_t dn_blksize;
347};
348
349struct globals {
350#if ENABLE_FEATURE_LS_COLOR
351 smallint show_color;
352# define G_show_color (G.show_color)
353#else
354# define G_show_color 0
355#endif
356 smallint exit_code;
357 unsigned all_fmt;
358#if ENABLE_FEATURE_AUTOWIDTH
359 unsigned terminal_width;
360# define G_terminal_width (G.terminal_width)
361#else
362# define G_terminal_width TERMINAL_WIDTH
363#endif
364#if ENABLE_FEATURE_LS_TIMESTAMPS
365 /* Do time() just once. Saves one syscall per file for "ls -l" */
366 time_t current_time_t;
367#endif
368} FIX_ALIASING;
369#define G (*(struct globals*)bb_common_bufsiz1)
370#define INIT_G() do { \
371 setup_common_bufsiz(); \
372 /* we have to zero it out because of NOEXEC */ \
373 memset(&G, 0, sizeof(G)); \
374 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
375 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
376} while (0)
377
378
379/*** Output code ***/
380
381
382/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
383 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
384 * 3/7:multiplexed char/block device)
385 * and we use 0 for unknown and 15 for executables (see below) */
386#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
387/* un fi chr - dir - blk - file - link - sock - - exe */
388#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
389/* 036 black foreground 050 black background
390 037 red foreground 051 red background
391 040 green foreground 052 green background
392 041 brown foreground 053 brown background
393 042 blue foreground 054 blue background
394 043 magenta (purple) foreground 055 magenta background
395 044 cyan (light blue) foreground 056 cyan background
396 045 gray foreground 057 white background
397*/
398#define COLOR(mode) ( \
399 /*un fi chr - dir - blk - file - link - sock - - exe */ \
400 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
401 [TYPEINDEX(mode)])
402/* Select normal (0) [actually "reset all"] or bold (1)
403 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
404 * let's use 7 for "impossible" types, just for fun)
405 * Note: coreutils 6.9 uses inverted red for setuid binaries.
406 */
407#define ATTR(mode) ( \
408 /*un fi chr - dir - blk - file- link- sock- - exe */ \
409 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
410 [TYPEINDEX(mode)])
411
412#if ENABLE_FEATURE_LS_COLOR
413/* mode of zero is interpreted as "unknown" (stat failed) */
414static char fgcolor(mode_t mode)
415{
416 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
417 return COLOR(0xF000); /* File is executable ... */
418 return COLOR(mode);
419}
420static char bold(mode_t mode)
421{
422 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
423 return ATTR(0xF000); /* File is executable ... */
424 return ATTR(mode);
425}
426#endif
427
428#if ENABLE_FEATURE_LS_FILETYPES
429static char append_char(mode_t mode)
430{
431 if (!(G.all_fmt & LIST_FILETYPE))
432 return '\0';
433 if (S_ISDIR(mode))
434 return '/';
435 if (!(G.all_fmt & LIST_CLASSIFY))
436 return '\0';
437 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
438 return '*';
439 return APPCHAR(mode);
440}
441#endif
442
443static unsigned calc_name_len(const char *name)
444{
445 unsigned len;
446 uni_stat_t uni_stat;
447
448 // TODO: quote tab as \t, etc, if -Q
449 name = printable_string(&uni_stat, name);
450
451 if (!(option_mask32 & OPT_Q)) {
452 return uni_stat.unicode_width;
453 }
454
455 len = 2 + uni_stat.unicode_width;
456 while (*name) {
457 if (*name == '"' || *name == '\\') {
458 len++;
459 }
460 name++;
461 }
462 return len;
463}
464
465/* Return the number of used columns.
466 * Note that only STYLE_COLUMNAR uses return value.
467 * STYLE_SINGLE and STYLE_LONG don't care.
468 * coreutils 7.2 also supports:
469 * ls -b (--escape) = octal escapes (although it doesn't look like working)
470 * ls -N (--literal) = not escape at all
471 */
472static unsigned print_name(const char *name)
473{
474 unsigned len;
475 uni_stat_t uni_stat;
476
477 // TODO: quote tab as \t, etc, if -Q
478 name = printable_string(&uni_stat, name);
479
480 if (!(option_mask32 & OPT_Q)) {
481 fputs(name, stdout);
482 return uni_stat.unicode_width;
483 }
484
485 len = 2 + uni_stat.unicode_width;
486 putchar('"');
487 while (*name) {
488 if (*name == '"' || *name == '\\') {
489 putchar('\\');
490 len++;
491 }
492 putchar(*name);
493 name++;
494 }
495 putchar('"');
496 return len;
497}
498
499/* Return the number of used columns.
500 * Note that only STYLE_COLUMNAR uses return value,
501 * STYLE_SINGLE and STYLE_LONG don't care.
502 */
503static NOINLINE unsigned display_single(const struct dnode *dn)
504{
505 unsigned column = 0;
506 char *lpath;
507#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
508 struct stat statbuf;
509 char append;
510#endif
511
512#if ENABLE_FEATURE_LS_FILETYPES
513 append = append_char(dn->dn_mode);
514#endif
515
516 /* Do readlink early, so that if it fails, error message
517 * does not appear *inside* the "ls -l" line */
518 lpath = NULL;
519 if (G.all_fmt & LIST_SYMLINK)
520 if (S_ISLNK(dn->dn_mode))
521 lpath = xmalloc_readlink_or_warn(dn->fullname);
522
523 if (G.all_fmt & LIST_INO)
524 column += printf("%7llu ", (long long) dn->dn_ino);
525//TODO: -h should affect -s too:
526 if (G.all_fmt & LIST_BLOCKS)
527 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
528 if (G.all_fmt & LIST_MODEBITS)
529 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
530 if (G.all_fmt & LIST_NLINKS)
531 column += printf("%4lu ", (long) dn->dn_nlink);
532 if (G.all_fmt & LIST_ID_NUMERIC) {
533 if (option_mask32 & OPT_g)
534 column += printf("%-8u ", (int) dn->dn_gid);
535 else
536 column += printf("%-8u %-8u ",
537 (int) dn->dn_uid,
538 (int) dn->dn_gid);
539 }
540#if ENABLE_FEATURE_LS_USERNAME
541 else if (G.all_fmt & LIST_ID_NAME) {
542 if (option_mask32 & OPT_g) {
543 column += printf("%-8.8s ",
544 get_cached_groupname(dn->dn_gid));
545 } else {
546 column += printf("%-8.8s %-8.8s ",
547 get_cached_username(dn->dn_uid),
548 get_cached_groupname(dn->dn_gid));
549 }
550 }
551#endif
552 if (G.all_fmt & LIST_SIZE) {
553 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
554 column += printf("%4u, %3u ",
555 dn->dn_rdev_maj,
556 dn->dn_rdev_min);
557 } else {
558 if (option_mask32 & OPT_h) {
559 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
560 /* print size, show one fractional, use suffixes */
561 make_human_readable_str(dn->dn_size, 1, 0)
562 );
563 } else {
564 column += printf("%9"OFF_FMT"u ", dn->dn_size);
565 }
566 }
567 }
568#if ENABLE_FEATURE_LS_TIMESTAMPS
569 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
570 char *filetime;
571 const time_t *ttime = &dn->dn_mtime;
572 if (G.all_fmt & TIME_ACCESS)
573 ttime = &dn->dn_atime;
574 if (G.all_fmt & TIME_CHANGE)
575 ttime = &dn->dn_ctime;
576 filetime = ctime(ttime);
577 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
578 if (G.all_fmt & LIST_FULLTIME) { /* -e */
579 /* Note: coreutils 8.4 ls --full-time prints:
580 * 2009-07-13 17:49:27.000000000 +0200
581 */
582 column += printf("%.24s ", filetime);
583 } else { /* LIST_DATE_TIME */
584 /* G.current_time_t ~== time(NULL) */
585 time_t age = G.current_time_t - *ttime;
586 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
587 /* less than 6 months old */
588 /* "mmm dd hh:mm " */
589 printf("%.12s ", filetime + 4);
590 } else {
591 /* "mmm dd yyyy " */
592 /* "mmm dd yyyyy " after year 9999 :) */
593 strchr(filetime + 20, '\n')[0] = ' ';
594 printf("%.7s%6s", filetime + 4, filetime + 20);
595 }
596 column += 13;
597 }
598 }
599#endif
600#if ENABLE_SELINUX
601 if (G.all_fmt & LIST_CONTEXT) {
602 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
603 freecon(dn->sid);
604 }
605#endif
606
607#if ENABLE_FEATURE_LS_COLOR
608 if (G_show_color) {
609 mode_t mode = dn->dn_mode_lstat;
610 if (!mode)
611 if (lstat(dn->fullname, &statbuf) == 0)
612 mode = statbuf.st_mode;
613 printf("\033[%u;%um", bold(mode), fgcolor(mode));
614 }
615#endif
616 column += print_name(dn->name);
617 if (G_show_color) {
618 printf("\033[0m");
619 }
620
621 if (lpath) {
622 printf(" -> ");
623#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
624 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
625 mode_t mode = dn->dn_mode_stat;
626 if (!mode)
627 if (stat(dn->fullname, &statbuf) == 0)
628 mode = statbuf.st_mode;
629# if ENABLE_FEATURE_LS_FILETYPES
630 append = append_char(mode);
631# endif
632# if ENABLE_FEATURE_LS_COLOR
633 if (G_show_color) {
634 printf("\033[%u;%um", bold(mode), fgcolor(mode));
635 }
636# endif
637 }
638#endif
639 column += print_name(lpath) + 4;
640 free(lpath);
641 if (G_show_color) {
642 printf("\033[0m");
643 }
644 }
645#if ENABLE_FEATURE_LS_FILETYPES
646 if (G.all_fmt & LIST_FILETYPE) {
647 if (append) {
648 putchar(append);
649 column++;
650 }
651 }
652#endif
653
654 return column;
655}
656
657static void display_files(struct dnode **dn, unsigned nfiles)
658{
659 unsigned i, ncols, nrows, row, nc;
660 unsigned column;
661 unsigned nexttab;
662 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
663
664 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
665 ncols = 1;
666 } else {
667 /* find the longest file name, use that as the column width */
668 for (i = 0; dn[i]; i++) {
669 int len = calc_name_len(dn[i]->name);
670 if (column_width < len)
671 column_width = len;
672 }
673 column_width += 2 +
674 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
675 ((G.all_fmt & LIST_INO) ? 8 : 0) +
676 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
677 ncols = (unsigned)G_terminal_width / column_width;
678 }
679
680 if (ncols > 1) {
681 nrows = nfiles / ncols;
682 if (nrows * ncols < nfiles)
683 nrows++; /* round up fractionals */
684 } else {
685 nrows = nfiles;
686 ncols = 1;
687 }
688
689 column = 0;
690 nexttab = 0;
691 for (row = 0; row < nrows; row++) {
692 for (nc = 0; nc < ncols; nc++) {
693 /* reach into the array based on the column and row */
694 if (G.all_fmt & DISP_ROWS)
695 i = (row * ncols) + nc; /* display across row */
696 else
697 i = (nc * nrows) + row; /* display by column */
698 if (i < nfiles) {
699 if (column > 0) {
700 nexttab -= column;
701 printf("%*s", nexttab, "");
702 column += nexttab;
703 }
704 nexttab = column + column_width;
705 column += display_single(dn[i]);
706 }
707 }
708 putchar('\n');
709 column = 0;
710 }
711}
712
713
714/*** Dir scanning code ***/
715
716static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
717{
718 struct stat statbuf;
719 struct dnode *cur;
720
721 cur = xzalloc(sizeof(*cur));
722 cur->fullname = fullname;
723 cur->name = name;
724
725 if ((option_mask32 & OPT_L) || force_follow) {
726#if ENABLE_SELINUX
727 if (is_selinux_enabled()) {
728 getfilecon(fullname, &cur->sid);
729 }
730#endif
731 if (stat(fullname, &statbuf)) {
732 bb_simple_perror_msg(fullname);
733 G.exit_code = EXIT_FAILURE;
734 free(cur);
735 return NULL;
736 }
737 cur->dn_mode_stat = statbuf.st_mode;
738 } else {
739#if ENABLE_SELINUX
740 if (is_selinux_enabled()) {
741 lgetfilecon(fullname, &cur->sid);
742 }
743#endif
744 if (lstat(fullname, &statbuf)) {
745 bb_simple_perror_msg(fullname);
746 G.exit_code = EXIT_FAILURE;
747 free(cur);
748 return NULL;
749 }
750 cur->dn_mode_lstat = statbuf.st_mode;
751 }
752
753 /* cur->dstat = statbuf: */
754 cur->dn_mode = statbuf.st_mode ;
755 cur->dn_size = statbuf.st_size ;
756#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
757 cur->dn_atime = statbuf.st_atime ;
758 cur->dn_mtime = statbuf.st_mtime ;
759 cur->dn_ctime = statbuf.st_ctime ;
760#endif
761 cur->dn_ino = statbuf.st_ino ;
762 cur->dn_blocks = statbuf.st_blocks;
763 cur->dn_nlink = statbuf.st_nlink ;
764 cur->dn_uid = statbuf.st_uid ;
765 cur->dn_gid = statbuf.st_gid ;
766 cur->dn_rdev_maj = major(statbuf.st_rdev);
767 cur->dn_rdev_min = minor(statbuf.st_rdev);
768
769 return cur;
770}
771
772static unsigned count_dirs(struct dnode **dn, int which)
773{
774 unsigned dirs, all;
775
776 if (!dn)
777 return 0;
778
779 dirs = all = 0;
780 for (; *dn; dn++) {
781 const char *name;
782
783 all++;
784 if (!S_ISDIR((*dn)->dn_mode))
785 continue;
786
787 name = (*dn)->name;
788 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
789 /* or if it's not . or .. */
790 || name[0] != '.'
791 || (name[1] && (name[1] != '.' || name[2]))
792 ) {
793 dirs++;
794 }
795 }
796 return which != SPLIT_FILE ? dirs : all - dirs;
797}
798
799/* get memory to hold an array of pointers */
800static struct dnode **dnalloc(unsigned num)
801{
802 if (num < 1)
803 return NULL;
804
805 num++; /* so that we have terminating NULL */
806 return xzalloc(num * sizeof(struct dnode *));
807}
808
809#if ENABLE_FEATURE_LS_RECURSIVE
810static void dfree(struct dnode **dnp)
811{
812 unsigned i;
813
814 if (dnp == NULL)
815 return;
816
817 for (i = 0; dnp[i]; i++) {
818 struct dnode *cur = dnp[i];
819 if (cur->fname_allocated)
820 free((char*)cur->fullname);
821 free(cur);
822 }
823 free(dnp);
824}
825#else
826#define dfree(...) ((void)0)
827#endif
828
829/* Returns NULL-terminated malloced vector of pointers (or NULL) */
830static struct dnode **splitdnarray(struct dnode **dn, int which)
831{
832 unsigned dncnt, d;
833 struct dnode **dnp;
834
835 if (dn == NULL)
836 return NULL;
837
838 /* count how many dirs or files there are */
839 dncnt = count_dirs(dn, which);
840
841 /* allocate a file array and a dir array */
842 dnp = dnalloc(dncnt);
843
844 /* copy the entrys into the file or dir array */
845 for (d = 0; *dn; dn++) {
846 if (S_ISDIR((*dn)->dn_mode)) {
847 const char *name;
848
849 if (which == SPLIT_FILE)
850 continue;
851
852 name = (*dn)->name;
853 if ((which & SPLIT_DIR) /* any dir... */
854 /* ... or not . or .. */
855 || name[0] != '.'
856 || (name[1] && (name[1] != '.' || name[2]))
857 ) {
858 dnp[d++] = *dn;
859 }
860 } else
861 if (which == SPLIT_FILE) {
862 dnp[d++] = *dn;
863 }
864 }
865 return dnp;
866}
867
868#if ENABLE_FEATURE_LS_SORTFILES
869static int sortcmp(const void *a, const void *b)
870{
871 struct dnode *d1 = *(struct dnode **)a;
872 struct dnode *d2 = *(struct dnode **)b;
873 unsigned sort_opts = G.all_fmt & SORT_MASK;
874 off_t dif;
875
876 dif = 0; /* assume SORT_NAME */
877 // TODO: use pre-initialized function pointer
878 // instead of branch forest
879 if (sort_opts == SORT_SIZE) {
880 dif = (d2->dn_size - d1->dn_size);
881 } else
882 if (sort_opts == SORT_ATIME) {
883 dif = (d2->dn_atime - d1->dn_atime);
884 } else
885 if (sort_opts == SORT_CTIME) {
886 dif = (d2->dn_ctime - d1->dn_ctime);
887 } else
888 if (sort_opts == SORT_MTIME) {
889 dif = (d2->dn_mtime - d1->dn_mtime);
890 } else
891 if (sort_opts == SORT_DIR) {
892 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
893 } else
894#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
895 if (sort_opts == SORT_VERSION) {
896 dif = strverscmp(d1->name, d2->name);
897 } else
898#endif
899 if (sort_opts == SORT_EXT) {
900 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
901 }
902 if (dif == 0) {
903 /* sort by name, use as tie breaker for other sorts */
904 if (ENABLE_LOCALE_SUPPORT)
905 dif = strcoll(d1->name, d2->name);
906 else
907 dif = strcmp(d1->name, d2->name);
908 }
909
910 /* Make dif fit into an int */
911 if (sizeof(dif) > sizeof(int)) {
912 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
913 /* shift leaving only "int" worth of bits */
914 if (dif != 0) {
915 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
916 }
917 }
918
919 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
920}
921
922static void dnsort(struct dnode **dn, int size)
923{
924 qsort(dn, size, sizeof(*dn), sortcmp);
925}
926
927static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
928{
929 dnsort(dn, nfiles);
930 display_files(dn, nfiles);
931}
932#else
933# define dnsort(dn, size) ((void)0)
934# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
935#endif
936
937/* Returns NULL-terminated malloced vector of pointers (or NULL) */
938static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
939{
940 struct dnode *dn, *cur, **dnp;
941 struct dirent *entry;
942 DIR *dir;
943 unsigned i, nfiles;
944
945 *nfiles_p = 0;
946 dir = warn_opendir(path);
947 if (dir == NULL) {
948 G.exit_code = EXIT_FAILURE;
949 return NULL; /* could not open the dir */
950 }
951 dn = NULL;
952 nfiles = 0;
953 while ((entry = readdir(dir)) != NULL) {
954 char *fullname;
955
956 /* are we going to list the file- it may be . or .. or a hidden file */
957 if (entry->d_name[0] == '.') {
958 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
959 && !(G.all_fmt & DISP_DOT)
960 ) {
961 continue;
962 }
963 if (!(G.all_fmt & DISP_HIDDEN))
964 continue;
965 }
966 fullname = concat_path_file(path, entry->d_name);
967 cur = my_stat(fullname, bb_basename(fullname), 0);
968 if (!cur) {
969 free(fullname);
970 continue;
971 }
972 cur->fname_allocated = 1;
973 cur->dn_next = dn;
974 dn = cur;
975 nfiles++;
976 }
977 closedir(dir);
978
979 if (dn == NULL)
980 return NULL;
981
982 /* now that we know how many files there are
983 * allocate memory for an array to hold dnode pointers
984 */
985 *nfiles_p = nfiles;
986 dnp = dnalloc(nfiles);
987 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
988 dnp[i] = dn; /* save pointer to node in array */
989 dn = dn->dn_next;
990 if (!dn)
991 break;
992 }
993
994 return dnp;
995}
996
997#if ENABLE_DESKTOP
998/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
999 * If any of the -l, -n, -s options is specified, each list
1000 * of files within the directory shall be preceded by a
1001 * status line indicating the number of file system blocks
1002 * occupied by files in the directory in 512-byte units if
1003 * the -k option is not specified, or 1024-byte units if the
1004 * -k option is specified, rounded up to the next integral
1005 * number of units.
1006 */
1007/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1008static off_t calculate_blocks(struct dnode **dn)
1009{
1010 uoff_t blocks = 1;
1011 if (dn) {
1012 while (*dn) {
1013 /* st_blocks is in 512 byte blocks */
1014 blocks += (*dn)->dn_blocks;
1015 dn++;
1016 }
1017 }
1018
1019 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1020 /* Actually, we round up by calculating (blocks + 1) / 2,
1021 * "+ 1" was done when we initialized blocks to 1 */
1022 return blocks >> 1;
1023}
1024#endif
1025
1026static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1027{
1028 unsigned nfiles;
1029 struct dnode **subdnp;
1030
1031 for (; *dn; dn++) {
1032 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
1033 if (!first)
1034 bb_putchar('\n');
1035 first = 0;
1036 printf("%s:\n", (*dn)->fullname);
1037 }
1038 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1039#if ENABLE_DESKTOP
1040 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
1041 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1042#endif
1043 if (nfiles > 0) {
1044 /* list all files at this level */
1045 sort_and_display_files(subdnp, nfiles);
1046
1047 if (ENABLE_FEATURE_LS_RECURSIVE
1048 && (G.all_fmt & DISP_RECURSIVE)
1049 ) {
1050 struct dnode **dnd;
1051 unsigned dndirs;
1052 /* recursive - list the sub-dirs */
1053 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1054 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1055 if (dndirs > 0) {
1056 dnsort(dnd, dndirs);
1057 scan_and_display_dirs_recur(dnd, 0);
1058 /* free the array of dnode pointers to the dirs */
1059 free(dnd);
1060 }
1061 }
1062 /* free the dnodes and the fullname mem */
1063 dfree(subdnp);
1064 }
1065 }
1066}
1067
1068
1069int ls_main(int argc UNUSED_PARAM, char **argv)
1070{
1071 struct dnode **dnd;
1072 struct dnode **dnf;
1073 struct dnode **dnp;
1074 struct dnode *dn;
1075 struct dnode *cur;
1076 unsigned opt;
1077 unsigned nfiles;
1078 unsigned dnfiles;
1079 unsigned dndirs;
1080 unsigned i;
1081#if ENABLE_FEATURE_LS_COLOR
1082 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1083 /* coreutils 6.10:
1084 * # ls --color=BOGUS
1085 * ls: invalid argument 'BOGUS' for '--color'
1086 * Valid arguments are:
1087 * 'always', 'yes', 'force'
1088 * 'never', 'no', 'none'
1089 * 'auto', 'tty', 'if-tty'
1090 * (and substrings: "--color=alwa" work too)
1091 */
1092 static const char ls_longopts[] ALIGN1 =
1093 "color\0" Optional_argument "\xff"; /* no short equivalent */
1094 static const char color_str[] ALIGN1 =
1095 "always\0""yes\0""force\0"
1096 "auto\0""tty\0""if-tty\0";
1097 /* need to initialize since --color has _an optional_ argument */
1098 const char *color_opt = color_str; /* "always" */
1099#endif
1100
1101 INIT_G();
1102
1103 init_unicode();
1104
1105 if (ENABLE_FEATURE_LS_SORTFILES)
1106 G.all_fmt = SORT_NAME;
1107
1108#if ENABLE_FEATURE_AUTOWIDTH
1109 /* obtain the terminal width */
1110 G_terminal_width = get_terminal_width(STDIN_FILENO);
1111 /* go one less... */
1112 G_terminal_width--;
1113#endif
1114
1115 /* process options */
1116 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
1117 opt_complementary =
1118 /* -e implies -l */
1119 IF_FEATURE_LS_TIMESTAMPS("el")
1120 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1121 * in some pairs of opts, only last one takes effect:
1122 */
1123 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1124 // ":m-l:l-m" - we don't have -m
1125 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1126 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1127 ":C-1:1-C" /* bycols/oneline */
1128 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1129 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1130 /* -w NUM: */
1131 IF_FEATURE_AUTOWIDTH(":w+");
1132 opt = getopt32(argv, ls_options
1133 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1134 IF_FEATURE_LS_COLOR(, &color_opt)
1135 );
1136 for (i = 0; opt_flags[i] != (1U << 31); i++) {
1137 if (opt & (1 << i)) {
1138 uint32_t flags = opt_flags[i];
1139
1140 if (flags & STYLE_MASK)
1141 G.all_fmt &= ~STYLE_MASK;
1142 if (flags & SORT_MASK)
1143 G.all_fmt &= ~SORT_MASK;
1144 if (flags & TIME_MASK)
1145 G.all_fmt &= ~TIME_MASK;
1146
1147 G.all_fmt |= flags;
1148 }
1149 }
1150
1151#if ENABLE_FEATURE_LS_COLOR
1152 /* set G_show_color = 1/0 */
1153 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1154 char *p = getenv("LS_COLORS");
1155 /* LS_COLORS is unset, or (not empty && not "none") ? */
1156 if (!p || (p[0] && strcmp(p, "none") != 0))
1157 G_show_color = 1;
1158 }
1159 if (opt & OPT_color) {
1160 if (color_opt[0] == 'n')
1161 G_show_color = 0;
1162 else switch (index_in_substrings(color_str, color_opt)) {
1163 case 3:
1164 case 4:
1165 case 5:
1166 if (isatty(STDOUT_FILENO)) {
1167 case 0:
1168 case 1:
1169 case 2:
1170 G_show_color = 1;
1171 }
1172 }
1173 }
1174#endif
1175
1176 /* sort out which command line options take precedence */
1177 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1178 G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
1179 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1180 if (G.all_fmt & TIME_CHANGE)
1181 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1182 if (G.all_fmt & TIME_ACCESS)
1183 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1184 }
1185 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1186 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1187
1188 /* choose a display format if one was not already specified by an option */
1189 if (!(G.all_fmt & STYLE_MASK))
1190 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1191
1192 argv += optind;
1193 if (!argv[0])
1194 *--argv = (char*)".";
1195
1196 if (argv[1])
1197 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1198
1199 /* stuff the command line file names into a dnode array */
1200 dn = NULL;
1201 nfiles = 0;
1202 do {
1203 cur = my_stat(*argv, *argv,
1204 /* follow links on command line unless -l, -s or -F: */
1205 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1206 || (G.all_fmt & LIST_BLOCKS)
1207 || (option_mask32 & OPT_F)
1208 )
1209 /* ... or if -H: */
1210 || (option_mask32 & OPT_H)
1211 /* ... or if -L, but my_stat always follows links if -L */
1212 );
1213 argv++;
1214 if (!cur)
1215 continue;
1216 /*cur->fname_allocated = 0; - already is */
1217 cur->dn_next = dn;
1218 dn = cur;
1219 nfiles++;
1220 } while (*argv);
1221
1222 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1223 if (nfiles == 0)
1224 return G.exit_code;
1225
1226 /* now that we know how many files there are
1227 * allocate memory for an array to hold dnode pointers
1228 */
1229 dnp = dnalloc(nfiles);
1230 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1231 dnp[i] = dn; /* save pointer to node in array */
1232 dn = dn->dn_next;
1233 if (!dn)
1234 break;
1235 }
1236
1237 if (G.all_fmt & DISP_NOLIST) {
1238 sort_and_display_files(dnp, nfiles);
1239 } else {
1240 dnd = splitdnarray(dnp, SPLIT_DIR);
1241 dnf = splitdnarray(dnp, SPLIT_FILE);
1242 dndirs = count_dirs(dnp, SPLIT_DIR);
1243 dnfiles = nfiles - dndirs;
1244 if (dnfiles > 0) {
1245 sort_and_display_files(dnf, dnfiles);
1246 if (ENABLE_FEATURE_CLEAN_UP)
1247 free(dnf);
1248 }
1249 if (dndirs > 0) {
1250 dnsort(dnd, dndirs);
1251 scan_and_display_dirs_recur(dnd, dnfiles == 0);
1252 if (ENABLE_FEATURE_CLEAN_UP)
1253 free(dnd);
1254 }
1255 }
1256
1257 if (ENABLE_FEATURE_CLEAN_UP)
1258 dfree(dnp);
1259 return G.exit_code;
1260}
Note: See TracBrowser for help on using the repository browser.