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