source: MondoRescue/branches/3.2/mindi-busybox/coreutils/ls.c@ 3232

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