ls.c revision 1.6
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
38char 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[] = "from: @(#)ls.c	5.48 (Berkeley) 4/3/91";*/
45static char rcsid[] = "$Id: ls.c,v 1.6 1993/08/01 18:59:29 mycroft 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
57int (*sortfcn)(), (*printfcn)();
58int lstat();
59char *emalloc();
60
61int termwidth = 80;		/* default terminal width */
62
63/* flags */
64int f_accesstime;		/* use time of last access */
65int f_column;			/* columnated format */
66int f_group;			/* show group ownership of a file */
67int f_ignorelink;		/* indirect through symbolic link operands */
68int f_inode;			/* print inode */
69int f_kblocks;			/* print size in kilobytes */
70int f_listalldot;		/* list . and .. as well */
71int f_listdir;			/* list actual directory, not contents */
72int f_listdot;			/* list files beginning with . */
73int f_longform;			/* long listing format */
74int f_needstat;			/* if need to stat files */
75int f_newline;			/* if precede with newline */
76int f_nonprint;			/* show unprintables as ? */
77int f_nosort;			/* don't sort output */
78int f_recursive;		/* ls subdirectories also */
79int f_reversesort;		/* reverse whatever sort is used */
80int f_sectime;			/* print the real time for all files */
81int f_singlecol;		/* use single column output */
82int f_size;			/* list size in short listing */
83int f_statustime;		/* use time of last mode change */
84int f_dirname;			/* if precede with directory name */
85int f_timesort;			/* sort by time vice name */
86int f_total;			/* if precede with "total" line */
87int f_type;			/* add type character for non-regular files */
88
89int (*statfcn)(), stat(), lstat();
90
91main(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];
243		char dot[2];
244
245		strcpy(dot, ".");
246		nargv[0] = dot;
247		doargs(1, nargv);
248	} else
249		doargs(argc, argv);
250	exit(0);
251}
252
253static char path[MAXPATHLEN + 1];
254static char *endofpath = path;
255
256doargs(argc, argv)
257	int argc;
258	char **argv;
259{
260	register LS *dstatp, *rstatp;
261	register int cnt, dircnt, maxlen, regcnt;
262	LS *dstats, *rstats;
263	struct stat sb;
264	char top[MAXPATHLEN + 1];
265	u_long blocks;
266
267	/*
268	 * walk through the operands, building separate arrays of LS
269	 * structures for directory and non-directory files.
270	 */
271	dstats = rstats = NULL;
272	for (dircnt = regcnt = 0; *argv; ++argv) {
273		if (statfcn(*argv, &sb) &&
274		    (statfcn == lstat || lstat(*argv, &sb))) {
275			(void)fprintf(stderr,
276			    "ls: %s: %s\n", *argv, strerror(errno));
277			if (errno == ENOENT)
278				continue;
279			exit(1);
280		}
281		if (S_ISDIR(sb.st_mode) && !f_listdir) {
282			if (!dstats)
283				dstatp = dstats = (LS *)emalloc((u_int)argc *
284				    (sizeof(LS)));
285			dstatp->name = *argv;
286			dstatp->lstat = sb;
287			++dstatp;
288			++dircnt;
289		}
290		else {
291			if (!rstats) {
292				rstatp = rstats = (LS *)emalloc((u_int)argc *
293				    (sizeof(LS)));
294				blocks = 0;
295				maxlen = -1;
296			}
297			rstatp->name = *argv;
298			rstatp->lstat = sb;
299
300			/* save name length for -C format */
301			rstatp->len = strlen(*argv);
302
303			if (f_nonprint)
304				prcopy(*argv, *argv, rstatp->len);
305
306			/* calculate number of blocks if -l/-s formats */
307			if (f_longform || f_size)
308				blocks += sb.st_blocks;
309
310			/* save max length if -C format */
311			if (f_column && maxlen < rstatp->len)
312				maxlen = rstatp->len;
313
314			++rstatp;
315			++regcnt;
316		}
317	}
318	/* display regular files */
319	if (regcnt) {
320		rstats[0].lstat.st_btotal = blocks;
321		rstats[0].lstat.st_maxlen = maxlen;
322		displaydir(rstats, regcnt);
323		f_newline = f_dirname = 1;
324	}
325	/* display directories */
326	if (dircnt) {
327		register char *p;
328
329		f_total = 1;
330		if (dircnt > 1) {
331			(void)getwd(top);
332			qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
333			f_dirname = 1;
334		}
335		for (cnt = 0; cnt < dircnt; ++dstats) {
336			for (endofpath = path, p = dstats->name;
337			    *endofpath = *p++; ++endofpath);
338			subdir(dstats);
339			f_newline = 1;
340			if (++cnt < dircnt && chdir(top)) {
341				(void)fprintf(stderr, "ls: %s: %s\n",
342				    top, strerror(errno));
343				exit(1);
344			}
345		}
346	}
347}
348
349displaydir(stats, num)
350	LS *stats;
351	register int num;
352{
353	register char *p, *savedpath;
354	LS *lp;
355
356	if (num > 1 && !f_nosort) {
357		u_long save1, save2;
358
359		save1 = stats[0].lstat.st_btotal;
360		save2 = stats[0].lstat.st_maxlen;
361		qsort((char *)stats, num, sizeof(LS), sortfcn);
362		stats[0].lstat.st_btotal = save1;
363		stats[0].lstat.st_maxlen = save2;
364	}
365
366	printfcn(stats, num);
367
368	if (f_recursive) {
369		savedpath = endofpath;
370		for (lp = stats; num--; ++lp) {
371			if (!S_ISDIR(lp->lstat.st_mode))
372				continue;
373			p = lp->name;
374			if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2]))
375				continue;
376			if (endofpath != path && endofpath[-1] != '/')
377				*endofpath++ = '/';
378			for (; *endofpath = *p++; ++endofpath);
379			f_newline = f_dirname = f_total = 1;
380			subdir(lp);
381			*(endofpath = savedpath) = '\0';
382		}
383	}
384}
385
386subdir(lp)
387	LS *lp;
388{
389	LS *stats;
390	int num;
391	char *names;
392
393	if (f_newline)
394		(void)putchar('\n');
395	if (f_dirname)
396		(void)printf("%s:\n", path);
397
398	if (chdir(lp->name)) {
399		(void)fprintf(stderr, "ls: %s: %s\n", lp->name,
400		     strerror(errno));
401		return;
402	}
403	if (num = tabdir(lp, &stats, &names)) {
404		displaydir(stats, num);
405		(void)free((char *)stats);
406		(void)free((char *)names);
407	}
408	if (chdir("..")) {
409		(void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
410		exit(1);
411	}
412}
413
414tabdir(lp, s_stats, s_names)
415	LS *lp, **s_stats;
416	char **s_names;
417{
418	register DIR *dirp;
419	register int cnt, maxentry, maxlen;
420	register char *p, *names;
421	struct dirent *dp;
422	u_long blocks;
423	LS *stats;
424
425	if (!(dirp = opendir("."))) {
426		(void)fprintf(stderr, "ls: %s: %s\n", lp->name,
427		    strerror(errno));
428		return(0);
429	}
430	blocks = maxentry = maxlen = 0;
431	stats = NULL;
432	for (cnt = 0; dp = readdir(dirp);) {
433		/* this does -A and -a */
434		p = dp->d_name;
435		if (p[0] == '.') {
436			if (!f_listdot)
437				continue;
438			if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2]))
439				continue;
440		}
441		if (cnt == maxentry) {
442			if (!maxentry)
443				*s_names = names =
444				    emalloc((u_int)lp->lstat.st_size);
445#define	DEFNUM	256
446			maxentry += DEFNUM;
447			if (!(*s_stats = stats = (LS *)realloc((char *)stats,
448			    (u_int)maxentry * sizeof(LS))))
449				nomem();
450		}
451		if (f_needstat && statfcn(dp->d_name, &stats[cnt].lstat) &&
452		    statfcn == stat && lstat(dp->d_name, &stats[cnt].lstat)) {
453			/*
454			 * don't exit -- this could be an NFS mount that has
455			 * gone away.  Flush stdout so the messages line up.
456			 */
457			(void)fflush(stdout);
458			(void)fprintf(stderr,
459			    "ls: %s: %s\n", dp->d_name, strerror(errno));
460			continue;
461		}
462		stats[cnt].name = names;
463
464		if (f_nonprint)
465			prcopy(dp->d_name, names, (int)dp->d_namlen);
466		else
467			bcopy(dp->d_name, names, (int)dp->d_namlen);
468		names += dp->d_namlen;
469		*names++ = '\0';
470
471		/*
472		 * get the inode from the directory, so the -f flag
473		 * works right.
474		 */
475		stats[cnt].lstat.st_ino = dp->d_ino;
476
477		/* save name length for -C format */
478		stats[cnt].len = dp->d_namlen;
479
480		/* calculate number of blocks if -l/-s formats */
481		if (f_longform || f_size)
482			blocks += stats[cnt].lstat.st_blocks;
483
484		/* save max length if -C format */
485		if (f_column && maxlen < (int)dp->d_namlen)
486			maxlen = dp->d_namlen;
487		++cnt;
488	}
489	(void)closedir(dirp);
490
491	if (cnt) {
492		stats[0].lstat.st_btotal = blocks;
493		stats[0].lstat.st_maxlen = maxlen;
494	} else if (stats) {
495		(void)free((char *)stats);
496		(void)free((char *)names);
497	}
498	return(cnt);
499}
500