]>
Commit | Line | Data |
---|---|---|
44a7a5ab A |
1 | /* |
2 | * Copyright (c) 1989, 1993, 1994 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Michael Fischbein. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by the University of | |
19 | * California, Berkeley and its contributors. | |
20 | * 4. Neither the name of the University nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | */ | |
36 | ||
44a7a5ab | 37 | #ifndef lint |
6c780a1f A |
38 | static const char copyright[] = |
39 | "@(#) Copyright (c) 1989, 1993, 1994\n\ | |
40 | The Regents of the University of California. All rights reserved.\n"; | |
44a7a5ab A |
41 | #endif /* not lint */ |
42 | ||
44a7a5ab | 43 | #if 0 |
6c780a1f A |
44 | #ifndef lint |
45 | static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; | |
44a7a5ab | 46 | #endif /* not lint */ |
6c780a1f A |
47 | #endif |
48 | #include <sys/cdefs.h> | |
49 | __RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $"); | |
44a7a5ab A |
50 | |
51 | #include <sys/types.h> | |
52 | #include <sys/stat.h> | |
53 | #include <sys/ioctl.h> | |
44a7a5ab A |
54 | |
55 | #include <dirent.h> | |
56 | #include <err.h> | |
57 | #include <errno.h> | |
58 | #include <fts.h> | |
6c780a1f A |
59 | #include <grp.h> |
60 | #include <limits.h> | |
61 | #include <locale.h> | |
62 | #include <pwd.h> | |
44a7a5ab A |
63 | #include <stdio.h> |
64 | #include <stdlib.h> | |
65 | #include <string.h> | |
66 | #include <unistd.h> | |
6c780a1f A |
67 | #ifdef COLORLS |
68 | #include <termcap.h> | |
69 | #include <signal.h> | |
70 | #endif | |
c59d3020 A |
71 | #ifdef __APPLE__ |
72 | #include <get_compat.h> | |
73 | #else | |
74 | #define COMPAT_MODE(a,b) (1) | |
75 | #endif /* __APPLE__ */ | |
44a7a5ab A |
76 | #include "ls.h" |
77 | #include "extern.h" | |
78 | ||
6c780a1f A |
79 | /* |
80 | * Upward approximation of the maximum number of characters needed to | |
81 | * represent a value of integral type t as a string, excluding the | |
82 | * NUL terminator, with provision for a sign. | |
83 | */ | |
84 | #define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) | |
44a7a5ab | 85 | |
6c780a1f | 86 | static void display(FTSENT *, FTSENT *); |
864a4b6e A |
87 | static u_quad_t makenines(u_quad_t); |
88 | static int mastercmp(const FTSENT **, const FTSENT **); | |
6c780a1f | 89 | static void traverse(int, char **, int); |
44a7a5ab | 90 | |
6c780a1f A |
91 | static void (*printfcn)(DISPLAY *); |
92 | static int (*sortfcn)(const FTSENT *, const FTSENT *); | |
44a7a5ab A |
93 | |
94 | long blocksize; /* block size units */ | |
95 | int termwidth = 80; /* default terminal width */ | |
44a7a5ab A |
96 | |
97 | /* flags */ | |
6c780a1f | 98 | int f_accesstime; /* use time of last access */ |
40bf83fe | 99 | int f_birthtime; /* use time of file birth */ |
6c780a1f A |
100 | int f_flags; /* show flags associated with a file */ |
101 | int f_humanval; /* show human-readable file sizes */ | |
102 | int f_inode; /* print inode */ | |
103 | static int f_kblocks; /* print size in kilobytes */ | |
104 | static int f_listdir; /* list actual directory, not contents */ | |
105 | static int f_listdot; /* list files beginning with . */ | |
106 | int f_longform; /* long listing format */ | |
107 | int f_nonprint; /* show unprintables as ? */ | |
108 | static int f_nosort; /* don't sort output */ | |
109 | int f_notabs; /* don't use tab-separated multi-col output */ | |
864a4b6e | 110 | int f_numericonly; /* don't convert uid/gid to name */ |
6c780a1f A |
111 | int f_octal; /* show unprintables as \xxx */ |
112 | int f_octal_escape; /* like f_octal but use C escapes if possible */ | |
113 | static int f_recursive; /* ls subdirectories also */ | |
114 | static int f_reversesort; /* reverse whatever sort is used */ | |
115 | int f_sectime; /* print the real time for all files */ | |
116 | static int f_singlecol; /* use single column output */ | |
117 | int f_size; /* list size in short listing */ | |
118 | int f_slash; /* similar to f_type, but only for dirs */ | |
119 | int f_sortacross; /* sort across rows, not down columns */ | |
120 | int f_statustime; /* use time of last mode change */ | |
121 | int f_stream; /* stream the output, separate with commas */ | |
122 | static int f_timesort; /* sort by time vice name */ | |
123 | static int f_sizesort; /* sort by size */ | |
124 | int f_type; /* add type character for non-regular files */ | |
125 | static int f_whiteout; /* show whiteout entries */ | |
c59d3020 | 126 | int f_acl; /* show ACLs in long listing */ |
864a4b6e | 127 | int f_xattr; /* show extended attributes in long listing */ |
c59d3020 | 128 | int f_group; /* show group */ |
864a4b6e | 129 | int f_owner; /* show owner */ |
6c780a1f A |
130 | #ifdef COLORLS |
131 | int f_color; /* add type in color for non-regular files */ | |
132 | ||
133 | char *ansi_bgcol; /* ANSI sequence to set background colour */ | |
134 | char *ansi_fgcol; /* ANSI sequence to set foreground colour */ | |
135 | char *ansi_coloff; /* ANSI sequence to reset colours */ | |
136 | char *attrs_off; /* ANSI sequence to turn off attributes */ | |
137 | char *enter_bold; /* ANSI sequence to set color to bold mode */ | |
138 | #endif | |
139 | ||
140 | static int rval; | |
44a7a5ab A |
141 | |
142 | int | |
6c780a1f | 143 | main(int argc, char *argv[]) |
44a7a5ab | 144 | { |
6c780a1f | 145 | static char dot[] = ".", *dotav[] = {dot, NULL}; |
44a7a5ab A |
146 | struct winsize win; |
147 | int ch, fts_options, notused; | |
6c780a1f A |
148 | char *p; |
149 | #ifdef COLORLS | |
150 | char termcapbuf[1024]; /* termcap definition buffer */ | |
151 | char tcapbuf[512]; /* capability buffer */ | |
152 | char *bp = tcapbuf; | |
153 | #endif | |
154 | ||
864a4b6e A |
155 | if (argc < 1) |
156 | usage(); | |
6c780a1f | 157 | (void)setlocale(LC_ALL, ""); |
44a7a5ab A |
158 | |
159 | /* Terminal defaults to -Cq, non-terminal defaults to -1. */ | |
160 | if (isatty(STDOUT_FILENO)) { | |
6c780a1f A |
161 | termwidth = 80; |
162 | if ((p = getenv("COLUMNS")) != NULL && *p != '\0') | |
44a7a5ab | 163 | termwidth = atoi(p); |
6c780a1f | 164 | else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && |
44a7a5ab A |
165 | win.ws_col > 0) |
166 | termwidth = win.ws_col; | |
6c780a1f A |
167 | f_nonprint = 1; |
168 | } else { | |
44a7a5ab | 169 | f_singlecol = 1; |
6c780a1f A |
170 | /* retrieve environment variable, in case of explicit -C */ |
171 | p = getenv("COLUMNS"); | |
172 | if (p) | |
173 | termwidth = atoi(p); | |
174 | } | |
44a7a5ab A |
175 | |
176 | /* Root is -A automatically. */ | |
177 | if (!getuid()) | |
178 | f_listdot = 1; | |
179 | ||
180 | fts_options = FTS_PHYSICAL; | |
40bf83fe | 181 | while ((ch = getopt(argc, argv, "1@ABCFGHLOPRSTUWabcdefghiklmnopqrstuvwx")) |
6c780a1f | 182 | != -1) { |
44a7a5ab A |
183 | switch (ch) { |
184 | /* | |
6c780a1f A |
185 | * The -1, -C, -x and -l options all override each other so |
186 | * shell aliasing works right. | |
44a7a5ab A |
187 | */ |
188 | case '1': | |
189 | f_singlecol = 1; | |
6c780a1f A |
190 | f_longform = 0; |
191 | f_stream = 0; | |
192 | break; | |
193 | case 'B': | |
194 | f_nonprint = 0; | |
195 | f_octal = 1; | |
196 | f_octal_escape = 0; | |
44a7a5ab A |
197 | break; |
198 | case 'C': | |
6c780a1f | 199 | f_sortacross = f_longform = f_singlecol = 0; |
44a7a5ab A |
200 | break; |
201 | case 'l': | |
202 | f_longform = 1; | |
6c780a1f A |
203 | f_singlecol = 0; |
204 | f_stream = 0; | |
44a7a5ab A |
205 | break; |
206 | case 'x': | |
6c780a1f A |
207 | f_sortacross = 1; |
208 | f_longform = 0; | |
209 | f_singlecol = 0; | |
44a7a5ab A |
210 | break; |
211 | /* The -c and -u options override each other. */ | |
212 | case 'c': | |
213 | f_statustime = 1; | |
40bf83fe | 214 | f_accesstime = f_birthtime = 0; |
44a7a5ab A |
215 | break; |
216 | case 'u': | |
217 | f_accesstime = 1; | |
40bf83fe A |
218 | f_statustime = f_birthtime = 0; |
219 | break; | |
220 | case 'U': | |
221 | f_birthtime = 1; | |
222 | f_statustime = f_accesstime = 0; | |
44a7a5ab A |
223 | break; |
224 | case 'F': | |
225 | f_type = 1; | |
6c780a1f A |
226 | f_slash = 0; |
227 | break; | |
228 | case 'H': | |
864a4b6e A |
229 | if (COMPAT_MODE("bin/ls", "Unix2003")) { |
230 | fts_options &= ~FTS_LOGICAL; | |
231 | fts_options |= FTS_PHYSICAL; | |
232 | fts_options |= FTS_COMFOLLOWDIR; | |
233 | } else | |
234 | fts_options |= FTS_COMFOLLOW; | |
6c780a1f A |
235 | break; |
236 | case 'G': | |
237 | setenv("CLICOLOR", "", 1); | |
44a7a5ab A |
238 | break; |
239 | case 'L': | |
240 | fts_options &= ~FTS_PHYSICAL; | |
241 | fts_options |= FTS_LOGICAL; | |
864a4b6e A |
242 | if (COMPAT_MODE("bin/ls", "Unix2003")) { |
243 | fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR); | |
244 | } | |
44a7a5ab | 245 | break; |
6c780a1f | 246 | case 'P': |
864a4b6e | 247 | fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR); |
6c780a1f A |
248 | fts_options &= ~FTS_LOGICAL; |
249 | fts_options |= FTS_PHYSICAL; | |
250 | break; | |
44a7a5ab A |
251 | case 'R': |
252 | f_recursive = 1; | |
253 | break; | |
254 | case 'a': | |
255 | fts_options |= FTS_SEEDOT; | |
256 | /* FALLTHROUGH */ | |
257 | case 'A': | |
258 | f_listdot = 1; | |
259 | break; | |
260 | /* The -d option turns off the -R option. */ | |
261 | case 'd': | |
262 | f_listdir = 1; | |
263 | f_recursive = 0; | |
264 | break; | |
265 | case 'f': | |
266 | f_nosort = 1; | |
864a4b6e A |
267 | if (COMPAT_MODE("bin/ls", "Unix2003")) { |
268 | fts_options |= FTS_SEEDOT; | |
269 | f_listdot = 1; | |
270 | } | |
44a7a5ab | 271 | break; |
c59d3020 A |
272 | case 'g': /* Compatibility with Unix03 */ |
273 | if (COMPAT_MODE("bin/ls", "Unix2003")) { | |
274 | f_group = 1; | |
275 | f_longform = 1; | |
276 | f_singlecol = 0; | |
277 | f_stream = 0; | |
278 | } | |
6c780a1f A |
279 | break; |
280 | case 'h': | |
281 | f_humanval = 1; | |
44a7a5ab A |
282 | break; |
283 | case 'i': | |
284 | f_inode = 1; | |
285 | break; | |
286 | case 'k': | |
6c780a1f A |
287 | f_kblocks = 1; |
288 | break; | |
289 | case 'm': | |
290 | f_stream = 1; | |
291 | f_singlecol = 0; | |
292 | f_longform = 0; | |
44a7a5ab A |
293 | break; |
294 | case 'n': | |
295 | f_numericonly = 1; | |
864a4b6e A |
296 | if (COMPAT_MODE("bin/ls", "Unix2003")) { |
297 | f_longform = 1; | |
298 | f_singlecol = 0; | |
299 | f_stream = 0; | |
300 | } | |
44a7a5ab A |
301 | break; |
302 | case 'o': | |
864a4b6e A |
303 | if (COMPAT_MODE("bin/ls", "Unix2003")) { |
304 | f_owner = 1; | |
305 | f_longform = 1; | |
306 | f_singlecol = 0; | |
307 | f_stream = 0; | |
308 | } else { | |
309 | f_flags = 1; | |
310 | } | |
44a7a5ab | 311 | break; |
6c780a1f A |
312 | case 'p': |
313 | f_slash = 1; | |
314 | f_type = 1; | |
315 | break; | |
44a7a5ab A |
316 | case 'q': |
317 | f_nonprint = 1; | |
6c780a1f A |
318 | f_octal = 0; |
319 | f_octal_escape = 0; | |
44a7a5ab A |
320 | break; |
321 | case 'r': | |
322 | f_reversesort = 1; | |
323 | break; | |
324 | case 'S': | |
6c780a1f A |
325 | /* Darwin 1.4.1 compatibility */ |
326 | f_sizesort = 1; | |
44a7a5ab A |
327 | break; |
328 | case 's': | |
329 | f_size = 1; | |
330 | break; | |
331 | case 'T': | |
332 | f_sectime = 1; | |
333 | break; | |
334 | case 't': | |
6c780a1f | 335 | f_timesort = 1; |
44a7a5ab A |
336 | break; |
337 | case 'W': | |
338 | f_whiteout = 1; | |
339 | break; | |
340 | case 'v': | |
6c780a1f A |
341 | /* Darwin 1.4.1 compatibility */ |
342 | f_nonprint = 0; | |
343 | break; | |
344 | case 'b': | |
345 | f_nonprint = 0; | |
346 | f_octal = 0; | |
347 | f_octal_escape = 1; | |
348 | break; | |
349 | case 'w': | |
44a7a5ab | 350 | f_nonprint = 0; |
6c780a1f A |
351 | f_octal = 0; |
352 | f_octal_escape = 0; | |
353 | break; | |
c59d3020 A |
354 | case 'e': |
355 | f_acl = 1; | |
44a7a5ab | 356 | break; |
864a4b6e A |
357 | case '@': |
358 | f_xattr = 1; | |
359 | break; | |
360 | case 'O': | |
361 | f_flags = 1; | |
362 | break; | |
44a7a5ab A |
363 | default: |
364 | case '?': | |
365 | usage(); | |
366 | } | |
367 | } | |
368 | argc -= optind; | |
369 | argv += optind; | |
370 | ||
6c780a1f A |
371 | /* Enabling of colours is conditional on the environment. */ |
372 | if (getenv("CLICOLOR") && | |
373 | (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) | |
374 | #ifdef COLORLS | |
375 | if (tgetent(termcapbuf, getenv("TERM")) == 1) { | |
376 | ansi_fgcol = tgetstr("AF", &bp); | |
377 | ansi_bgcol = tgetstr("AB", &bp); | |
378 | attrs_off = tgetstr("me", &bp); | |
379 | enter_bold = tgetstr("md", &bp); | |
380 | ||
381 | /* To switch colours off use 'op' if | |
382 | * available, otherwise use 'oc', or | |
383 | * don't do colours at all. */ | |
384 | ansi_coloff = tgetstr("op", &bp); | |
385 | if (!ansi_coloff) | |
386 | ansi_coloff = tgetstr("oc", &bp); | |
387 | if (ansi_fgcol && ansi_bgcol && ansi_coloff) | |
388 | f_color = 1; | |
389 | } | |
390 | #else | |
391 | (void)fprintf(stderr, "Color support not compiled in.\n"); | |
392 | #endif /*COLORLS*/ | |
393 | ||
394 | #ifdef COLORLS | |
395 | if (f_color) { | |
396 | /* | |
397 | * We can't put tabs and color sequences together: | |
398 | * column number will be incremented incorrectly | |
399 | * for "stty oxtabs" mode. | |
400 | */ | |
401 | f_notabs = 1; | |
402 | (void)signal(SIGINT, colorquit); | |
403 | (void)signal(SIGQUIT, colorquit); | |
404 | parsecolors(getenv("LSCOLORS")); | |
405 | } | |
406 | #endif | |
407 | ||
44a7a5ab | 408 | /* |
6c780a1f A |
409 | * If not -F, -i, -l, -s or -t options, don't require stat |
410 | * information, unless in color mode in which case we do | |
411 | * need this to determine which colors to display. | |
44a7a5ab | 412 | */ |
6c780a1f A |
413 | if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type && !f_sizesort |
414 | #ifdef COLORLS | |
415 | && !f_color | |
416 | #endif | |
417 | ) | |
44a7a5ab A |
418 | fts_options |= FTS_NOSTAT; |
419 | ||
420 | /* | |
421 | * If not -F, -d or -l options, follow any symbolic links listed on | |
422 | * the command line. | |
423 | */ | |
864a4b6e | 424 | if (!f_longform && !f_listdir && !f_type && !f_inode) |
44a7a5ab A |
425 | fts_options |= FTS_COMFOLLOW; |
426 | ||
427 | /* | |
428 | * If -W, show whiteout entries | |
429 | */ | |
430 | #ifdef FTS_WHITEOUT | |
431 | if (f_whiteout) | |
432 | fts_options |= FTS_WHITEOUT; | |
433 | #endif | |
434 | ||
435 | /* If -l or -s, figure out block size. */ | |
436 | if (f_longform || f_size) { | |
6c780a1f A |
437 | if (f_kblocks) |
438 | blocksize = 2; | |
439 | else { | |
44a7a5ab | 440 | (void)getbsize(¬used, &blocksize); |
6c780a1f A |
441 | blocksize /= 512; |
442 | } | |
44a7a5ab | 443 | } |
44a7a5ab A |
444 | /* Select a sort function. */ |
445 | if (f_reversesort) { | |
6c780a1f | 446 | if (f_sizesort) |
44a7a5ab | 447 | sortfcn = revsizecmp; |
6c780a1f A |
448 | else if (!f_timesort) |
449 | sortfcn = revnamecmp; | |
450 | else if (f_accesstime) | |
451 | sortfcn = revacccmp; | |
452 | else if (f_statustime) | |
453 | sortfcn = revstatcmp; | |
40bf83fe A |
454 | else if (f_birthtime) |
455 | sortfcn = revbirthcmp; | |
6c780a1f A |
456 | else /* Use modification time. */ |
457 | sortfcn = revmodcmp; | |
44a7a5ab | 458 | } else { |
6c780a1f | 459 | if (f_sizesort) |
44a7a5ab | 460 | sortfcn = sizecmp; |
6c780a1f A |
461 | else if (!f_timesort) |
462 | sortfcn = namecmp; | |
463 | else if (f_accesstime) | |
464 | sortfcn = acccmp; | |
465 | else if (f_statustime) | |
466 | sortfcn = statcmp; | |
40bf83fe A |
467 | else if (f_birthtime) |
468 | sortfcn = birthcmp; | |
6c780a1f A |
469 | else /* Use modification time. */ |
470 | sortfcn = modcmp; | |
44a7a5ab A |
471 | } |
472 | ||
473 | /* Select a print function. */ | |
474 | if (f_singlecol) | |
475 | printfcn = printscol; | |
44a7a5ab A |
476 | else if (f_longform) |
477 | printfcn = printlong; | |
6c780a1f A |
478 | else if (f_stream) |
479 | printfcn = printstream; | |
44a7a5ab A |
480 | else |
481 | printfcn = printcol; | |
482 | ||
483 | if (argc) | |
484 | traverse(argc, argv, fts_options); | |
485 | else | |
486 | traverse(1, dotav, fts_options); | |
6c780a1f | 487 | exit(rval); |
44a7a5ab A |
488 | } |
489 | ||
6c780a1f | 490 | static int output; /* If anything output. */ |
44a7a5ab A |
491 | |
492 | /* | |
493 | * Traverse() walks the logical directory structure specified by the argv list | |
494 | * in the order specified by the mastercmp() comparison function. During the | |
495 | * traversal it passes linked lists of structures to display() which represent | |
496 | * a superset (may be exact set) of the files to be displayed. | |
497 | */ | |
498 | static void | |
6c780a1f | 499 | traverse(int argc, char *argv[], int options) |
44a7a5ab A |
500 | { |
501 | FTS *ftsp; | |
502 | FTSENT *p, *chp; | |
864a4b6e | 503 | int ch_options, error; |
44a7a5ab A |
504 | |
505 | if ((ftsp = | |
506 | fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) | |
6c780a1f | 507 | err(1, "fts_open"); |
44a7a5ab A |
508 | |
509 | display(NULL, fts_children(ftsp, 0)); | |
864a4b6e A |
510 | if (f_listdir) { |
511 | fts_close(ftsp); | |
44a7a5ab | 512 | return; |
864a4b6e | 513 | } |
44a7a5ab A |
514 | |
515 | /* | |
516 | * If not recursing down this tree and don't need stat info, just get | |
517 | * the names. | |
518 | */ | |
519 | ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; | |
520 | ||
521 | while ((p = fts_read(ftsp)) != NULL) | |
522 | switch (p->fts_info) { | |
523 | case FTS_DC: | |
524 | warnx("%s: directory causes a cycle", p->fts_name); | |
864a4b6e A |
525 | if (COMPAT_MODE("bin/ls", "Unix2003")) { |
526 | rval = 1; | |
527 | } | |
44a7a5ab A |
528 | break; |
529 | case FTS_DNR: | |
530 | case FTS_ERR: | |
531 | warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); | |
6c780a1f | 532 | rval = 1; |
44a7a5ab A |
533 | break; |
534 | case FTS_D: | |
535 | if (p->fts_level != FTS_ROOTLEVEL && | |
536 | p->fts_name[0] == '.' && !f_listdot) | |
537 | break; | |
538 | ||
539 | /* | |
540 | * If already output something, put out a newline as | |
541 | * a separator. If multiple arguments, precede each | |
542 | * directory with its name. | |
543 | */ | |
44a7a5ab | 544 | if (output) |
6c780a1f | 545 | (void)printf("\n%s:\n", p->fts_path); |
44a7a5ab | 546 | else if (argc > 1) { |
6c780a1f | 547 | (void)printf("%s:\n", p->fts_path); |
44a7a5ab A |
548 | output = 1; |
549 | } | |
44a7a5ab | 550 | chp = fts_children(ftsp, ch_options); |
40bf83fe A |
551 | if (COMPAT_MODE("bin/ls", "Unix2003") && ((options & FTS_LOGICAL)!=0)) { |
552 | FTSENT *curr; | |
553 | for (curr = chp; curr; curr = curr->fts_link) { | |
554 | if (curr->fts_info == FTS_SLNONE) | |
555 | curr->fts_number = NO_PRINT; | |
556 | } | |
557 | } | |
44a7a5ab A |
558 | display(p, chp); |
559 | ||
560 | if (!f_recursive && chp != NULL) | |
561 | (void)fts_set(ftsp, p, FTS_SKIP); | |
562 | break; | |
864a4b6e A |
563 | case FTS_SLNONE: /* Same as default unless Unix conformance */ |
564 | if (COMPAT_MODE("bin/ls", "Unix2003")) { | |
565 | if ((options & FTS_LOGICAL)!=0) { /* -L was specified */ | |
40bf83fe A |
566 | warnx("%s: %s", p->fts_name, strerror(p->fts_errno ?: ENOENT)); |
567 | rval = 1; | |
864a4b6e A |
568 | } |
569 | } | |
570 | break; | |
6c780a1f A |
571 | default: |
572 | break; | |
44a7a5ab | 573 | } |
864a4b6e A |
574 | error = errno; |
575 | fts_close(ftsp); | |
576 | errno = error; | |
577 | ||
44a7a5ab A |
578 | if (errno) |
579 | err(1, "fts_read"); | |
580 | } | |
581 | ||
582 | /* | |
583 | * Display() takes a linked list of FTSENT structures and passes the list | |
584 | * along with any other necessary information to the print function. P | |
585 | * points to the parent directory of the display list. | |
586 | */ | |
587 | static void | |
6c780a1f | 588 | display(FTSENT *p, FTSENT *list) |
44a7a5ab A |
589 | { |
590 | struct stat *sp; | |
591 | DISPLAY d; | |
592 | FTSENT *cur; | |
593 | NAMES *np; | |
6c780a1f A |
594 | off_t maxsize; |
595 | u_int64_t btotal, maxblock; | |
864a4b6e A |
596 | u_long lattrlen, maxlen, maxnlink, maxlattr; |
597 | ino_t maxinode; | |
6c780a1f A |
598 | int bcfile, maxflags; |
599 | gid_t maxgroup; | |
600 | uid_t maxuser; | |
601 | size_t flen, ulen, glen; | |
602 | char *initmax; | |
603 | int entries, needstats; | |
44a7a5ab | 604 | const char *user, *group; |
6c780a1f A |
605 | char *flags, *lattr = NULL; |
606 | char buf[STRBUF_SIZEOF(u_quad_t) + 1]; | |
607 | char ngroup[STRBUF_SIZEOF(uid_t) + 1]; | |
608 | char nuser[STRBUF_SIZEOF(gid_t) + 1]; | |
44a7a5ab A |
609 | |
610 | /* | |
611 | * If list is NULL there are two possibilities: that the parent | |
612 | * directory p has no children, or that fts_children() returned an | |
613 | * error. We ignore the error case since it will be replicated | |
614 | * on the next call to fts_read() on the post-order visit to the | |
6c780a1f | 615 | * directory p, and will be signaled in traverse(). |
44a7a5ab A |
616 | */ |
617 | if (list == NULL) | |
618 | return; | |
619 | ||
620 | needstats = f_inode || f_longform || f_size; | |
6c780a1f A |
621 | btotal = 0; |
622 | initmax = getenv("LS_COLWIDTHS"); | |
623 | /* Fields match -lios order. New ones should be added at the end. */ | |
624 | maxlattr = maxblock = maxinode = maxlen = maxnlink = | |
625 | maxuser = maxgroup = maxflags = maxsize = 0; | |
626 | if (initmax != NULL && *initmax != '\0') { | |
627 | char *initmax2, *jinitmax; | |
628 | int ninitmax; | |
629 | ||
630 | /* Fill-in "::" as "0:0:0" for the sake of scanf. */ | |
631 | jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); | |
632 | if (jinitmax == NULL) | |
633 | err(1, "malloc"); | |
634 | if (*initmax == ':') | |
635 | strcpy(initmax2, "0:"), initmax2 += 2; | |
636 | else | |
637 | *initmax2++ = *initmax, *initmax2 = '\0'; | |
638 | for (initmax++; *initmax != '\0'; initmax++) { | |
639 | if (initmax[-1] == ':' && initmax[0] == ':') { | |
640 | *initmax2++ = '0'; | |
641 | *initmax2++ = initmax[0]; | |
642 | initmax2[1] = '\0'; | |
643 | } else { | |
644 | *initmax2++ = initmax[0]; | |
645 | initmax2[1] = '\0'; | |
646 | } | |
647 | } | |
648 | if (initmax2[-1] == ':') | |
649 | strcpy(initmax2, "0"); | |
650 | ||
651 | ninitmax = sscanf(jinitmax, | |
864a4b6e A |
652 | #if _DARWIN_FEATURE_64_BIT_INODE |
653 | " %llu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ", | |
654 | #else | |
6c780a1f | 655 | " %lu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ", |
864a4b6e | 656 | #endif |
6c780a1f A |
657 | &maxinode, &maxblock, &maxnlink, &maxuser, |
658 | &maxgroup, &maxflags, &maxsize, &maxlen, &maxlattr); | |
659 | f_notabs = 1; | |
660 | switch (ninitmax) { | |
661 | case 0: | |
662 | maxinode = 0; | |
663 | /* FALLTHROUGH */ | |
664 | case 1: | |
665 | maxblock = 0; | |
666 | /* FALLTHROUGH */ | |
667 | case 2: | |
668 | maxnlink = 0; | |
669 | /* FALLTHROUGH */ | |
670 | case 3: | |
671 | maxuser = 0; | |
672 | /* FALLTHROUGH */ | |
673 | case 4: | |
674 | maxgroup = 0; | |
675 | /* FALLTHROUGH */ | |
676 | case 5: | |
677 | maxflags = 0; | |
678 | /* FALLTHROUGH */ | |
679 | case 6: | |
680 | maxsize = 0; | |
681 | /* FALLTHROUGH */ | |
682 | case 7: | |
683 | maxlen = 0; | |
684 | /* FALLTHROUGH */ | |
685 | case 8: | |
686 | maxlattr = 0; | |
687 | /* FALLTHROUGH */ | |
688 | #ifdef COLORLS | |
689 | if (!f_color) | |
690 | #endif | |
691 | f_notabs = 0; | |
692 | /* FALLTHROUGH */ | |
693 | default: | |
694 | break; | |
695 | } | |
696 | maxinode = makenines(maxinode); | |
697 | maxblock = makenines(maxblock); | |
698 | maxnlink = makenines(maxnlink); | |
699 | maxsize = makenines(maxsize); | |
700 | } | |
44a7a5ab | 701 | bcfile = 0; |
6c780a1f | 702 | flags = NULL; |
44a7a5ab A |
703 | for (cur = list, entries = 0; cur; cur = cur->fts_link) { |
704 | if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { | |
705 | warnx("%s: %s", | |
706 | cur->fts_name, strerror(cur->fts_errno)); | |
707 | cur->fts_number = NO_PRINT; | |
6c780a1f | 708 | rval = 1; |
44a7a5ab A |
709 | continue; |
710 | } | |
44a7a5ab A |
711 | /* |
712 | * P is NULL if list is the argv list, to which different rules | |
713 | * apply. | |
714 | */ | |
715 | if (p == NULL) { | |
716 | /* Directories will be displayed later. */ | |
717 | if (cur->fts_info == FTS_D && !f_listdir) { | |
718 | cur->fts_number = NO_PRINT; | |
719 | continue; | |
720 | } | |
721 | } else { | |
722 | /* Only display dot file if -a/-A set. */ | |
723 | if (cur->fts_name[0] == '.' && !f_listdot) { | |
724 | cur->fts_number = NO_PRINT; | |
725 | continue; | |
726 | } | |
727 | } | |
728 | if (cur->fts_namelen > maxlen) | |
729 | maxlen = cur->fts_namelen; | |
6c780a1f A |
730 | if (f_octal || f_octal_escape) { |
731 | u_long t = len_octal(cur->fts_name, cur->fts_namelen); | |
732 | ||
733 | if (t > maxlen) | |
734 | maxlen = t; | |
735 | } | |
44a7a5ab A |
736 | if (needstats) { |
737 | sp = cur->fts_statp; | |
738 | if (sp->st_blocks > maxblock) | |
739 | maxblock = sp->st_blocks; | |
740 | if (sp->st_ino > maxinode) | |
741 | maxinode = sp->st_ino; | |
742 | if (sp->st_nlink > maxnlink) | |
743 | maxnlink = sp->st_nlink; | |
744 | if (sp->st_size > maxsize) | |
745 | maxsize = sp->st_size; | |
44a7a5ab A |
746 | |
747 | btotal += sp->st_blocks; | |
748 | if (f_longform) { | |
749 | if (f_numericonly) { | |
750 | (void)snprintf(nuser, sizeof(nuser), | |
751 | "%u", sp->st_uid); | |
752 | (void)snprintf(ngroup, sizeof(ngroup), | |
753 | "%u", sp->st_gid); | |
754 | user = nuser; | |
755 | group = ngroup; | |
756 | } else { | |
6c780a1f A |
757 | user = user_from_uid(sp->st_uid, 0); |
758 | group = group_from_gid(sp->st_gid, 0); | |
44a7a5ab A |
759 | } |
760 | if ((ulen = strlen(user)) > maxuser) | |
761 | maxuser = ulen; | |
762 | if ((glen = strlen(group)) > maxgroup) | |
763 | maxgroup = glen; | |
764 | if (f_flags) { | |
6c780a1f A |
765 | flags = fflagstostr(sp->st_flags); |
766 | if (flags != NULL && *flags == '\0') { | |
767 | free(flags); | |
768 | flags = strdup("-"); | |
769 | } | |
770 | if (flags == NULL) | |
771 | err(1, "fflagstostr"); | |
772 | flen = strlen(flags); | |
773 | if (flen > (size_t)maxflags) | |
44a7a5ab A |
774 | maxflags = flen; |
775 | } else | |
776 | flen = 0; | |
6c780a1f | 777 | lattr = NULL; |
c59d3020 A |
778 | lattrlen = 0; |
779 | ||
6c780a1f A |
780 | if ((np = malloc(sizeof(NAMES) + lattrlen + |
781 | ulen + glen + flen + 4)) == NULL) | |
782 | err(1, "malloc"); | |
44a7a5ab A |
783 | |
784 | np->user = &np->data[0]; | |
785 | (void)strcpy(np->user, user); | |
786 | np->group = &np->data[ulen + 1]; | |
787 | (void)strcpy(np->group, group); | |
788 | ||
6c780a1f A |
789 | if (S_ISCHR(sp->st_mode) || |
790 | S_ISBLK(sp->st_mode)) | |
791 | bcfile = 1; | |
792 | ||
44a7a5ab A |
793 | if (f_flags) { |
794 | np->flags = &np->data[ulen + glen + 2]; | |
6c780a1f A |
795 | (void)strcpy(np->flags, flags); |
796 | free(flags); | |
797 | } | |
44a7a5ab A |
798 | cur->fts_pointer = np; |
799 | } | |
800 | } | |
801 | ++entries; | |
802 | } | |
803 | ||
804 | if (!entries) | |
805 | return; | |
806 | ||
807 | d.list = list; | |
808 | d.entries = entries; | |
809 | d.maxlen = maxlen; | |
810 | if (needstats) { | |
6c780a1f | 811 | d.bcfile = bcfile; |
44a7a5ab | 812 | d.btotal = btotal; |
6c780a1f | 813 | (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxblock); |
44a7a5ab A |
814 | d.s_block = strlen(buf); |
815 | d.s_flags = maxflags; | |
6c780a1f | 816 | d.s_lattr = maxlattr; |
44a7a5ab | 817 | d.s_group = maxgroup; |
864a4b6e A |
818 | #if _DARWIN_FEATURE_64_BIT_INODE |
819 | (void)snprintf(buf, sizeof(buf), "%llu", maxinode); | |
820 | #else | |
6c780a1f | 821 | (void)snprintf(buf, sizeof(buf), "%lu", maxinode); |
864a4b6e | 822 | #endif |
44a7a5ab | 823 | d.s_inode = strlen(buf); |
6c780a1f | 824 | (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); |
44a7a5ab | 825 | d.s_nlink = strlen(buf); |
6c780a1f | 826 | (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxsize); |
44a7a5ab A |
827 | d.s_size = strlen(buf); |
828 | d.s_user = maxuser; | |
44a7a5ab | 829 | } |
44a7a5ab A |
830 | printfcn(&d); |
831 | output = 1; | |
832 | ||
833 | if (f_longform) | |
834 | for (cur = list; cur; cur = cur->fts_link) | |
835 | free(cur->fts_pointer); | |
836 | } | |
837 | ||
838 | /* | |
839 | * Ordering for mastercmp: | |
840 | * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories | |
841 | * as larger than directories. Within either group, use the sort function. | |
842 | * All other levels use the sort function. Error entries remain unsorted. | |
843 | */ | |
844 | static int | |
864a4b6e | 845 | mastercmp(const FTSENT **a, const FTSENT **b) |
44a7a5ab A |
846 | { |
847 | int a_info, b_info; | |
848 | ||
849 | a_info = (*a)->fts_info; | |
850 | if (a_info == FTS_ERR) | |
851 | return (0); | |
852 | b_info = (*b)->fts_info; | |
853 | if (b_info == FTS_ERR) | |
854 | return (0); | |
855 | ||
6c780a1f A |
856 | if (a_info == FTS_NS || b_info == FTS_NS) |
857 | return (namecmp(*a, *b)); | |
44a7a5ab | 858 | |
6c780a1f A |
859 | if (a_info != b_info && |
860 | (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { | |
44a7a5ab A |
861 | if (a_info == FTS_D) |
862 | return (1); | |
6c780a1f | 863 | if (b_info == FTS_D) |
44a7a5ab A |
864 | return (-1); |
865 | } | |
866 | return (sortfcn(*a, *b)); | |
867 | } | |
6c780a1f A |
868 | |
869 | /* | |
870 | * Makenines() returns (10**n)-1. This is useful for converting a width | |
871 | * into a number that wide in decimal. | |
872 | */ | |
873 | static u_quad_t | |
864a4b6e | 874 | makenines(u_quad_t n) |
6c780a1f A |
875 | { |
876 | u_long i; | |
877 | u_quad_t reg; | |
878 | ||
879 | reg = 1; | |
880 | /* Use a loop instead of pow(), since all values of n are small. */ | |
881 | for (i = 0; i < n; i++) | |
882 | reg *= 10; | |
883 | reg--; | |
884 | ||
885 | return reg; | |
886 | } |