add FTS_COMFOLLOW; document that the order of the files in the directory
[unix-history] / usr / src / bin / ls / ls.c
CommitLineData
bcf1365c 1/*
9ffe97fe
KB
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Michael Fischbein.
7 *
ae14fb92 8 * %sccs.include.redist.c%
bcf1365c 9 */
9ffe97fe
KB
10
11#ifndef lint
12char copyright[] =
13"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
14 All rights reserved.\n";
15#endif /* not lint */
16
17#ifndef lint
4db4a32d 18static char sccsid[] = "@(#)ls.c 5.55 (Berkeley) %G%";
9ffe97fe
KB
19#endif /* not lint */
20
f0a884e9 21#include <sys/types.h>
9ffe97fe 22#include <sys/stat.h>
4847d483 23#include <sys/ioctl.h>
0ce0ae04 24#include <unistd.h>
f0a884e9 25#include <fts.h>
0ce0ae04 26#include <stdlib.h>
6ebcb998 27#include <string.h>
9ffe97fe
KB
28#include <errno.h>
29#include <stdio.h>
b3f77fd1 30#include "ls.h"
0ce0ae04 31#include "extern.h"
2fd18a0a 32
4db4a32d
KB
33void display __P((int, FTSENT *, FTSENT *));
34int mastercmp __P((const FTSENT **, const FTSENT **));
35void traverse __P((int, char **, int));
f0a884e9
EA
36
37static void (*printfcn) __P((FTSENT *, int, u_long, int));
38static int (*sortfcn) __P((const FTSENT *, const FTSENT *));
bcf1365c 39
fb29be9b
KB
40int termwidth = 80; /* default terminal width */
41
9ffe97fe 42/* flags */
9ffe97fe 43int f_accesstime; /* use time of last access */
4845c19f 44int f_column; /* columnated format */
9ffe97fe 45int f_group; /* show group ownership of a file */
32980675 46int f_flags; /* show flags associated with a file */
9ffe97fe 47int f_inode; /* print inode */
a657fc85 48int f_kblocks; /* print size in kilobytes */
38eeee6d
KB
49int f_listdir; /* list actual directory, not contents */
50int f_listdot; /* list files beginning with . */
9ffe97fe 51int f_longform; /* long listing format */
4dfe4db2 52int f_newline; /* if precede with newline */
9ffe97fe 53int f_nonprint; /* show unprintables as ? */
9d5fbfa8 54int f_nosort; /* don't sort output */
9ffe97fe 55int f_recursive; /* ls subdirectories also */
38eeee6d 56int f_reversesort; /* reverse whatever sort is used */
926d5d7d 57int f_sectime; /* print the real time for all files */
38eeee6d 58int f_singlecol; /* use single column output */
9ffe97fe 59int f_size; /* list size in short listing */
38eeee6d 60int f_statustime; /* use time of last mode change */
4dfe4db2 61int f_dirname; /* if precede with directory name */
9ffe97fe 62int f_timesort; /* sort by time vice name */
38eeee6d 63int f_type; /* add type character for non-regular files */
2fd18a0a 64
4db4a32d 65int
9ffe97fe
KB
66main(argc, argv)
67 int argc;
f0a884e9 68 char *argv[];
547b0031 69{
4847d483 70 struct winsize win;
f0a884e9 71 int ch, fts_options;
0ce0ae04 72 char *p;
2fd18a0a 73
f0a884e9 74 /* Terminal defaults to -Cq, non-terminal defaults to -1. */
547b0031 75 if (isatty(1)) {
9ffe97fe 76 f_nonprint = 1;
fb29be9b
KB
77 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
78 if (p = getenv("COLUMNS"))
79 termwidth = atoi(p);
80 }
4847d483
KB
81 else
82 termwidth = win.ws_col;
4845c19f 83 f_column = 1;
9ffe97fe
KB
84 } else
85 f_singlecol = 1;
2fd18a0a 86
f0a884e9 87 /* Root is -A automatically. */
9ffe97fe 88 if (!getuid())
b3f77fd1 89 f_listdot = 1;
9ffe97fe 90
4db4a32d 91 fts_options = FTS_PHYSICAL;
32980675 92 while ((ch = getopt(argc, argv, "1ACFLRTacdfgikloqrstu")) != EOF) {
9ffe97fe 93 switch (ch) {
38eeee6d 94 /*
f0a884e9
EA
95 * The -1, -C and -l options all override each other so shell
96 * aliasing works right.
38eeee6d 97 */
9ffe97fe
KB
98 case '1':
99 f_singlecol = 1;
4845c19f 100 f_column = f_longform = 0;
2fd18a0a 101 break;
9ffe97fe 102 case 'C':
4845c19f 103 f_column = 1;
38eeee6d
KB
104 f_longform = f_singlecol = 0;
105 break;
106 case 'l':
107 f_longform = 1;
4845c19f 108 f_column = f_singlecol = 0;
9ffe97fe 109 break;
38eeee6d
KB
110 /* -c and -u override each other */
111 case 'c':
112 f_statustime = 1;
113 f_accesstime = 0;
114 break;
115 case 'u':
116 f_accesstime = 1;
117 f_statustime = 0;
118 break;
9ffe97fe
KB
119 case 'F':
120 f_type = 1;
121 break;
122 case 'L':
4db4a32d
KB
123 fts_options &= ~FTS_PHYSICAL;
124 fts_options |= FTS_LOGICAL;
9ffe97fe
KB
125 break;
126 case 'R':
127 f_recursive = 1;
9ffe97fe
KB
128 break;
129 case 'a':
f0a884e9 130 fts_options |= FTS_SEEDOT;
9ffe97fe 131 /* FALLTHROUGH */
2fd18a0a 132 case 'A':
b3f77fd1 133 f_listdot = 1;
2fd18a0a 134 break;
1c52c7b1
JB
135 case 'S':
136 Sflg++; /* fall into... */
547b0031 137 case 'd':
9ffe97fe 138 f_listdir = 1;
2fd18a0a 139 break;
9d5fbfa8
KB
140 case 'f':
141 f_nosort = 1;
142 break;
547b0031 143 case 'g':
9ffe97fe 144 f_group = 1;
2fd18a0a 145 break;
ec0c22c1 146 case 'i':
9ffe97fe 147 f_inode = 1;
2fd18a0a 148 break;
a657fc85
KB
149 case 'k':
150 f_kblocks = 1;
151 break;
32980675
KM
152 case 'o':
153 f_flags = 1;
154 break;
ec0c22c1 155 case 'q':
9ffe97fe 156 f_nonprint = 1;
2fd18a0a 157 break;
547b0031 158 case 'r':
9ffe97fe 159 f_reversesort = 1;
2fd18a0a 160 break;
ec0c22c1 161 case 's':
9ffe97fe 162 f_size = 1;
2fd18a0a 163 break;
926d5d7d
KB
164 case 'T':
165 f_sectime = 1;
166 break;
547b0031 167 case 't':
9ffe97fe 168 f_timesort = 1;
2fd18a0a 169 break;
ec0c22c1 170 default:
2fd18a0a 171 case '?':
9ffe97fe 172 usage();
2955376b 173 }
9ffe97fe 174 }
b3f77fd1
KB
175 argc -= optind;
176 argv += optind;
9ffe97fe 177
f0a884e9 178 /* The -d option turns off the -R option. */
b3f77fd1
KB
179 if (f_listdir)
180 f_recursive = 0;
181
f0a884e9 182 /* If options require that the files be stat'ed. */
4db4a32d 183 if (!f_longform && !f_size && !f_timesort && !f_type)
f0a884e9 184 fts_options |= FTS_NOSTAT;
b3f77fd1 185
f0a884e9 186 /* Select a sort function. */
9ffe97fe
KB
187 if (f_reversesort) {
188 if (!f_timesort)
189 sortfcn = revnamecmp;
190 else if (f_accesstime)
191 sortfcn = revacccmp;
9ffe97fe
KB
192 else if (f_statustime)
193 sortfcn = revstatcmp;
f0a884e9 194 else /* Use modification time. */
38eeee6d 195 sortfcn = revmodcmp;
2fd18a0a 196 } else {
9ffe97fe
KB
197 if (!f_timesort)
198 sortfcn = namecmp;
199 else if (f_accesstime)
200 sortfcn = acccmp;
9ffe97fe
KB
201 else if (f_statustime)
202 sortfcn = statcmp;
f0a884e9 203 else /* Use modification time. */
38eeee6d 204 sortfcn = modcmp;
9ffe97fe
KB
205 }
206
f0a884e9 207 /* Select a print function. */
4847d483
KB
208 if (f_singlecol)
209 printfcn = printscol;
210 else if (f_longform)
211 printfcn = printlong;
b3f77fd1 212 else
4e9923b0 213 printfcn = printcol;
9ffe97fe 214
0a3501e4 215 if (argc)
f0a884e9 216 traverse(argc, argv, fts_options);
0a3501e4 217 else {
aca1f24a 218 static char dot[] = ".", *dotav[] = { dot, NULL };
f0a884e9 219 traverse(1, dotav, fts_options);
2fd18a0a 220 }
4e9923b0 221 exit(0);
2fd18a0a
KB
222}
223
f0a884e9
EA
224/*
225 * Traverse() walks the logical directory structure specified by the argv list
226 * in the order specified by the mastercmp() comparison function. During the
227 * traversal it passes linked lists of structures to display() which represent
228 * a superset (may be exact set) of the files to be displayed.
229 */
4db4a32d 230void
f0a884e9
EA
231traverse(argc, argv, options)
232 int argc, options;
233 char *argv[];
234{
235 register FTS *ftsp;
236 register FTSENT *p;
f0a884e9
EA
237 register int is_ddot;
238
239 if ((ftsp =
240 fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
241 err(1, "fts_open: %s", strerror(errno));
242 display(argc, NULL, fts_children(ftsp));
243 if (f_listdir)
244 return;
245 while (p = fts_read(ftsp))
246 switch(p->fts_info) {
247 case FTS_DC:
248 err(0, "%s: directory causes a cycle", p->fts_name);
249 break;
250 case FTS_DNR:
251 case FTS_ERR:
252 err(0, "%s: %s",
253 p->fts_name, strerror(p->fts_errno));
254 break;
255 case FTS_D:
4db4a32d
KB
256 if (p->fts_level != FTS_ROOTLEVEL &&
257 p->fts_name[0] == '.' && !f_listdot)
258 break;
259 display(argc, p, fts_children(ftsp));
260 if (!f_recursive)
261 (void)fts_set(ftsp, p, FTS_SKIP);
f0a884e9
EA
262 break;
263 }
264 (void)fts_close(ftsp);
265}
be4f2604 266
f0a884e9
EA
267/*
268 * Display() takes a linked list of FTSENT structures and based on the flags
269 * set on the command line passes the list along with any other necessary
270 * information to the print function (printfcn()). P always points to the
271 * parent directory of the display list.
272 */
4db4a32d 273void
f0a884e9 274display(argc, p, list)
b3f77fd1 275 int argc;
f0a884e9
EA
276 register FTSENT *p;
277 FTSENT *list;
2fd18a0a 278{
f0a884e9
EA
279 register FTSENT *cur;
280 u_long btotal;
281 int entries, maxlen;
282
283 /*
284 * If list is NULL there are two possibilities: that the parent
285 * directory p has no children, or that fts_children() returned an
286 * error. We ignore the error case since, it will be replicated
287 * on the next call to fts_read() on the post-order visit to the
288 * directory p, and will be signalled in traverse().
289 */
290 if (list == NULL && (p->fts_level != FTS_ROOTLEVEL || argc != 1)) {
291 (void)printf("\n%s:\n", p->fts_path);
292 return;
293 }
b3f77fd1 294
c573514d 295 /*
f0a884e9
EA
296 * P can only be NULL if list is the argv list. This list must be
297 * handled slightly differently due to the fact that there does not
298 * exist a proper parent directory and different rules apply to it.
c573514d 299 */
f0a884e9
EA
300 btotal = 0;
301 maxlen = 0;
302 if (p == NULL)
303 for (cur = list, entries = 0; cur; cur = cur->fts_link) {
304 if (cur->fts_info == FTS_ERR ||
4db4a32d 305 cur->fts_info == FTS_NS) {
f0a884e9
EA
306 err(0, "%s: %s",
307 cur->fts_name, strerror(cur->fts_errno));
308 cur->fts_number = NO_PRINT;
f0864d0b 309 continue;
0ce0ae04 310 }
f0a884e9
EA
311 /*
312 * If cur is a directory, do not print it out now. Its
313 * contents will be printed out when fts_read() reads
314 * it.
315 */
316 if (cur->fts_info == FTS_D && !f_listdir) {
317 cur->fts_number = NO_PRINT;
318 continue;
4845c19f 319 }
f0a884e9 320 ++entries;
4845c19f 321 if (f_nonprint)
f0a884e9
EA
322 prcopy(cur->fts_name, cur->fts_name,
323 cur->fts_namelen);
324 if (f_column && cur->fts_namelen > maxlen)
325 maxlen = cur->fts_namelen;
9ffe97fe 326 }
f0a884e9
EA
327 else {
328 if (p->fts_level != FTS_ROOTLEVEL || argc != 1)
329 (void)printf("\n%s:\n", p->fts_path);
330
331 for (cur = list, entries = 0; cur; cur = cur->fts_link) {
332 if (cur->fts_info == FTS_ERR ||
4db4a32d 333 cur->fts_info == FTS_NS) {
f0a884e9
EA
334 err(0, "%s: %s",
335 cur->fts_name, strerror(cur->fts_errno));
336 cur->fts_number = NO_PRINT;
b3f77fd1 337 continue;
f0a884e9 338 }
4db4a32d 339 /* Don't display dot file if -a/-A not set. */
f0a884e9
EA
340 if (cur->fts_name[0] == '.' && !f_listdot) {
341 cur->fts_number = NO_PRINT;
b3f77fd1 342 continue;
f0a884e9
EA
343 }
344 ++entries;
345 if (f_nonprint)
346 prcopy(cur->fts_name, cur->fts_name,
347 cur->fts_namelen);
348 if (f_column && cur->fts_namelen > maxlen)
349 maxlen = cur->fts_namelen;
350
351 if (f_longform || f_size)
352 btotal += cur->fts_statp->st_blocks;
b3f77fd1 353 }
be4f2604 354 }
547b0031 355
f0a884e9
EA
356 if (entries)
357 printfcn(list, entries, btotal, maxlen);
547b0031
BJ
358}
359
f0a884e9
EA
360/*
361 * Ordering for mastercmp:
362 * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
363 * as larger than directories. Within either group, use the sort function.
364 * All other levels use the sort function. Error entries remain unsorted.
365 */
4db4a32d 366int
f0a884e9
EA
367mastercmp(a, b)
368 const FTSENT **a, **b;
547b0031 369{
f0a884e9 370 register int a_info, b_info;
547b0031 371
f0a884e9
EA
372 a_info = (*a)->fts_info;
373 if (a_info == FTS_ERR)
0ce0ae04 374 return (0);
f0a884e9
EA
375 b_info = (*b)->fts_info;
376 if (b_info == FTS_ERR)
377 return (0);
4db4a32d
KB
378
379 if (a_info == FTS_NS || b_info == FTS_NS)
f0a884e9 380 return (namecmp(*a, *b));
4845c19f 381
f0a884e9
EA
382 if (a_info == b_info)
383 return (sortfcn(*a, *b));
c7f12bd6 384
f0a884e9
EA
385 if ((*a)->fts_level == FTS_ROOTLEVEL)
386 if (a_info == FTS_D)
387 return (1);
388 else if (b_info == FTS_D)
389 return (-1);
390 else
391 return (sortfcn(*a, *b));
392 else
393 return (sortfcn(*a, *b));
547b0031 394}