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