ls.c revision 1.1.1.1
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
44static 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
56int (*sortfcn)(), (*printfcn)();
57int lstat();
58char *emalloc();
59
60int termwidth = 80;		/* default terminal width */
61
62/* flags */
63int f_accesstime;		/* use time of last access */
64int f_column;			/* columnated format */
65int f_group;			/* show group ownership of a file */
66int f_ignorelink;		/* indirect through symbolic link operands */
67int f_inode;			/* print inode */
68int f_kblocks;			/* print size in kilobytes */
69int f_listalldot;		/* list . and .. as well */
70int f_listdir;			/* list actual directory, not contents */
71int f_listdot;			/* list files beginning with . */
72int f_longform;			/* long listing format */
73int f_needstat;			/* if need to stat files */
74int f_newline;			/* if precede with newline */
75int f_nonprint;			/* show unprintables as ? */
76int f_nosort;			/* don't sort output */
77int f_recursive;		/* ls subdirectories also */
78int f_reversesort;		/* reverse whatever sort is used */
79int f_sectime;			/* print the real time for all files */
80int f_singlecol;		/* use single column output */
81int f_size;			/* list size in short listing */
82int f_statustime;		/* use time of last mode change */
83int f_dirname;			/* if precede with directory name */
84int f_timesort;			/* sort by time vice name */
85int f_total;			/* if precede with "total" line */
86int f_type;			/* add type character for non-regular files */
87
88int (*statfcn)(), stat(), lstat();
89
90main(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
251static char path[MAXPATHLEN + 1];
252static char *endofpath = path;
253
254doargs(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[MAXPATHLEN + 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
347displaydir(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
384subdir(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
412tabdir(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