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