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