don't wait for packets on SIGINTR, just die
[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
562f122f 28static char sccsid[] = "@(#)ls.c 5.31 (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 56int f_longform; /* long listing format */
4dfe4db2
KB
57int f_needstat; /* if need to stat files */
58int f_newline; /* if precede with newline */
9ffe97fe 59int f_nonprint; /* show unprintables as ? */
9ffe97fe 60int f_recursive; /* ls subdirectories also */
38eeee6d
KB
61int f_reversesort; /* reverse whatever sort is used */
62int f_singlecol; /* use single column output */
9ffe97fe 63int f_size; /* list size in short listing */
38eeee6d
KB
64int f_specialdir; /* force params to be directories */
65int f_statustime; /* use time of last mode change */
4dfe4db2 66int f_dirname; /* if precede with directory name */
9ffe97fe 67int f_timesort; /* sort by time vice name */
4dfe4db2 68int f_total; /* if precede with "total" line */
38eeee6d 69int f_type; /* add type character for non-regular files */
2fd18a0a 70
9ffe97fe
KB
71main(argc, argv)
72 int argc;
73 char **argv;
547b0031 74{
9ffe97fe 75 extern int optind, stat();
4847d483 76 struct winsize win;
9ffe97fe 77 int ch;
4847d483 78 char *p, *getenv();
4845c19f
KB
79 int acccmp(), bcopy(), modcmp(), namecmp(), prcopy(), printcol();
80 int printlong(), printscol(), revacccmp(), revmodcmp(), revnamecmp();
81 int revstatcmp(), statcmp();
2fd18a0a 82
4845c19f 83 /* terminal defaults to -Cq, non-terminal defaults to -1 */
547b0031 84 if (isatty(1)) {
9ffe97fe 85 f_nonprint = 1;
fb29be9b
KB
86 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
87 if (p = getenv("COLUMNS"))
88 termwidth = atoi(p);
89 }
4847d483
KB
90 else
91 termwidth = win.ws_col;
4845c19f 92 f_column = 1;
9ffe97fe
KB
93 } else
94 f_singlecol = 1;
2fd18a0a 95
38eeee6d 96 /* root is -A automatically */
9ffe97fe 97 if (!getuid())
b3f77fd1 98 f_listdot = 1;
9ffe97fe
KB
99
100 while ((ch = getopt(argc, argv, "1ACFLRacdfgilqrstu")) != EOF) {
101 switch (ch) {
38eeee6d
KB
102 /*
103 * -1, -C and -l all override each other
104 * so shell aliasing works right
105 */
9ffe97fe
KB
106 case '1':
107 f_singlecol = 1;
4845c19f 108 f_column = f_longform = 0;
2fd18a0a 109 break;
9ffe97fe 110 case 'C':
4845c19f 111 f_column = 1;
38eeee6d
KB
112 f_longform = f_singlecol = 0;
113 break;
114 case 'l':
115 f_longform = 1;
4845c19f 116 f_column = f_singlecol = 0;
9ffe97fe 117 break;
38eeee6d
KB
118 /* -c and -u override each other */
119 case 'c':
120 f_statustime = 1;
121 f_accesstime = 0;
122 break;
123 case 'u':
124 f_accesstime = 1;
125 f_statustime = 0;
126 break;
9ffe97fe
KB
127 case 'F':
128 f_type = 1;
129 break;
130 case 'L':
38eeee6d 131 f_ignorelink = 1;
9ffe97fe
KB
132 break;
133 case 'R':
134 f_recursive = 1;
9ffe97fe
KB
135 break;
136 case 'a':
b3f77fd1 137 f_listalldot = 1;
9ffe97fe 138 /* FALLTHROUGH */
2fd18a0a 139 case 'A':
b3f77fd1 140 f_listdot = 1;
2fd18a0a 141 break;
1c52c7b1
JB
142 case 'S':
143 Sflg++; /* fall into... */
547b0031 144 case 'd':
9ffe97fe 145 f_listdir = 1;
2fd18a0a 146 break;
ec0c22c1 147 case 'f':
9ffe97fe 148 f_specialdir = 1;
2fd18a0a 149 break;
547b0031 150 case 'g':
9ffe97fe 151 f_group = 1;
2fd18a0a 152 break;
ec0c22c1 153 case 'i':
9ffe97fe 154 f_inode = 1;
2fd18a0a 155 break;
ec0c22c1 156 case 'q':
9ffe97fe 157 f_nonprint = 1;
2fd18a0a 158 break;
547b0031 159 case 'r':
9ffe97fe 160 f_reversesort = 1;
2fd18a0a 161 break;
ec0c22c1 162 case 's':
9ffe97fe 163 f_size = 1;
2fd18a0a 164 break;
547b0031 165 case 't':
9ffe97fe 166 f_timesort = 1;
2fd18a0a 167 break;
ec0c22c1 168 default:
2fd18a0a 169 case '?':
9ffe97fe 170 usage();
2955376b 171 }
9ffe97fe 172 }
b3f77fd1
KB
173 argc -= optind;
174 argv += optind;
9ffe97fe 175
be4f2604 176 /* -f turns off -F, -R, -l, -t, -s, -r, turns on -a */
9ffe97fe 177 if (f_specialdir) {
be4f2604
KB
178 f_longform = f_recursive = f_reversesort = f_size =
179 f_timesort = f_type = 0;
b3f77fd1 180 f_listdot = f_listalldot = 1;
9ffe97fe
KB
181 }
182
b3f77fd1
KB
183 /* -d turns off -R */
184 if (f_listdir)
185 f_recursive = 0;
186
95fa2ad8 187 /* if need to stat files */
4dfe4db2
KB
188 f_needstat = f_longform || f_recursive || f_timesort ||
189 f_size || f_type;
b3f77fd1 190
9ffe97fe
KB
191 /* select a sort function */
192 if (f_reversesort) {
193 if (!f_timesort)
194 sortfcn = revnamecmp;
195 else if (f_accesstime)
196 sortfcn = revacccmp;
9ffe97fe
KB
197 else if (f_statustime)
198 sortfcn = revstatcmp;
38eeee6d
KB
199 else /* use modification time */
200 sortfcn = revmodcmp;
2fd18a0a 201 } else {
9ffe97fe
KB
202 if (!f_timesort)
203 sortfcn = namecmp;
204 else if (f_accesstime)
205 sortfcn = acccmp;
9ffe97fe
KB
206 else if (f_statustime)
207 sortfcn = statcmp;
38eeee6d
KB
208 else /* use modification time */
209 sortfcn = modcmp;
9ffe97fe
KB
210 }
211
4847d483
KB
212 /* select a print function */
213 if (f_singlecol)
214 printfcn = printscol;
215 else if (f_longform)
216 printfcn = printlong;
4845c19f
KB
217 else {
218 /*
219 * set f_column, in case f_longform selected, then turned
220 * off by f_special.
221 */
222 f_column = 1;
4847d483 223 printfcn = printcol;
4845c19f 224 }
4847d483 225
4845c19f
KB
226 if (f_specialdir) {
227 if (!argc) {
228 (void)fprintf(stderr, "ls: -f requires operands.\n");
229 exit(1);
230 }
2fe2e935
KB
231 for (;;) {
232 if (argc > 1)
233 (void)printf("%s:\n", *argv);
4845c19f 234 dodir(*argv);
2fe2e935
KB
235 if (!*++argv)
236 break;
237 putchar('\n');
238 }
4845c19f 239 } else if (argc)
918d99af 240 doargs(argc, argv);
b3f77fd1 241 else
4845c19f 242 dodir(".");
9ffe97fe 243 exit(0);
547b0031
BJ
244}
245
4845c19f
KB
246dodir(name)
247 char *name;
547b0031 248{
b3f77fd1
KB
249 LS local, *stats;
250 int num;
251 char *names;
9ffe97fe 252
4845c19f 253 if (lstat(local.name = name, &local.lstat)) {
2fe2e935 254 (void)fprintf(stderr, "ls: %s: %s\n", name, strerror(errno));
4845c19f 255 return;
2fd18a0a 256 }
918d99af
KB
257 if (num = tabdir(&local, &stats, &names))
258 displaydir(stats, num);
4845c19f
KB
259 (void)free((char *)stats);
260 (void)free((char *)names);
2fd18a0a
KB
261}
262
be4f2604
KB
263static char path[MAXPATHLEN + 1];
264static char *endofpath = path;
265
918d99af 266doargs(argc, argv)
b3f77fd1
KB
267 int argc;
268 char **argv;
2fd18a0a 269{
c573514d 270 register LS *dstatp, *rstatp;
4845c19f 271 register int cnt, dircnt, maxlen, regcnt;
c573514d 272 LS *dstats, *rstats;
b3f77fd1 273 struct stat sb;
4845c19f
KB
274 int (*statfcn)(), stat(), lstat();
275 char top[MAXPATHLEN + 1];
276 u_long blocks;
b3f77fd1 277
c573514d
KB
278 /*
279 * walk through the operands, building separate arrays of LS
280 * structures for directory and non-directory files.
281 */
b3f77fd1 282 dstats = rstats = NULL;
562f122f 283 statfcn = (f_longform || f_listdir) && !f_ignorelink ? lstat : stat;
b3f77fd1
KB
284 for (dircnt = regcnt = 0; *argv; ++argv) {
285 if (statfcn(*argv, &sb)) {
562f122f
KB
286 if (statfcn != stat || lstat(*argv, &sb)) {
287 (void)fprintf(stderr, "ls: %s: %s\n", *argv,
288 strerror(errno));
289 if (errno == ENOENT)
290 continue;
291 exit(1);
292 }
9ffe97fe 293 }
4845c19f 294 if (S_ISDIR(sb.st_mode) && !f_listdir) {
b3f77fd1 295 if (!dstats)
c573514d 296 dstatp = dstats = (LS *)emalloc((u_int)argc *
b3f77fd1 297 (sizeof(LS)));
c573514d
KB
298 dstatp->name = *argv;
299 dstatp->lstat = sb;
300 ++dstatp;
b3f77fd1
KB
301 ++dircnt;
302 }
303 else {
4845c19f 304 if (!rstats) {
c573514d 305 rstatp = rstats = (LS *)emalloc((u_int)argc *
b3f77fd1 306 (sizeof(LS)));
4845c19f
KB
307 blocks = 0;
308 maxlen = -1;
309 }
c573514d
KB
310 rstatp->name = *argv;
311 rstatp->lstat = sb;
4845c19f
KB
312
313 /* save name length for -C format */
314 rstatp->len = strlen(*argv);
315
316 if (f_nonprint)
317 prcopy(*argv, *argv, rstatp->len);
318
319 /* calculate number of blocks if -l/-s formats */
320 if (f_longform || f_size)
321 blocks += sb.st_blocks;
322
323 /* save max length if -C format */
324 if (f_column && maxlen < rstatp->len)
325 maxlen = rstatp->len;
326
c573514d 327 ++rstatp;
b3f77fd1 328 ++regcnt;
9ffe97fe 329 }
9ffe97fe 330 }
c573514d 331 /* display regular files */
b3f77fd1 332 if (regcnt) {
4845c19f
KB
333 rstats[0].lstat.st_btotal = blocks;
334 rstats[0].lstat.st_maxlen = maxlen;
335 displaydir(rstats, regcnt);
4dfe4db2 336 f_newline = f_dirname = 1;
547b0031 337 }
c573514d 338 /* display directories */
b3f77fd1 339 if (dircnt) {
be4f2604
KB
340 register char *p;
341
4dfe4db2 342 f_total = 1;
95fa2ad8
KB
343 if (dircnt > 1) {
344 (void)getwd(top);
b3f77fd1 345 qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
4dfe4db2 346 f_dirname = 1;
95fa2ad8
KB
347 }
348 for (cnt = 0; cnt < dircnt; ++dstats) {
be4f2604
KB
349 for (endofpath = path, p = dstats->name;
350 *endofpath = *p++; ++endofpath);
4dfe4db2
KB
351 subdir(dstats);
352 f_newline = 1;
95fa2ad8
KB
353 if (++cnt < dircnt && chdir(top)) {
354 (void)fprintf(stderr, "ls: %s: %s\n",
355 top, strerror(errno));
356 exit(1);
357 }
be4f2604 358 }
547b0031 359 }
547b0031
BJ
360}
361
918d99af 362displaydir(stats, num)
b3f77fd1
KB
363 LS *stats;
364 register int num;
547b0031 365{
be4f2604 366 register char *p, *savedpath;
b3f77fd1 367 LS *lp;
9ffe97fe 368
219ae3c3 369 if (num > 1 && !f_specialdir) {
82567da7
KB
370 u_long save1, save2;
371
372 save1 = stats[0].lstat.st_btotal;
373 save2 = stats[0].lstat.st_maxlen;
b3f77fd1 374 qsort((char *)stats, num, sizeof(LS), sortfcn);
82567da7
KB
375 stats[0].lstat.st_btotal = save1;
376 stats[0].lstat.st_maxlen = save2;
219ae3c3 377 }
9ffe97fe 378
4847d483 379 printfcn(stats, num);
547b0031 380
be4f2604
KB
381 if (f_recursive) {
382 savedpath = endofpath;
b3f77fd1
KB
383 for (lp = stats; num--; ++lp) {
384 if (!S_ISDIR(lp->lstat.st_mode))
385 continue;
386 p = lp->name;
387 if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2]))
388 continue;
be4f2604
KB
389 if (endofpath != path && endofpath[-1] != '/')
390 *endofpath++ = '/';
391 for (; *endofpath = *p++; ++endofpath);
4dfe4db2
KB
392 f_newline = f_dirname = f_total = 1;
393 subdir(lp);
be4f2604 394 *(endofpath = savedpath) = '\0';
b3f77fd1 395 }
be4f2604 396 }
547b0031
BJ
397}
398
4dfe4db2 399subdir(lp)
b3f77fd1 400 LS *lp;
2fd18a0a 401{
b3f77fd1
KB
402 LS *stats;
403 int num;
be4f2604 404 char *names;
b3f77fd1 405
4dfe4db2 406 if (f_newline)
b3f77fd1 407 (void)putchar('\n');
4dfe4db2 408 if (f_dirname)
b3f77fd1
KB
409 (void)printf("%s:\n", path);
410
411 if (chdir(lp->name)) {
d6bb46f2
KB
412 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
413 strerror(errno));
b3f77fd1 414 return;
2fd18a0a 415 }
918d99af
KB
416 if (num = tabdir(lp, &stats, &names))
417 displaydir(stats, num);
b3f77fd1
KB
418 (void)free((char *)stats);
419 (void)free((char *)names);
420 if (chdir("..")) {
421 (void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
422 exit(1);
2fd18a0a 423 }
547b0031
BJ
424}
425
918d99af 426tabdir(lp, s_stats, s_names)
b3f77fd1
KB
427 LS *lp, **s_stats;
428 char **s_names;
547b0031 429{
219ae3c3
KB
430 register DIR *dirp;
431 register int cnt, maxentry, maxlen;
b3f77fd1 432 register char *p, *names;
7a122474 433 struct dirent *dp;
219ae3c3 434 u_long blocks;
b3f77fd1 435 LS *stats;
547b0031 436
219ae3c3
KB
437 /*
438 * allocate space for array of LS structures and the file names
439 * the name field will point to. Make it big so we don't have
440 * to realloc often.
441 */
b3f77fd1
KB
442#define DEFNUM 256
443 maxentry = DEFNUM;
d6bb46f2
KB
444 stats = (LS *)emalloc((u_int)DEFNUM * sizeof(LS));
445 names = emalloc((u_int)lp->lstat.st_size);
9ffe97fe 446
b3f77fd1 447 if (!(dirp = opendir(f_specialdir ? lp->name : "."))) {
219ae3c3
KB
448 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
449 strerror(errno));
b3f77fd1 450 return(0);
9ffe97fe 451 }
219ae3c3
KB
452 blocks = 0;
453 maxlen = -1;
7a122474 454 for (cnt = 0; dp = readdir(dirp);) {
c573514d 455 /* this does -A and -a */
7a122474 456 p = dp->d_name;
b3f77fd1
KB
457 if (p[0] == '.') {
458 if (!f_listdot)
459 continue;
460 if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2]))
461 continue;
462 }
463 if (cnt == maxentry) {
d6bb46f2 464 maxentry *= 2;
b3f77fd1
KB
465 if (!(stats = (LS *)realloc((char *)stats,
466 (u_int)maxentry * sizeof(LS))))
467 nomem();
468 }
4dfe4db2 469 if (f_needstat && lstat(dp->d_name, &stats[cnt].lstat)) {
b3f77fd1 470 (void)fprintf(stderr, "ls: %s: %s\n",
7a122474 471 dp->d_name, strerror(errno));
b3f77fd1
KB
472 if (errno == ENOENT)
473 continue;
474 exit(1);
475 }
476 stats[cnt].name = names;
219ae3c3 477
219ae3c3
KB
478 if (f_nonprint)
479 prcopy(dp->d_name, names, (int)dp->d_namlen);
480 else
481 bcopy(dp->d_name, names, (int)dp->d_namlen);
7a122474 482 names += dp->d_namlen;
b3f77fd1 483 *names++ = '\0';
219ae3c3
KB
484
485 /*
486 * get the inode from the directory, so the -f flag
487 * works right.
488 */
489 stats[cnt].lstat.st_ino = dp->d_ino;
490
491 /* save name length for -C format */
492 stats[cnt].len = dp->d_namlen;
4845c19f
KB
493
494 /* calculate number of blocks if -l/-s formats */
82567da7 495 if (f_longform || f_size)
219ae3c3 496 blocks += stats[cnt].lstat.st_blocks;
4845c19f 497
219ae3c3 498 /* save max length if -C format */
4845c19f 499 if (f_column && maxlen < (int)dp->d_namlen)
219ae3c3 500 maxlen = dp->d_namlen;
b3f77fd1 501 ++cnt;
2fd18a0a 502 }
d6bb46f2
KB
503 *s_stats = stats;
504 *s_names = names;
82567da7
KB
505 stats[0].lstat.st_btotal = blocks;
506 stats[0].lstat.st_maxlen = maxlen;
be4f2604 507 closedir(dirp);
b3f77fd1 508 return(cnt);
547b0031 509}