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