386BSD 0.1 development
[unix-history] / usr / src / bin / ls / ls.c
CommitLineData
1513de2c
WJ
1/*
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, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38char copyright[] =
39"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
40 All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44static char sccsid[] = "@(#)ls.c 5.48 (Berkeley) 4/3/91";
45#endif /* not lint */
46
47#include <sys/param.h>
48#include <sys/stat.h>
49#include <sys/ioctl.h>
50#include <dirent.h>
51#include <string.h>
52#include <errno.h>
53#include <stdio.h>
54#include "ls.h"
55
56int (*sortfcn)(), (*printfcn)();
57int lstat();
58char *emalloc();
59
60int termwidth = 80; /* default terminal width */
61
62/* flags */
63int f_accesstime; /* use time of last access */
64int f_column; /* columnated format */
65int f_group; /* show group ownership of a file */
66int f_ignorelink; /* indirect through symbolic link operands */
67int f_inode; /* print inode */
68int f_kblocks; /* print size in kilobytes */
69int f_listalldot; /* list . and .. as well */
70int f_listdir; /* list actual directory, not contents */
71int f_listdot; /* list files beginning with . */
72int f_longform; /* long listing format */
73int f_needstat; /* if need to stat files */
74int f_newline; /* if precede with newline */
75int f_nonprint; /* show unprintables as ? */
76int f_nosort; /* don't sort output */
77int f_recursive; /* ls subdirectories also */
78int f_reversesort; /* reverse whatever sort is used */
79int f_sectime; /* print the real time for all files */
80int f_singlecol; /* use single column output */
81int f_size; /* list size in short listing */
82int f_statustime; /* use time of last mode change */
83int f_dirname; /* if precede with directory name */
84int f_timesort; /* sort by time vice name */
85int f_total; /* if precede with "total" line */
86int f_type; /* add type character for non-regular files */
87
88int (*statfcn)(), stat(), lstat();
89
90main(argc, argv)
91 int argc;
92 char **argv;
93{
94 extern int optind, stat();
95 struct winsize win;
96 int ch;
97 char *p, *getenv();
98 int acccmp(), modcmp(), namecmp(), prcopy(), printcol();
99 int printlong(), printscol(), revacccmp(), revmodcmp(), revnamecmp();
100 int revstatcmp(), statcmp();
101
102 /* terminal defaults to -Cq, non-terminal defaults to -1 */
103 if (isatty(1)) {
104 f_nonprint = 1;
105 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
106 if (p = getenv("COLUMNS"))
107 termwidth = atoi(p);
108 }
109 else
110 termwidth = win.ws_col;
111 f_column = 1;
112 } else
113 f_singlecol = 1;
114
115 /* root is -A automatically */
116 if (!getuid())
117 f_listdot = 1;
118
119 while ((ch = getopt(argc, argv, "1ACFLRTacdfgiklqrstu")) != EOF) {
120 switch (ch) {
121 /*
122 * -1, -C and -l all override each other
123 * so shell aliasing works right
124 */
125 case '1':
126 f_singlecol = 1;
127 f_column = f_longform = 0;
128 break;
129 case 'C':
130 f_column = 1;
131 f_longform = f_singlecol = 0;
132 break;
133 case 'l':
134 f_longform = 1;
135 f_column = f_singlecol = 0;
136 break;
137 /* -c and -u override each other */
138 case 'c':
139 f_statustime = 1;
140 f_accesstime = 0;
141 break;
142 case 'u':
143 f_accesstime = 1;
144 f_statustime = 0;
145 break;
146 case 'F':
147 f_type = 1;
148 break;
149 case 'L':
150 f_ignorelink = 1;
151 break;
152 case 'R':
153 f_recursive = 1;
154 break;
155 case 'a':
156 f_listalldot = 1;
157 /* FALLTHROUGH */
158 case 'A':
159 f_listdot = 1;
160 break;
161 case 'd':
162 f_listdir = 1;
163 break;
164 case 'f':
165 f_nosort = 1;
166 break;
167 case 'g':
168 f_group = 1;
169 break;
170 case 'i':
171 f_inode = 1;
172 break;
173 case 'k':
174 f_kblocks = 1;
175 break;
176 case 'q':
177 f_nonprint = 1;
178 break;
179 case 'r':
180 f_reversesort = 1;
181 break;
182 case 's':
183 f_size = 1;
184 break;
185 case 'T':
186 f_sectime = 1;
187 break;
188 case 't':
189 f_timesort = 1;
190 break;
191 default:
192 case '?':
193 usage();
194 }
195 }
196 argc -= optind;
197 argv += optind;
198
199 /* -d turns off -R */
200 if (f_listdir)
201 f_recursive = 0;
202
203 /* if need to stat files */
204 f_needstat = f_longform || f_recursive || f_timesort ||
205 f_size || f_type;
206
207 /* select a sort function */
208 if (f_reversesort) {
209 if (!f_timesort)
210 sortfcn = revnamecmp;
211 else if (f_accesstime)
212 sortfcn = revacccmp;
213 else if (f_statustime)
214 sortfcn = revstatcmp;
215 else /* use modification time */
216 sortfcn = revmodcmp;
217 } else {
218 if (!f_timesort)
219 sortfcn = namecmp;
220 else if (f_accesstime)
221 sortfcn = acccmp;
222 else if (f_statustime)
223 sortfcn = statcmp;
224 else /* use modification time */
225 sortfcn = modcmp;
226 }
227
228 /* select a print function */
229 if (f_singlecol)
230 printfcn = printscol;
231 else if (f_longform)
232 printfcn = printlong;
233 else
234 printfcn = printcol;
235
236 /* if -l, -d, -R, or -F, and not ignoring the link, use lstat() */
237 statfcn =
238 (f_longform || f_listdir || f_type || f_recursive) && !f_ignorelink ? lstat : stat;
239
240 if (!argc) {
241 static char dot[] = ".";
242
243 argc = 1;
244 argv[0] = dot;
245 argv[1] = NULL;
246 }
247 doargs(argc, argv);
248 exit(0);
249}
250
251static char path[MAXPATHLEN + 1];
252static char *endofpath = path;
253
254doargs(argc, argv)
255 int argc;
256 char **argv;
257{
258 register LS *dstatp, *rstatp;
259 register int cnt, dircnt, maxlen, regcnt;
260 LS *dstats, *rstats;
261 struct stat sb;
262 char top[MAXPATHLEN + 1];
263 u_long blocks;
264
265 /*
266 * walk through the operands, building separate arrays of LS
267 * structures for directory and non-directory files.
268 */
269 dstats = rstats = NULL;
270 for (dircnt = regcnt = 0; *argv; ++argv) {
271 if (statfcn(*argv, &sb) &&
272 (statfcn == lstat || lstat(*argv, &sb))) {
273 (void)fprintf(stderr,
274 "ls: %s: %s\n", *argv, strerror(errno));
275 if (errno == ENOENT)
276 continue;
277 exit(1);
278 }
279 if (S_ISDIR(sb.st_mode) && !f_listdir) {
280 if (!dstats)
281 dstatp = dstats = (LS *)emalloc((u_int)argc *
282 (sizeof(LS)));
283 dstatp->name = *argv;
284 dstatp->lstat = sb;
285 ++dstatp;
286 ++dircnt;
287 }
288 else {
289 if (!rstats) {
290 rstatp = rstats = (LS *)emalloc((u_int)argc *
291 (sizeof(LS)));
292 blocks = 0;
293 maxlen = -1;
294 }
295 rstatp->name = *argv;
296 rstatp->lstat = sb;
297
298 /* save name length for -C format */
299 rstatp->len = strlen(*argv);
300
301 if (f_nonprint)
302 prcopy(*argv, *argv, rstatp->len);
303
304 /* calculate number of blocks if -l/-s formats */
305 if (f_longform || f_size)
306 blocks += sb.st_blocks;
307
308 /* save max length if -C format */
309 if (f_column && maxlen < rstatp->len)
310 maxlen = rstatp->len;
311
312 ++rstatp;
313 ++regcnt;
314 }
315 }
316 /* display regular files */
317 if (regcnt) {
318 rstats[0].lstat.st_btotal = blocks;
319 rstats[0].lstat.st_maxlen = maxlen;
320 displaydir(rstats, regcnt);
321 f_newline = f_dirname = 1;
322 }
323 /* display directories */
324 if (dircnt) {
325 register char *p;
326
327 f_total = 1;
328 if (dircnt > 1) {
329 (void)getwd(top);
330 qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
331 f_dirname = 1;
332 }
333 for (cnt = 0; cnt < dircnt; ++dstats) {
334 for (endofpath = path, p = dstats->name;
335 *endofpath = *p++; ++endofpath);
336 subdir(dstats);
337 f_newline = 1;
338 if (++cnt < dircnt && chdir(top)) {
339 (void)fprintf(stderr, "ls: %s: %s\n",
340 top, strerror(errno));
341 exit(1);
342 }
343 }
344 }
345}
346
347displaydir(stats, num)
348 LS *stats;
349 register int num;
350{
351 register char *p, *savedpath;
352 LS *lp;
353
354 if (num > 1 && !f_nosort) {
355 u_long save1, save2;
356
357 save1 = stats[0].lstat.st_btotal;
358 save2 = stats[0].lstat.st_maxlen;
359 qsort((char *)stats, num, sizeof(LS), sortfcn);
360 stats[0].lstat.st_btotal = save1;
361 stats[0].lstat.st_maxlen = save2;
362 }
363
364 printfcn(stats, num);
365
366 if (f_recursive) {
367 savedpath = endofpath;
368 for (lp = stats; num--; ++lp) {
369 if (!S_ISDIR(lp->lstat.st_mode))
370 continue;
371 p = lp->name;
372 if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2]))
373 continue;
374 if (endofpath != path && endofpath[-1] != '/')
375 *endofpath++ = '/';
376 for (; *endofpath = *p++; ++endofpath);
377 f_newline = f_dirname = f_total = 1;
378 subdir(lp);
379 *(endofpath = savedpath) = '\0';
380 }
381 }
382}
383
384subdir(lp)
385 LS *lp;
386{
387 LS *stats;
388 int num;
389 char *names;
390
391 if (f_newline)
392 (void)putchar('\n');
393 if (f_dirname)
394 (void)printf("%s:\n", path);
395
396 if (chdir(lp->name)) {
397 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
398 strerror(errno));
399 return;
400 }
401 if (num = tabdir(lp, &stats, &names)) {
402 displaydir(stats, num);
403 (void)free((char *)stats);
404 (void)free((char *)names);
405 }
406 if (chdir("..")) {
407 (void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
408 exit(1);
409 }
410}
411
412tabdir(lp, s_stats, s_names)
413 LS *lp, **s_stats;
414 char **s_names;
415{
416 register DIR *dirp;
417 register int cnt, maxentry, maxlen;
418 register char *p, *names;
419 struct dirent *dp;
420 u_long blocks;
421 LS *stats;
422
423 if (!(dirp = opendir("."))) {
424 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
425 strerror(errno));
426 return(0);
427 }
428 blocks = maxentry = maxlen = 0;
429 stats = NULL;
430 for (cnt = 0; dp = readdir(dirp);) {
431 /* this does -A and -a */
432 p = dp->d_name;
433 if (p[0] == '.') {
434 if (!f_listdot)
435 continue;
436 if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2]))
437 continue;
438 }
439 if (cnt == maxentry) {
440 if (!maxentry)
441 *s_names = names =
442 emalloc((u_int)lp->lstat.st_size);
443#define DEFNUM 256
444 maxentry += DEFNUM;
445 if (!(*s_stats = stats = (LS *)realloc((char *)stats,
446 (u_int)maxentry * sizeof(LS))))
447 nomem();
448 }
449 if (f_needstat && statfcn(dp->d_name, &stats[cnt].lstat) &&
450 statfcn == stat && lstat(dp->d_name, &stats[cnt].lstat)) {
451 /*
452 * don't exit -- this could be an NFS mount that has
453 * gone away. Flush stdout so the messages line up.
454 */
455 (void)fflush(stdout);
456 (void)fprintf(stderr,
457 "ls: %s: %s\n", dp->d_name, strerror(errno));
458 continue;
459 }
460 stats[cnt].name = names;
461
462 if (f_nonprint)
463 prcopy(dp->d_name, names, (int)dp->d_namlen);
464 else
465 bcopy(dp->d_name, names, (int)dp->d_namlen);
466 names += dp->d_namlen;
467 *names++ = '\0';
468
469 /*
470 * get the inode from the directory, so the -f flag
471 * works right.
472 */
473 stats[cnt].lstat.st_ino = dp->d_ino;
474
475 /* save name length for -C format */
476 stats[cnt].len = dp->d_namlen;
477
478 /* calculate number of blocks if -l/-s formats */
479 if (f_longform || f_size)
480 blocks += stats[cnt].lstat.st_blocks;
481
482 /* save max length if -C format */
483 if (f_column && maxlen < (int)dp->d_namlen)
484 maxlen = dp->d_namlen;
485 ++cnt;
486 }
487 (void)closedir(dirp);
488
489 if (cnt) {
490 stats[0].lstat.st_btotal = blocks;
491 stats[0].lstat.st_maxlen = maxlen;
492 } else if (stats) {
493 (void)free((char *)stats);
494 (void)free((char *)names);
495 }
496 return(cnt);
497}