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