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