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