ls.c revision 1.2 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
38 char 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
44 static char sccsid[] = "@(#)ls.c 5.48 (Berkeley) 4/3/91";
45 static char rcsid[] = "$Id: ls.c,v 1.2 1993/03/22 08:04:00 cgd Exp $";
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
57 int (*sortfcn)(), (*printfcn)();
58 int lstat();
59 char *emalloc();
60
61 int termwidth = 80; /* default terminal width */
62
63 /* flags */
64 int f_accesstime; /* use time of last access */
65 int f_column; /* columnated format */
66 int f_group; /* show group ownership of a file */
67 int f_ignorelink; /* indirect through symbolic link operands */
68 int f_inode; /* print inode */
69 int f_kblocks; /* print size in kilobytes */
70 int f_listalldot; /* list . and .. as well */
71 int f_listdir; /* list actual directory, not contents */
72 int f_listdot; /* list files beginning with . */
73 int f_longform; /* long listing format */
74 int f_needstat; /* if need to stat files */
75 int f_newline; /* if precede with newline */
76 int f_nonprint; /* show unprintables as ? */
77 int f_nosort; /* don't sort output */
78 int f_recursive; /* ls subdirectories also */
79 int f_reversesort; /* reverse whatever sort is used */
80 int f_sectime; /* print the real time for all files */
81 int f_singlecol; /* use single column output */
82 int f_size; /* list size in short listing */
83 int f_statustime; /* use time of last mode change */
84 int f_dirname; /* if precede with directory name */
85 int f_timesort; /* sort by time vice name */
86 int f_total; /* if precede with "total" line */
87 int f_type; /* add type character for non-regular files */
88
89 int (*statfcn)(), stat(), lstat();
90
91 main(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) {
242 static char dot[] = ".";
243
244 argc = 1;
245 argv[0] = dot;
246 argv[1] = NULL;
247 }
248 doargs(argc, argv);
249 exit(0);
250 }
251
252 static char path[MAXPATHLEN + 1];
253 static char *endofpath = path;
254
255 doargs(argc, argv)
256 int argc;
257 char **argv;
258 {
259 register LS *dstatp, *rstatp;
260 register int cnt, dircnt, maxlen, regcnt;
261 LS *dstats, *rstats;
262 struct stat sb;
263 char top[MAXPATHLEN + 1];
264 u_long blocks;
265
266 /*
267 * walk through the operands, building separate arrays of LS
268 * structures for directory and non-directory files.
269 */
270 dstats = rstats = NULL;
271 for (dircnt = regcnt = 0; *argv; ++argv) {
272 if (statfcn(*argv, &sb) &&
273 (statfcn == lstat || lstat(*argv, &sb))) {
274 (void)fprintf(stderr,
275 "ls: %s: %s\n", *argv, strerror(errno));
276 if (errno == ENOENT)
277 continue;
278 exit(1);
279 }
280 if (S_ISDIR(sb.st_mode) && !f_listdir) {
281 if (!dstats)
282 dstatp = dstats = (LS *)emalloc((u_int)argc *
283 (sizeof(LS)));
284 dstatp->name = *argv;
285 dstatp->lstat = sb;
286 ++dstatp;
287 ++dircnt;
288 }
289 else {
290 if (!rstats) {
291 rstatp = rstats = (LS *)emalloc((u_int)argc *
292 (sizeof(LS)));
293 blocks = 0;
294 maxlen = -1;
295 }
296 rstatp->name = *argv;
297 rstatp->lstat = sb;
298
299 /* save name length for -C format */
300 rstatp->len = strlen(*argv);
301
302 if (f_nonprint)
303 prcopy(*argv, *argv, rstatp->len);
304
305 /* calculate number of blocks if -l/-s formats */
306 if (f_longform || f_size)
307 blocks += sb.st_blocks;
308
309 /* save max length if -C format */
310 if (f_column && maxlen < rstatp->len)
311 maxlen = rstatp->len;
312
313 ++rstatp;
314 ++regcnt;
315 }
316 }
317 /* display regular files */
318 if (regcnt) {
319 rstats[0].lstat.st_btotal = blocks;
320 rstats[0].lstat.st_maxlen = maxlen;
321 displaydir(rstats, regcnt);
322 f_newline = f_dirname = 1;
323 }
324 /* display directories */
325 if (dircnt) {
326 register char *p;
327
328 f_total = 1;
329 if (dircnt > 1) {
330 (void)getwd(top);
331 qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
332 f_dirname = 1;
333 }
334 for (cnt = 0; cnt < dircnt; ++dstats) {
335 for (endofpath = path, p = dstats->name;
336 *endofpath = *p++; ++endofpath);
337 subdir(dstats);
338 f_newline = 1;
339 if (++cnt < dircnt && chdir(top)) {
340 (void)fprintf(stderr, "ls: %s: %s\n",
341 top, strerror(errno));
342 exit(1);
343 }
344 }
345 }
346 }
347
348 displaydir(stats, num)
349 LS *stats;
350 register int num;
351 {
352 register char *p, *savedpath;
353 LS *lp;
354
355 if (num > 1 && !f_nosort) {
356 u_long save1, save2;
357
358 save1 = stats[0].lstat.st_btotal;
359 save2 = stats[0].lstat.st_maxlen;
360 qsort((char *)stats, num, sizeof(LS), sortfcn);
361 stats[0].lstat.st_btotal = save1;
362 stats[0].lstat.st_maxlen = save2;
363 }
364
365 printfcn(stats, num);
366
367 if (f_recursive) {
368 savedpath = endofpath;
369 for (lp = stats; num--; ++lp) {
370 if (!S_ISDIR(lp->lstat.st_mode))
371 continue;
372 p = lp->name;
373 if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2]))
374 continue;
375 if (endofpath != path && endofpath[-1] != '/')
376 *endofpath++ = '/';
377 for (; *endofpath = *p++; ++endofpath);
378 f_newline = f_dirname = f_total = 1;
379 subdir(lp);
380 *(endofpath = savedpath) = '\0';
381 }
382 }
383 }
384
385 subdir(lp)
386 LS *lp;
387 {
388 LS *stats;
389 int num;
390 char *names;
391
392 if (f_newline)
393 (void)putchar('\n');
394 if (f_dirname)
395 (void)printf("%s:\n", path);
396
397 if (chdir(lp->name)) {
398 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
399 strerror(errno));
400 return;
401 }
402 if (num = tabdir(lp, &stats, &names)) {
403 displaydir(stats, num);
404 (void)free((char *)stats);
405 (void)free((char *)names);
406 }
407 if (chdir("..")) {
408 (void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
409 exit(1);
410 }
411 }
412
413 tabdir(lp, s_stats, s_names)
414 LS *lp, **s_stats;
415 char **s_names;
416 {
417 register DIR *dirp;
418 register int cnt, maxentry, maxlen;
419 register char *p, *names;
420 struct dirent *dp;
421 u_long blocks;
422 LS *stats;
423
424 if (!(dirp = opendir("."))) {
425 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
426 strerror(errno));
427 return(0);
428 }
429 blocks = maxentry = maxlen = 0;
430 stats = NULL;
431 for (cnt = 0; dp = readdir(dirp);) {
432 /* this does -A and -a */
433 p = dp->d_name;
434 if (p[0] == '.') {
435 if (!f_listdot)
436 continue;
437 if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2]))
438 continue;
439 }
440 if (cnt == maxentry) {
441 if (!maxentry)
442 *s_names = names =
443 emalloc((u_int)lp->lstat.st_size);
444 #define DEFNUM 256
445 maxentry += DEFNUM;
446 if (!(*s_stats = stats = (LS *)realloc((char *)stats,
447 (u_int)maxentry * sizeof(LS))))
448 nomem();
449 }
450 if (f_needstat && statfcn(dp->d_name, &stats[cnt].lstat) &&
451 statfcn == stat && lstat(dp->d_name, &stats[cnt].lstat)) {
452 /*
453 * don't exit -- this could be an NFS mount that has
454 * gone away. Flush stdout so the messages line up.
455 */
456 (void)fflush(stdout);
457 (void)fprintf(stderr,
458 "ls: %s: %s\n", dp->d_name, strerror(errno));
459 continue;
460 }
461 stats[cnt].name = names;
462
463 if (f_nonprint)
464 prcopy(dp->d_name, names, (int)dp->d_namlen);
465 else
466 bcopy(dp->d_name, names, (int)dp->d_namlen);
467 names += dp->d_namlen;
468 *names++ = '\0';
469
470 /*
471 * get the inode from the directory, so the -f flag
472 * works right.
473 */
474 stats[cnt].lstat.st_ino = dp->d_ino;
475
476 /* save name length for -C format */
477 stats[cnt].len = dp->d_namlen;
478
479 /* calculate number of blocks if -l/-s formats */
480 if (f_longform || f_size)
481 blocks += stats[cnt].lstat.st_blocks;
482
483 /* save max length if -C format */
484 if (f_column && maxlen < (int)dp->d_namlen)
485 maxlen = dp->d_namlen;
486 ++cnt;
487 }
488 (void)closedir(dirp);
489
490 if (cnt) {
491 stats[0].lstat.st_btotal = blocks;
492 stats[0].lstat.st_maxlen = maxlen;
493 } else if (stats) {
494 (void)free((char *)stats);
495 (void)free((char *)names);
496 }
497 return(cnt);
498 }
499