ls.c revision 1.4 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[] = "$Header: /tank/opengrok/rsync2/NetBSD/src/bin/ls/ls.c,v 1.4 1993/05/24 07:15:03 deraadt 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 *nargv[2] = { ".", NULL };
243
244 doargs(1, nargv);
245 } else
246 doargs(argc, argv);
247 exit(0);
248 }
249
250 static char path[MAXPATHLEN + 1];
251 static char *endofpath = path;
252
253 doargs(argc, argv)
254 int argc;
255 char **argv;
256 {
257 register LS *dstatp, *rstatp;
258 register int cnt, dircnt, maxlen, regcnt;
259 LS *dstats, *rstats;
260 struct stat sb;
261 char top[MAXPATHLEN + 1];
262 u_long blocks;
263
264 /*
265 * walk through the operands, building separate arrays of LS
266 * structures for directory and non-directory files.
267 */
268 dstats = rstats = NULL;
269 for (dircnt = regcnt = 0; *argv; ++argv) {
270 if (statfcn(*argv, &sb) &&
271 (statfcn == lstat || lstat(*argv, &sb))) {
272 (void)fprintf(stderr,
273 "ls: %s: %s\n", *argv, strerror(errno));
274 if (errno == ENOENT)
275 continue;
276 exit(1);
277 }
278 if (S_ISDIR(sb.st_mode) && !f_listdir) {
279 if (!dstats)
280 dstatp = dstats = (LS *)emalloc((u_int)argc *
281 (sizeof(LS)));
282 dstatp->name = *argv;
283 dstatp->lstat = sb;
284 ++dstatp;
285 ++dircnt;
286 }
287 else {
288 if (!rstats) {
289 rstatp = rstats = (LS *)emalloc((u_int)argc *
290 (sizeof(LS)));
291 blocks = 0;
292 maxlen = -1;
293 }
294 rstatp->name = *argv;
295 rstatp->lstat = sb;
296
297 /* save name length for -C format */
298 rstatp->len = strlen(*argv);
299
300 if (f_nonprint)
301 prcopy(*argv, *argv, rstatp->len);
302
303 /* calculate number of blocks if -l/-s formats */
304 if (f_longform || f_size)
305 blocks += sb.st_blocks;
306
307 /* save max length if -C format */
308 if (f_column && maxlen < rstatp->len)
309 maxlen = rstatp->len;
310
311 ++rstatp;
312 ++regcnt;
313 }
314 }
315 /* display regular files */
316 if (regcnt) {
317 rstats[0].lstat.st_btotal = blocks;
318 rstats[0].lstat.st_maxlen = maxlen;
319 displaydir(rstats, regcnt);
320 f_newline = f_dirname = 1;
321 }
322 /* display directories */
323 if (dircnt) {
324 register char *p;
325
326 f_total = 1;
327 if (dircnt > 1) {
328 (void)getwd(top);
329 qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
330 f_dirname = 1;
331 }
332 for (cnt = 0; cnt < dircnt; ++dstats) {
333 for (endofpath = path, p = dstats->name;
334 *endofpath = *p++; ++endofpath);
335 subdir(dstats);
336 f_newline = 1;
337 if (++cnt < dircnt && chdir(top)) {
338 (void)fprintf(stderr, "ls: %s: %s\n",
339 top, strerror(errno));
340 exit(1);
341 }
342 }
343 }
344 }
345
346 displaydir(stats, num)
347 LS *stats;
348 register int num;
349 {
350 register char *p, *savedpath;
351 LS *lp;
352
353 if (num > 1 && !f_nosort) {
354 u_long save1, save2;
355
356 save1 = stats[0].lstat.st_btotal;
357 save2 = stats[0].lstat.st_maxlen;
358 qsort((char *)stats, num, sizeof(LS), sortfcn);
359 stats[0].lstat.st_btotal = save1;
360 stats[0].lstat.st_maxlen = save2;
361 }
362
363 printfcn(stats, num);
364
365 if (f_recursive) {
366 savedpath = endofpath;
367 for (lp = stats; num--; ++lp) {
368 if (!S_ISDIR(lp->lstat.st_mode))
369 continue;
370 p = lp->name;
371 if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2]))
372 continue;
373 if (endofpath != path && endofpath[-1] != '/')
374 *endofpath++ = '/';
375 for (; *endofpath = *p++; ++endofpath);
376 f_newline = f_dirname = f_total = 1;
377 subdir(lp);
378 *(endofpath = savedpath) = '\0';
379 }
380 }
381 }
382
383 subdir(lp)
384 LS *lp;
385 {
386 LS *stats;
387 int num;
388 char *names;
389
390 if (f_newline)
391 (void)putchar('\n');
392 if (f_dirname)
393 (void)printf("%s:\n", path);
394
395 if (chdir(lp->name)) {
396 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
397 strerror(errno));
398 return;
399 }
400 if (num = tabdir(lp, &stats, &names)) {
401 displaydir(stats, num);
402 (void)free((char *)stats);
403 (void)free((char *)names);
404 }
405 if (chdir("..")) {
406 (void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
407 exit(1);
408 }
409 }
410
411 tabdir(lp, s_stats, s_names)
412 LS *lp, **s_stats;
413 char **s_names;
414 {
415 register DIR *dirp;
416 register int cnt, maxentry, maxlen;
417 register char *p, *names;
418 struct dirent *dp;
419 u_long blocks;
420 LS *stats;
421
422 if (!(dirp = opendir("."))) {
423 (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
424 strerror(errno));
425 return(0);
426 }
427 blocks = maxentry = maxlen = 0;
428 stats = NULL;
429 for (cnt = 0; dp = readdir(dirp);) {
430 /* this does -A and -a */
431 p = dp->d_name;
432 if (p[0] == '.') {
433 if (!f_listdot)
434 continue;
435 if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2]))
436 continue;
437 }
438 if (cnt == maxentry) {
439 if (!maxentry)
440 *s_names = names =
441 emalloc((u_int)lp->lstat.st_size);
442 #define DEFNUM 256
443 maxentry += DEFNUM;
444 if (!(*s_stats = stats = (LS *)realloc((char *)stats,
445 (u_int)maxentry * sizeof(LS))))
446 nomem();
447 }
448 if (f_needstat && statfcn(dp->d_name, &stats[cnt].lstat) &&
449 statfcn == stat && lstat(dp->d_name, &stats[cnt].lstat)) {
450 /*
451 * don't exit -- this could be an NFS mount that has
452 * gone away. Flush stdout so the messages line up.
453 */
454 (void)fflush(stdout);
455 (void)fprintf(stderr,
456 "ls: %s: %s\n", dp->d_name, strerror(errno));
457 continue;
458 }
459 stats[cnt].name = names;
460
461 if (f_nonprint)
462 prcopy(dp->d_name, names, (int)dp->d_namlen);
463 else
464 bcopy(dp->d_name, names, (int)dp->d_namlen);
465 names += dp->d_namlen;
466 *names++ = '\0';
467
468 /*
469 * get the inode from the directory, so the -f flag
470 * works right.
471 */
472 stats[cnt].lstat.st_ino = dp->d_ino;
473
474 /* save name length for -C format */
475 stats[cnt].len = dp->d_namlen;
476
477 /* calculate number of blocks if -l/-s formats */
478 if (f_longform || f_size)
479 blocks += stats[cnt].lstat.st_blocks;
480
481 /* save max length if -C format */
482 if (f_column && maxlen < (int)dp->d_namlen)
483 maxlen = dp->d_namlen;
484 ++cnt;
485 }
486 (void)closedir(dirp);
487
488 if (cnt) {
489 stats[0].lstat.st_btotal = blocks;
490 stats[0].lstat.st_maxlen = maxlen;
491 } else if (stats) {
492 (void)free((char *)stats);
493 (void)free((char *)names);
494 }
495 return(cnt);
496 }
497