make -f output look like standard outputs
[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 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
bcf1365c 19 */
9ffe97fe
KB
20
21#ifndef lint
22char copyright[] =
23"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
24 All rights reserved.\n";
25#endif /* not lint */
26
27#ifndef lint
2fe2e935 28static char sccsid[] = "@(#)ls.c 5.28 (Berkeley) %G%";
9ffe97fe
KB
29#endif /* not lint */
30
2fd18a0a 31#include <sys/param.h>
9ffe97fe 32#include <sys/stat.h>
4847d483 33#include <sys/ioctl.h>
2fd18a0a 34#include <dirent.h>
9ffe97fe 35#include <strings.h>
9ffe97fe
KB
36#include <errno.h>
37#include <stdio.h>
b3f77fd1 38#include "ls.h"
2fd18a0a 39
219ae3c3 40int lstat(), strlen();
b3f77fd1 41char *emalloc();
bcf1365c 42
2fd18a0a 43int qflg, Aflg, Cflg, Fflg, Lflg, Rflg, Sflg;
547b0031 44
fb29be9b
KB
45int termwidth = 80; /* default terminal width */
46
9ffe97fe 47/* flags */
9ffe97fe 48int f_accesstime; /* use time of last access */
4845c19f 49int f_column; /* columnated format */
9ffe97fe 50int f_group; /* show group ownership of a file */
38eeee6d 51int f_ignorelink; /* indirect through symbolic link operands */
9ffe97fe 52int f_inode; /* print inode */
38eeee6d
KB
53int f_listalldot; /* list . and .. as well */
54int f_listdir; /* list actual directory, not contents */
55int f_listdot; /* list files beginning with . */
9ffe97fe
KB
56int f_longform; /* long listing format */
57int f_nonprint; /* show unprintables as ? */
9ffe97fe 58int f_recursive; /* ls subdirectories also */
38eeee6d
KB
59int f_reversesort; /* reverse whatever sort is used */
60int f_singlecol; /* use single column output */
9ffe97fe 61int f_size; /* list size in short listing */
38eeee6d
KB
62int f_specialdir; /* force params to be directories */
63int f_statustime; /* use time of last mode change */
9ffe97fe 64int f_timesort; /* sort by time vice name */
38eeee6d 65int f_type; /* add type character for non-regular files */
2fd18a0a 66
9ffe97fe
KB
67main(argc, argv)
68 int argc;
69 char **argv;
547b0031 70{
9ffe97fe 71 extern int optind, stat();
4847d483 72 struct winsize win;
9ffe97fe 73 int ch;
4847d483 74 char *p, *getenv();
4845c19f
KB
75 int acccmp(), bcopy(), modcmp(), namecmp(), prcopy(), printcol();
76 int printlong(), printscol(), revacccmp(), revmodcmp(), revnamecmp();
77 int revstatcmp(), statcmp();
2fd18a0a 78
4845c19f 79 /* terminal defaults to -Cq, non-terminal defaults to -1 */
547b0031 80 if (isatty(1)) {
9ffe97fe 81 f_nonprint = 1;
fb29be9b
KB
82 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
83 if (p = getenv("COLUMNS"))
84 termwidth = atoi(p);
85 }
4847d483
KB
86 else
87 termwidth = win.ws_col;
4845c19f 88 f_column = 1;
9ffe97fe
KB
89 } else
90 f_singlecol = 1;
2fd18a0a 91
38eeee6d 92 /* root is -A automatically */
9ffe97fe 93 if (!getuid())
b3f77fd1 94 f_listdot = 1;
9ffe97fe
KB
95
96 while ((ch = getopt(argc, argv, "1ACFLRacdfgilqrstu")) != EOF) {
97 switch (ch) {
38eeee6d
KB
98 /*
99 * -1, -C and -l all override each other
100 * so shell aliasing works right
101 */
9ffe97fe
KB
102 case '1':
103 f_singlecol = 1;
4845c19f 104 f_column = f_longform = 0;
2fd18a0a 105 break;
9ffe97fe 106 case 'C':
4845c19f 107 f_column = 1;
38eeee6d
KB
108 f_longform = f_singlecol = 0;
109 break;
110 case 'l':
111 f_longform = 1;
4845c19f 112 f_column = f_singlecol = 0;
9ffe97fe 113 break;
38eeee6d
KB
114 /* -c and -u override each other */
115 case 'c':
116 f_statustime = 1;
117 f_accesstime = 0;
118 break;
119 case 'u':
120 f_accesstime = 1;
121 f_statustime = 0;
122 break;
9ffe97fe
KB
123 case 'F':
124 f_type = 1;
125 break;
126 case 'L':
38eeee6d 127 f_ignorelink = 1;
9ffe97fe
KB
128 break;
129 case 'R':
130 f_recursive = 1;
9ffe97fe
KB
131 break;
132 case 'a':
b3f77fd1 133 f_listalldot = 1;
9ffe97fe 134 /* FALLTHROUGH */
2fd18a0a 135 case 'A':
b3f77fd1 136 f_listdot = 1;
2fd18a0a 137 break;
1c52c7b1
JB
138 case 'S':
139 Sflg++; /* fall into... */
547b0031 140 case 'd':
9ffe97fe 141 f_listdir = 1;
2fd18a0a 142 break;
ec0c22c1 143 case 'f':
9ffe97fe 144 f_specialdir = 1;
2fd18a0a 145 break;
547b0031 146 case 'g':
9ffe97fe 147 f_group = 1;
2fd18a0a 148 break;
ec0c22c1 149 case 'i':
9ffe97fe 150 f_inode = 1;
2fd18a0a 151 break;
ec0c22c1 152 case 'q':
9ffe97fe 153 f_nonprint = 1;
2fd18a0a 154 break;
547b0031 155 case 'r':
9ffe97fe 156 f_reversesort = 1;
2fd18a0a 157 break;
ec0c22c1 158 case 's':
9ffe97fe 159 f_size = 1;
2fd18a0a 160 break;
547b0031 161 case 't':
9ffe97fe 162 f_timesort = 1;
2fd18a0a 163 break;
ec0c22c1 164 default:
2fd18a0a 165 case '?':
9ffe97fe 166 usage();
2955376b 167 }
9ffe97fe 168 }
b3f77fd1
KB
169 argc -= optind;
170 argv += optind;
9ffe97fe 171
be4f2604 172 /* -f turns off -F, -R, -l, -t, -s, -r, turns on -a */
9ffe97fe 173 if (f_specialdir) {
be4f2604
KB
174 f_longform = f_recursive = f_reversesort = f_size =
175 f_timesort = f_type = 0;
b3f77fd1 176 f_listdot = f_listalldot = 1;
9ffe97fe
KB
177 }
178
b3f77fd1
KB
179 /* -d turns off -R */
180 if (f_listdir)
181 f_recursive = 0;
182
95fa2ad8 183 /* if need to stat files */
7a122474 184 needstat = f_longform || f_recursive || f_timesort || f_size || f_type;
b3f77fd1 185
9ffe97fe
KB
186 /* select a sort function */
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;
38eeee6d
KB
194 else /* use modification time */
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;
38eeee6d
KB
203 else /* use modification time */
204 sortfcn = modcmp;
9ffe97fe
KB
205 }
206
4847d483
KB
207 /* select a print function */
208 if (f_singlecol)
209 printfcn = printscol;
210 else if (f_longform)
211 printfcn = printlong;
4845c19f
KB
212 else {
213 /*
214 * set f_column, in case f_longform selected, then turned
215 * off by f_special.
216 */
217 f_column = 1;
4847d483 218 printfcn = printcol;
4845c19f 219 }
4847d483 220
4845c19f
KB
221 if (f_specialdir) {
222 if (!argc) {
223 (void)fprintf(stderr, "ls: -f requires operands.\n");
224 exit(1);
225 }
2fe2e935
KB
226 for (;;) {
227 if (argc > 1)
228 (void)printf("%s:\n", *argv);
4845c19f 229 dodir(*argv);
2fe2e935
KB
230 if (!*++argv)
231 break;
232 putchar('\n');
233 }
4845c19f 234 } else if (argc)
918d99af 235 doargs(argc, argv);
b3f77fd1 236 else
4845c19f 237 dodir(".");
9ffe97fe 238 exit(0);
547b0031
BJ
239}
240
4845c19f
KB
241dodir(name)
242 char *name;
547b0031 243{
b3f77fd1
KB
244 LS local, *stats;
245 int num;
246 char *names;
9ffe97fe 247
4845c19f 248 if (lstat(local.name = name, &local.lstat)) {
2fe2e935 249 (void)fprintf(stderr, "ls: %s: %s\n", name, strerror(errno));
4845c19f 250 return;
2fd18a0a 251 }
918d99af
KB
252 if (num = tabdir(&local, &stats, &names))
253 displaydir(stats, num);
4845c19f
KB
254 (void)free((char *)stats);
255 (void)free((char *)names);
2fd18a0a
KB
256}
257
be4f2604
KB
258static char path[MAXPATHLEN + 1];
259static char *endofpath = path;
260
918d99af 261doargs(argc, argv)
b3f77fd1
KB
262 int argc;
263 char **argv;
2fd18a0a 264{
c573514d 265 register LS *dstatp, *rstatp;
4845c19f 266 register int cnt, dircnt, maxlen, regcnt;
c573514d 267 LS *dstats, *rstats;
b3f77fd1 268 struct stat sb;
4845c19f
KB
269 int (*statfcn)(), stat(), lstat();
270 char top[MAXPATHLEN + 1];
271 u_long blocks;
b3f77fd1 272
c573514d
KB
273 /*
274 * walk through the operands, building separate arrays of LS
275 * structures for directory and non-directory files.
276 */
b3f77fd1 277 dstats = rstats = NULL;
38eeee6d 278 statfcn = f_ignorelink ? stat : lstat;
b3f77fd1
KB
279 for (dircnt = regcnt = 0; *argv; ++argv) {
280 if (statfcn(*argv, &sb)) {
4845c19f
KB
281 (void)fprintf(stderr, "ls: %s: %s\n", *argv,
282 strerror(errno));
b3f77fd1
KB
283 if (errno == ENOENT)
284 continue;
285 exit(1);
9ffe97fe 286 }
4845c19f 287 if (S_ISDIR(sb.st_mode) && !f_listdir) {
b3f77fd1 288 if (!dstats)
c573514d 289 dstatp = dstats = (LS *)emalloc((u_int)argc *
b3f77fd1 290 (sizeof(LS)));
c573514d
KB
291 dstatp->name = *argv;
292 dstatp->lstat = sb;
293 ++dstatp;
b3f77fd1
KB
294 ++dircnt;
295 }
296 else {
4845c19f 297 if (!rstats) {
c573514d 298 rstatp = rstats = (LS *)emalloc((u_int)argc *
b3f77fd1 299 (sizeof(LS)));
4845c19f
KB
300 blocks = 0;
301 maxlen = -1;
302 }
c573514d
KB
303 rstatp->name = *argv;
304 rstatp->lstat = sb;
4845c19f
KB
305
306 /* save name length for -C format */
307 rstatp->len = strlen(*argv);
308
309 if (f_nonprint)
310 prcopy(*argv, *argv, rstatp->len);
311
312 /* calculate number of blocks if -l/-s formats */
313 if (f_longform || f_size)
314 blocks += sb.st_blocks;
315
316 /* save max length if -C format */
317 if (f_column && maxlen < rstatp->len)
318 maxlen = rstatp->len;
319
c573514d 320 ++rstatp;
b3f77fd1 321 ++regcnt;
9ffe97fe 322 }
9ffe97fe 323 }
c573514d 324 /* display regular files */
b3f77fd1 325 if (regcnt) {
4845c19f
KB
326 rstats[0].lstat.st_btotal = blocks;
327 rstats[0].lstat.st_maxlen = maxlen;
328 displaydir(rstats, regcnt);
547b0031 329 }
c573514d 330 /* display directories */
b3f77fd1 331 if (dircnt) {
be4f2604
KB
332 register char *p;
333
95fa2ad8
KB
334 if (dircnt > 1) {
335 (void)getwd(top);
b3f77fd1 336 qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
95fa2ad8
KB
337 }
338 for (cnt = 0; cnt < dircnt; ++dstats) {
be4f2604
KB
339 for (endofpath = path, p = dstats->name;
340 *endofpath = *p++; ++endofpath);
8603dc1e 341 subdir(dstats, regcnt || cnt, regcnt || dircnt > 1);
95fa2ad8
KB
342 if (++cnt < dircnt && chdir(top)) {
343 (void)fprintf(stderr, "ls: %s: %s\n",
344 top, strerror(errno));
345 exit(1);
346 }
be4f2604 347 }
547b0031 348 }
547b0031
BJ
349}
350
918d99af 351displaydir(stats, num)
b3f77fd1
KB
352 LS *stats;
353 register int num;
547b0031 354{
be4f2604 355 register char *p, *savedpath;
b3f77fd1 356 LS *lp;
9ffe97fe 357
219ae3c3 358 if (num > 1 && !f_specialdir) {
82567da7
KB
359 u_long save1, save2;
360
361 save1 = stats[0].lstat.st_btotal;
362 save2 = stats[0].lstat.st_maxlen;
b3f77fd1 363 qsort((char *)stats, num, sizeof(LS), sortfcn);
82567da7
KB
364 stats[0].lstat.st_btotal = save1;
365 stats[0].lstat.st_maxlen = save2;
219ae3c3 366 }
9ffe97fe 367
4847d483 368 printfcn(stats, num);
547b0031 369
be4f2604
KB
370 if (f_recursive) {
371 savedpath = endofpath;
b3f77fd1
KB
372 for (lp = stats; num--; ++lp) {
373 if (!S_ISDIR(lp->lstat.st_mode))
374 continue;
375 p = lp->name;
376 if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2]))
377 continue;
be4f2604
KB
378 if (endofpath != path && endofpath[-1] != '/')
379 *endofpath++ = '/';
380 for (; *endofpath = *p++; ++endofpath);
918d99af 381 subdir(lp, 1, 1);
be4f2604 382 *(endofpath = savedpath) = '\0';
b3f77fd1 383 }
be4f2604 384 }
547b0031
BJ
385}
386
918d99af 387subdir(lp, newline, tag)
b3f77fd1
KB
388 LS *lp;
389 int newline, tag;
2fd18a0a 390{
b3f77fd1
KB
391 LS *stats;
392 int num;
be4f2604 393 char *names;
b3f77fd1 394
c573514d
KB
395 /*
396 * this doesn't really belong here, but it's the only place that
397 * everybody goes through; the `tag' variable is so that we don't
398 * print the header for directories unless we're going to display
399 * more directories, or we've already displayed files or directories.
400 * The `newline' variable keeps us from inserting a newline before
401 * we've displayed anything at all.
402 */
b3f77fd1
KB
403 if (newline)
404 (void)putchar('\n');
405 if (tag)
406 (void)printf("%s:\n", path);
407
408 if (chdir(lp->name)) {
409 (void)fprintf(stderr, "ls: %s: %s\n",
410 lp->name, strerror(errno));
411 return;
2fd18a0a 412 }
918d99af
KB
413 if (num = tabdir(lp, &stats, &names))
414 displaydir(stats, num);
b3f77fd1
KB
415 (void)free((char *)stats);
416 (void)free((char *)names);
417 if (chdir("..")) {
418 (void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
419 exit(1);
2fd18a0a 420 }
547b0031
BJ
421}
422
918d99af 423tabdir(lp, s_stats, s_names)
b3f77fd1
KB
424 LS *lp, **s_stats;
425 char **s_names;
547b0031 426{
219ae3c3
KB
427 register DIR *dirp;
428 register int cnt, maxentry, maxlen;
b3f77fd1 429 register char *p, *names;
7a122474 430 struct dirent *dp;
219ae3c3 431 u_long blocks;
b3f77fd1 432 LS *stats;
547b0031 433
219ae3c3
KB
434 /*
435 * allocate space for array of LS structures and the file names
436 * the name field will point to. Make it big so we don't have
437 * to realloc often.
438 */
b3f77fd1
KB
439#define DEFNUM 256
440 maxentry = DEFNUM;
441 *s_stats = stats = (LS *)emalloc((u_int)DEFNUM * sizeof(LS));
442 *s_names = names = emalloc((u_int)lp->lstat.st_size);
9ffe97fe 443
b3f77fd1 444 if (!(dirp = opendir(f_specialdir ? lp->name : "."))) {
219ae3c3
KB
445 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
446 strerror(errno));
b3f77fd1 447 return(0);
9ffe97fe 448 }
219ae3c3
KB
449 blocks = 0;
450 maxlen = -1;
7a122474 451 for (cnt = 0; dp = readdir(dirp);) {
c573514d 452 /* this does -A and -a */
7a122474 453 p = dp->d_name;
b3f77fd1
KB
454 if (p[0] == '.') {
455 if (!f_listdot)
456 continue;
457 if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2]))
458 continue;
459 }
460 if (cnt == maxentry) {
461 maxentry += DEFNUM;
462 if (!(stats = (LS *)realloc((char *)stats,
463 (u_int)maxentry * sizeof(LS))))
464 nomem();
465 }
7a122474 466 if (needstat && lstat(dp->d_name, &stats[cnt].lstat)) {
b3f77fd1 467 (void)fprintf(stderr, "ls: %s: %s\n",
7a122474 468 dp->d_name, strerror(errno));
b3f77fd1
KB
469 if (errno == ENOENT)
470 continue;
471 exit(1);
472 }
473 stats[cnt].name = names;
219ae3c3 474
219ae3c3
KB
475 if (f_nonprint)
476 prcopy(dp->d_name, names, (int)dp->d_namlen);
477 else
478 bcopy(dp->d_name, names, (int)dp->d_namlen);
7a122474 479 names += dp->d_namlen;
b3f77fd1 480 *names++ = '\0';
219ae3c3
KB
481
482 /*
483 * get the inode from the directory, so the -f flag
484 * works right.
485 */
486 stats[cnt].lstat.st_ino = dp->d_ino;
487
488 /* save name length for -C format */
489 stats[cnt].len = dp->d_namlen;
4845c19f
KB
490
491 /* calculate number of blocks if -l/-s formats */
82567da7 492 if (f_longform || f_size)
219ae3c3 493 blocks += stats[cnt].lstat.st_blocks;
4845c19f 494
219ae3c3 495 /* save max length if -C format */
4845c19f 496 if (f_column && maxlen < (int)dp->d_namlen)
219ae3c3 497 maxlen = dp->d_namlen;
b3f77fd1 498 ++cnt;
2fd18a0a 499 }
82567da7
KB
500 stats[0].lstat.st_btotal = blocks;
501 stats[0].lstat.st_maxlen = maxlen;
be4f2604 502 closedir(dirp);
b3f77fd1 503 return(cnt);
547b0031 504}