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