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