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