1 /*	ls 4.1 - List files.				Author: Kees J. Bot
2  *								25 Apr 1989
3  */
4 
5 /*
6  * Changes by Gunnar Ritter, Freiburg i. Br., Germany, October 2001. Taken
7  * from the MINIX sources:
8  */
9 
10 /*
11  * Copyright (c) 1987,1997, Prentice Hall All rights reserved.
12  *
13  * Redistribution and use of the MINIX operating system in source and binary
14  * forms, with or without modification, are permitted provided that the
15  * following conditions are met:
16  *
17  * Redistributions of source code must retain the above copyright notice, this
18  * list of conditions and the following disclaimer.
19  *
20  * Redistributions in binary form must reproduce the above copyright notice,
21  * this list of conditions and the following disclaimer in the documentation
22  * and/or other materials provided with the distribution.
23  *
24  * Neither the name of Prentice Hall nor the names of the software authors or
25  * contributors may be used to endorse or promote products derived from this
26  * software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRENTICE HALL OR ANY
32  * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
34  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
42 #define	USED	__attribute__ ((used))
43 #elif defined __GNUC__
44 #define	USED	__attribute__ ((unused))
45 #else
46 #define	USED
47 #endif
48 #if defined (SU3)
49 static const char sccsid[] USED = "@(#)ls_su3.sl	1.78 (gritter) 4/17/07>";
50 #elif defined (SUS)
51 static const char sccsid[] USED = "@(#)ls_sus.sl	1.78 (gritter) 4/17/07>";
52 #elif defined (UCB)
53 static const char sccsid[] USED = "@(#)/usr/ucb/ls.sl	1.78 (gritter) 4/17/07>";
54 #else
55 static const char sccsid[] USED = "@(#)ls.sl	1.78 (gritter) 4/17/07>";
56 #endif
57 
58 /*
59  * About the amount of bytes for heap + stack under Minix:
60  * Ls needs a average amount of 42 bytes per unserviced directory entry, so
61  * scanning 10 directory levels deep in an ls -R with 100 entries per directory
62  * takes 42000 bytes of heap.  So giving ls 10000 bytes is tight, 20000 is
63  * usually enough, 40000 is pessimistic.
64  */
65 
66 /* The array ifmt_c[] is used in an 'ls -l' to map the type of a file to a
67  * letter.  This is done so that ls can list any future file or device type
68  * other than symlinks, without recompilation.  (Yes it's dirty.)
69  */
70 static char ifmt_c[] = "-pc-d-b--nl-SD--";
71 /*                       S_IFIFO
72  *                        S_IFCHR
73  *                          S_IFDIR
74  *                            S_IFBLK
75  *                              S_IFREG
76  *                               S_IFNWK
77  *                                S_IFLNK
78  *                                  S_IFSOCK
79  *                                   S_IFDOOR
80  */
81 
82 #define ifmt(mode)	ifmt_c[((mode) >> 12) & 0xF]
83 
84 #ifndef	USE_TERMCAP
85 #ifdef	sun
86 #include <curses.h>
87 #include <term.h>
88 #endif
89 #endif
90 #define nil 0
91 #include <sys/types.h>
92 #include <sys/stat.h>
93 #include <sys/ioctl.h>
94 #include <stdio.h>
95 #include <string.h>
96 #include <stddef.h>
97 #include <stdlib.h>
98 #include <unistd.h>
99 #include <dirent.h>
100 #include <time.h>
101 #include <pwd.h>
102 #include <grp.h>
103 #include <errno.h>
104 #include <fcntl.h>
105 #include <termios.h>
106 #include <locale.h>
107 #include <limits.h>
108 #include <ctype.h>
109 #include <libgen.h>
110 #include <wchar.h>
111 #include <wctype.h>
112 #include "config.h"
113 #ifndef	USE_TERMCAP
114 #ifndef	sun
115 #include <curses.h>
116 #include <term.h>
117 #endif
118 #else	/* USE_TERMCAP */
119 #include <termcap.h>
120 #endif	/* USE_TERMCAP */
121 
122 #ifdef	_AIX
123 #include <sys/sysmacros.h>
124 #endif
125 
126 #ifndef	S_IFNAM
127 #define	S_IFNAM		0x5000	/* XENIX special named file */
128 #endif
129 #ifndef	S_INSEM
130 #define	S_INSEM		0x1	/* XENIX semaphore subtype of IFNAM */
131 #endif
132 #ifndef	S_INSHD
133 #define	S_INSHD		0x2	/* XENIX shared data subtype of IFNAM */
134 #endif
135 #ifndef	S_IFDOOR
136 #define	S_IFDOOR	0xD000	/* Solaris door */
137 #endif
138 #ifndef	S_IFNWK
139 #define	S_IFNWK		0x9000	/* HP-UX network special */
140 #endif
141 
142 #if !__minix
143 #define SUPER_ID	uid	/* Let -A flag be default for SUPER_ID == 0. */
144 #else
145 #define SUPER_ID	gid
146 #endif
147 
148 #if __minix
149 #define BLOCK	1024
150 #else
151 #define BLOCK	 512
152 #endif
153 
154 /* Assume other systems have st_blocks. */
155 #if !__minix
156 #define ST_BLOCKS 1
157 #endif
158 
159 /* Some terminals ignore more than 80 characters on a line.  Dumb ones wrap
160  * when the cursor hits the side.  Nice terminals don't wrap until they have
161  * to print the 81st character.  Wether we like it or not, no column 80.
162  */
163 static int ncols = 79;
164 
165 #define NSEP	2	/* # spaces between columns. */
166 
167 #define MAXCOLS	150	/* Max # of files per line. */
168 
169 static char *arg0;	/* Last component of argv[0]. */
170 static int uid, gid;	/* callers id. */
171 static int ex = 0;	/* Exit status to be. */
172 static int istty;	/* Output is on a terminal. */
173 static int tinfostat = -1;	/* terminfo is initalized */
174 extern int sysv3;	/* emulate SYSV3 behavior */
175 
176 #ifdef	SU3
177 static struct	visit {
178 	ino_t	v_ino;
179 	dev_t	v_dev;
180 } *visited;
181 static int	vismax;	/* number of members in visited */
182 #endif	/* SU3 */
183 
184 static enum {
185 	PER_LS	= 0,
186 	PER_DIR	= 1
187 } personality = PER_LS;
188 
189 static struct {
190 	const char	*per_opt;
191 } personalities[] = {
192 	/* orig: acdfgilnqrstu1ACFLMRTX */
193 #ifdef	UCB
194 	{ ":1RaAdClgrtucFqisfLSUXhH" },	/* PER_LS */
195 #else	/* !UCB */
196 	{ "1RadCxmnlogrtucpFbqisfLSUXhH" },	/* PER_LS */
197 #endif	/* !UCB */
198 	{ "1RadCxmnlogrtucpFbqiOfLSXhH" }	/* PER_DIR */
199 };
200 
201 /* Safer versions of malloc and realloc: */
202 
203 static void
heaperr(void)204 heaperr(void)
205 {
206 	write(2, "out of memory\n", 14);
207 	exit(077);
208 }
209 
210 /*
211  * Deliver or die.
212  */
213 static void *
salloc(size_t n)214 salloc(size_t n)
215 {
216 	void *a;
217 
218 	if ((a = malloc(n)) == nil)
219 		heaperr();
220 	return a;
221 }
222 
223 static void *
srealloc(void * a,size_t n)224 srealloc(void *a, size_t n)
225 {
226 	if ((a = realloc(a, n)) == nil)
227 		heaperr();
228 	return a;
229 }
230 
231 static char flags[0477];
232 
233 static int
present(int f)234 present(int f)
235 {
236 	return f == 0 || flags[f] != 0;
237 }
238 
239 /*
240  * Like perror(3), but in the style: "ls: junk: No such file or directory.
241  */
242 static void
report(const char * f)243 report(const char *f)
244 {
245 #ifdef	UCB
246 	fprintf(stderr, "%s not found\n", f);
247 #else	/* !UCB */
248 	fprintf(stderr, "%s: %s\n", f, strerror(errno));
249 #endif	/* !UCB */
250 	ex = 1;
251 }
252 
253 #ifdef	UCB
254 #define	blockcount(n)	(((n) & 1 ? (n)+1 : (n)) >> 1)
255 #else
256 #define	blockcount(n)	(n)
257 #endif
258 
259 /*
260  * Two functions, uidname and gidname, translate id's to readable names.
261  * All names are remembered to avoid searching the password file.
262  */
263 #define NNAMES	(1 << (sizeof(int) + sizeof(char *)))
264 enum whatmap { PASSWD, GROUP };
265 
266 static struct idname {		/* Hash list of names. */
267 	struct idname	*next;
268 	char		*name;
269 	uid_t		id;
270 } *uids[NNAMES], *gids[NNAMES];
271 
272 /*
273  * Return name for a given user/group id.
274  */
275 static char *
idname(unsigned id,enum whatmap map)276 idname(unsigned id, enum whatmap map)
277 {
278 	struct idname *i;
279 	struct idname **ids = &(map == PASSWD ? uids : gids)[id % NNAMES];
280 
281 	while ((i = *ids) != nil && id < i->id)
282 		ids = &i->next;
283 	if (i == nil || id != i->id) {
284 		/* Not found, go look in the password or group map. */
285 		char *name = nil;
286 		char noname[3 * sizeof(uid_t)];
287 
288 		if (!present('n')) {
289 			if (map == PASSWD) {
290 				struct passwd *pw = getpwuid(id);
291 
292 				if (pw != nil) name = pw->pw_name;
293 			} else {
294 				struct group *gr = getgrgid(id);
295 
296 				if (gr != nil) name = gr->gr_name;
297 			}
298 		}
299 		if (name == nil) {
300 			/* Can't find it, weird.  Use numerical "name." */
301 			sprintf(noname, "%lu", (long)id);
302 			name = noname;
303 		}
304 		/* Add a new id-to-name cell. */
305 		i = salloc(sizeof(*i));
306 		i->id = id;
307 		i->name = salloc(strlen(name) + 1);
308 		strcpy(i->name, name);
309 		i->next = *ids;
310 		*ids = i;
311 	}
312 	return i->name;
313 }
314 
315 #define uidname(uid)	idname((uid), PASSWD)
316 #define gidname(gid)	idname((gid), GROUP)
317 
318 /*
319  * Path name construction, addpath adds a component, delpath removes it.
320  * The string path is used throughout the program as the file under examination.
321  */
322 
323 static char *path;	/* Path name constructed in path[]. */
324 static int plen = 0, pidx = 0;	/* Lenght/index for path[]. */
325 
326 /*
327  * Add a component to path. (name may also be a full path at the first call)
328  * The index where the current path ends is stored in *pdi.
329  */
330 static void
addpath(int * didx,char * name)331 addpath(int *didx, char *name)
332 {
333 	if (plen == 0)
334 		path = salloc((plen = 32) * sizeof(path[0]));
335 	if (pidx == 1 && path[0] == '.')
336 		pidx = 0;	/* Remove "." */
337 	*didx = pidx;	/* Record point to go back to for delpath. */
338 	if (pidx > 0 && path[pidx-1] != '/')
339 		path[pidx++] = '/';
340 	do {
341 		if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
342 			if (pidx == plen) {
343 				path = srealloc(path,
344 						(plen *= 2) * sizeof(path[0]));
345 			}
346 			path[pidx++] = *name;
347 		}
348 	} while (*name++ != 0);
349 	--pidx;		/*
350 			 * Put pidx back at the null.  The path[pidx++] = '/'
351 			 * statement will overwrite it at the next call.
352 			 */
353 }
354 
355 #define delpath(didx)	(path[pidx = didx] = 0)	/* Remove component. */
356 
357 static int field = 0;	/* (used to be) Fields that must be printed. */
358 		/* (now) Effects triggered by certain flags. */
359 
360 #define FL_INODE	0x001	/* -i */
361 #define FL_BLOCKS	0x002	/* -s */
362 #define FL_UNUSED	0x004
363 #define FL_MODE		0x008	/* -lMX */
364 #define FL_LONG		0x010	/* -l */
365 #define FL_GROUP	0x020	/* -g */
366 #define FL_BYTIME	0x040	/* -tuc */
367 #define FL_ATIME	0x080	/* -u */
368 #define FL_CTIME	0x100	/* -c */
369 #define FL_MARK		0x200	/* -F */
370 /*#define FL_UNUSED	0x400	*/
371 #define FL_DIR		0x800	/* -d */
372 #define	FL_OWNER	0x1000	/* -o */
373 #define	FL_STATUS	0x2000
374 
375 #define	ENDCOL		0x001	/* last printable column in -x mode */
376 
377 struct file {		/* A file plus stat(2) information. */
378 	off_t		size;
379 #if ST_BLOCKS
380 	blkcnt_t	blocks;
381 #endif
382 	ino_t		ino;
383 	struct file	*next;	/* Lists are made of them. */
384 	struct file	*sord;	/* Saved order of list. */
385 	char		*name;	/* Null terminated name. */
386 	time_t		mtime;
387 	time_t		atime;
388 	time_t		ctime;
389 	mode_t		mode;
390 	int		flag;
391 	uid_t		uid;
392 	gid_t		gid;
393 	dev_t		rdev;
394 	nlink_t		nlink;
395 	unsigned short	namlen;
396 };
397 
398 static void
setstat(struct file * f,struct stat * stp)399 setstat(struct file *f, struct stat *stp)
400 {
401 	f->ino =	stp->st_ino;
402 	f->mode =	stp->st_mode;
403 	f->nlink =	stp->st_nlink;
404 	f->uid =	stp->st_uid;
405 	f->gid =	stp->st_gid;
406 	f->rdev =	stp->st_rdev;
407 	f->size =	stp->st_size;
408 	f->mtime =	stp->st_mtime;
409 	f->atime =	stp->st_atime;
410 	f->ctime =	stp->st_ctime;
411 #if ST_BLOCKS
412 	f->blocks =	stp->st_blocks;
413 #ifdef	__hpux
414 	f->blocks <<= 1;
415 #endif
416 #endif
417 }
418 
419 #define	PAST	(26*7*24*3600L)	/* Half a year ago. */
420 /* Between PAST and FUTURE from now a time is printed, otherwise a year. */
421 #define FUTURE	(15*60L)	/* Fifteen minutes. */
422 
423 /*
424  * Transform the right time field into something readable.
425  */
426 static char *
timestamp(struct file * f)427 timestamp(struct file *f)
428 {
429 	struct tm *tm;
430 	time_t t;
431 	static time_t now;
432 	static int drift = 0;
433 	static char date[32];
434 
435 	t = f->mtime;
436 	if (field & FL_ATIME)
437 		t = f->atime;
438 	if (field & FL_CTIME)
439 		t = f->ctime;
440 	tm = localtime(&t);
441 	if (--drift < 0) {
442 		time(&now);
443 		drift = 50;
444 	}	/* limit time() calls */
445 	if (t < now - PAST || t > now + FUTURE) {
446 		strftime(date, sizeof date - 1, "%b %e  %Y", tm);
447 	} else {
448 		strftime(date, sizeof date - 1, "%b %e %H:%M", tm);
449 	}
450 	return date;
451 }
452 
453 /*
454  * Compute long or short rwx bits.
455  */
456 static char *
permissions(struct file * f)457 permissions(struct file *f)
458 {
459 	/*
460 	 * Note that rwx[0] is a guess for the more alien file types.  It is
461 	 * correct for BSD4.3 and derived systems.  I just don't know how
462 	 * "standardized" these numbers are.
463 	 */
464 	static char rwx[] = "drwxr-x--x ";
465 	char *p = rwx+1;
466 	int mode = f->mode;
467 
468 	rwx[0] = ifmt(f->mode);
469 	if ((f->mode & S_IFMT) == S_IFNAM) {
470 		if (f->rdev == S_INSEM)
471 			rwx[0] = 's';
472 		else if (f->rdev == S_INSHD)
473 			rwx[0] = 'm';
474 		else
475 			rwx[0] = '?';
476 	}
477 	do {
478 		p[0] = (mode & S_IRUSR) ? 'r' : '-';
479 		p[1] = (mode & S_IWUSR) ? 'w' : '-';
480 		p[2] = (mode & S_IXUSR) ? 'x' : '-';
481 		mode <<= 3;
482 	} while ((p += 3) <= rwx + 7);
483 	if (f->mode&S_ISUID)
484 		rwx[3] = f->mode&(S_IXUSR>>0) ? 's' : 'S';
485 	if (f->mode&S_ISGID)
486 		rwx[6] = f->mode&(S_IXGRP>>0) ? 's' :
487 #ifndef	UCB
488 			(f->mode & S_IFMT) != S_IFDIR ?
489 #if defined (SUS) || defined (SU3)
490 			'L'
491 #else	/* !SUS, !SU3 */
492 			'l'
493 #endif	/* !SUS, !SU3 */
494 			:
495 #endif	/* !UCB */
496 			'S'
497 			;
498 	if (f->mode&S_ISVTX)
499 		rwx[9] = f->mode&(S_IXUSR>>6) ? 't' : 'T';
500 	/*
501 	 * rwx[10] would be the "optional alternate access method flag",
502 	 * leave as a space for now.
503 	 */
504 	return rwx;
505 }
506 
507 #ifdef	notdef
508 void
numeral(int i,char ** pp)509 numeral(int i, char **pp)
510 {
511 	char itoa[3*sizeof(int)], *a = itoa;
512 
513 	do
514 		*a++ = i % 10 + '0';
515 	while ((i /= 10) > 0);
516 	do
517 		*(*pp)++ = *--a;
518 	while (a > itoa);
519 }
520 #endif
521 
522 static char *
extension(const char * fn)523 extension(const char *fn)
524 {
525 	const char	*ep = "";
526 
527 	while (*fn++)
528 		if (*fn == '.')
529 			ep = &fn[1];
530 	return (char *)ep;
531 }
532 
533 static int (*CMP)(struct file *f1, struct file *f2);
534 static int (*rCMP)(struct file *f1, struct file *f2);
535 
536 /*
537  * This is either a stable mergesort, or thermal noise, I'm no longer sure.
538  * It must be called like this: if (L != nil && L->next != nil) mergesort(&L);
539  */
540 static void
_mergesort(struct file ** al)541 _mergesort(struct file **al)
542 {
543 	/* static */ struct file *l1, **mid;  /* Need not be local */
544 	struct file *l2;
545 
546 	l1 = *(mid = &(*al)->next);
547 	do {
548 		if ((l1 = l1->next) == nil)
549 			break;
550 		mid = &(*mid)->next;
551 	} while ((l1 = l1->next) != nil);
552 	l2 = *mid;
553 	*mid = nil;
554 	if ((*al)->next != nil)
555 		_mergesort(al);
556 	if (l2->next != nil)
557 		_mergesort(&l2);
558 	l1 = *al;
559 	for (;;) {
560 		if ((*CMP)(l1, l2) <= 0) {
561 			if ((l1 = *(al = &l1->next)) == nil) {
562 				*al = l2;
563 				break;
564 			}
565 		} else {
566 			*al = l2;
567 			l2 = *(al = &l2->next);
568 			*al = l1;
569 			if (l2 == nil)
570 				break;
571 		}
572 	}
573 }
574 
575 static int
namecmp(struct file * f1,struct file * f2)576 namecmp(struct file *f1, struct file *f2)
577 {
578 	return strcoll(f1->name, f2->name);
579 }
580 
581 static int
extcmp(struct file * f1,struct file * f2)582 extcmp(struct file *f1, struct file *f2)
583 {
584 	return strcoll(extension(f1->name), extension(f2->name));
585 }
586 
587 static int
mtimecmp(struct file * f1,struct file * f2)588 mtimecmp(struct file *f1, struct file *f2)
589 {
590 	return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;
591 }
592 
593 static int
atimecmp(struct file * f1,struct file * f2)594 atimecmp(struct file *f1, struct file *f2)
595 {
596 	return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;
597 }
598 
599 static int
ctimecmp(struct file * f1,struct file * f2)600 ctimecmp(struct file *f1, struct file *f2)
601 {
602 	return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;
603 }
604 
605 static int
sizecmp(struct file * f1,struct file * f2)606 sizecmp(struct file *f1, struct file *f2)
607 {
608 	return f1->size == f2->size ? 0 : f1->size > f2->size ? -1 : 1;
609 }
610 
611 static int
revcmp(struct file * f1,struct file * f2)612 revcmp(struct file *f1, struct file *f2) {
613 	return (*rCMP)(f2, f1);
614 }
615 
616 /*
617  * Sort the files according to the flags.
618  */
619 static void
sort(struct file ** al)620 sort(struct file **al)
621 {
622 	if (present('U'))
623 		return;
624 	if (!present('f') && *al != nil && (*al)->next != nil) {
625 		CMP = namecmp;
626 		if (!(field & FL_BYTIME)) {
627 			/* Sort on name */
628 
629 			if (present('r')) {
630 				rCMP = CMP;
631 				CMP = revcmp;
632 			}
633 			_mergesort(al);
634 		} else {
635 			/* Sort on name first, then sort on time. */
636 			_mergesort(al);
637 			if (field & FL_CTIME) {
638 				CMP = ctimecmp;
639 			} else if (field & FL_ATIME) {
640 				CMP = atimecmp;
641 			} else {
642 				CMP = mtimecmp;
643 			}
644 			if (present('r')) {
645 				rCMP = CMP;
646 				CMP = revcmp;
647 			}
648 			_mergesort(al);
649 		}
650 		if (present('X')) {
651 			CMP = extcmp;
652 			if (present('r')) {
653 				rCMP = CMP;
654 				CMP = revcmp;
655 			}
656 			_mergesort(al);
657 		}
658 		if (present('S')) {
659 			CMP = sizecmp;
660 			if (present('r')) {
661 				rCMP = CMP;
662 				CMP = revcmp;
663 			}
664 			_mergesort(al);
665 		}
666 	}
667 }
668 
669 /*
670  * Create file structure for given name.
671  */
672 static struct file *
newfile(char * name)673 newfile(char *name)
674 {
675 	struct file *new;
676 
677 	new = salloc(sizeof(*new));
678 	new->name = strcpy(salloc(strlen(name)+1), name);
679 	new->namlen = 0;
680 	new->flag = 0;
681 	return new;
682 }
683 
684 /*
685  * Add file to the head of a list.
686  */
687 static void
pushfile(struct file ** flist,struct file * new)688 pushfile(struct file **flist, struct file *new)
689 {
690 	new->next = *flist;
691 	*flist = new;
692 }
693 
694 /*
695  * Release old file structure.
696  */
697 static void
delfile(struct file * old)698 delfile(struct file *old)
699 {
700 	free(old->name);
701 	free(old);
702 }
703 
704 /*
705  * Pop file off top of file list.
706  */
707 static struct file *
popfile(struct file ** flist)708 popfile(struct file **flist)
709 {
710 	struct file *f;
711 
712 	f = *flist;
713 	*flist = f->next;
714 	return f;
715 }
716 
717 /*
718  * Save the current file order.
719  */
720 static void
savord(struct file * f)721 savord(struct file *f)
722 {
723 	while (f) {
724 		f->sord = f->next;
725 		f = f->next;
726 	}
727 }
728 
729 /*
730  * Restore the saved file order.
731  */
732 static void
resord(struct file * f)733 resord(struct file *f)
734 {
735 	while (f) {
736 		f->next = f->sord;
737 		f = f->sord;
738 	}
739 }
740 
741 /*
742  * Return flag that would make ls list this name: -a or -A.
743  */
744 static int
dotflag(char * name)745 dotflag(char *name)
746 {
747 	if (*name++ != '.')
748 		return 0;
749 	switch (*name++) {
750 	case 0:
751 		return 'a';			/* "." */
752 	case '.':
753 		if (*name == 0)
754 			return 'a';	/* ".." */
755 	default:
756 		return 'A';			/* ".*" */
757 	}
758 }
759 
760 /*
761  * Add directory entries of directory name to a file list.
762  */
763 static int
adddir(struct file ** aflist,char * name)764 adddir(struct file **aflist, char *name)
765 {
766 	DIR *d;
767 	struct dirent *e;
768 
769 	if (access(name, 0) < 0) {
770 		report(name);
771 		return 0;
772 	}
773 	if ((d = opendir(name)) == nil) {
774 #ifdef	UCB
775 		fprintf(stderr, "%s unreadable\n", name);
776 #else
777 		fprintf(stderr, "can not access directory %s\n", name);
778 #endif
779 		ex = 1;
780 		return 0;
781 	}
782 	while ((e = readdir(d)) != nil) {
783 		if (e->d_ino != 0 && present(dotflag(e->d_name))) {
784 			pushfile(aflist, newfile(e->d_name));
785 			aflist = &(*aflist)->next;
786 		}
787 	}
788 	closedir(d);
789 	return 1;
790 }
791 
792 /*
793  * Compute total block count for a list of files.
794  */
795 static off_t
countblocks(struct file * flist)796 countblocks(struct file *flist)
797 {
798 	off_t cb = 0;
799 
800 	while (flist != nil) {
801 		switch (flist->mode & S_IFMT) {
802 		case S_IFDIR:
803 		case S_IFREG:
804 #ifdef S_IFLNK
805 		case S_IFLNK:
806 #endif
807 			cb += flist->blocks;
808 		}
809 		flist = flist->next;
810 	}
811 	return cb;
812 }
813 
814 static char *
815 #ifdef	LONGLONG
hfmt(long long n,int fill)816 hfmt(long long n, int fill)
817 #else
818 hfmt(long n, int fill)
819 #endif
820 {
821 	static char	b[10];
822 	const char	units[] = " KMGTPE", *up = units;
823 	int	rest = 0;
824 
825 	while (n > 1023) {
826 		rest = (n % 1024) / 128;
827 		n /= 1024;
828 		up++;
829 	}
830 #ifdef	LONGLONG
831 	if (up == units)
832 		snprintf(b, sizeof b, "%*llu", fill ? 5 : 1, n);
833 	else if (n < 10 && rest)
834 		snprintf(b, sizeof b, "%*llu.%u%c", fill ? 2 : 1, n, rest, *up);
835 	else
836 		snprintf(b, sizeof b, "%*llu%c", fill ? 4 : 1, n, *up);
837 #else	/* !LONGLONG */
838 	if (up == units)
839 		snprintf(b, sizeof b, "%*lu", fill ? 5 : 1, n);
840 	else if (n < 10 && rest)
841 		snprintf(b, sizeof b, "%*lu.%u%c", fill ? 2 : 1, n, rest, *up);
842 	else
843 		snprintf(b, sizeof b, "%*lu%c", fill ? 4 : 1, n, *up);
844 #endif	/* !LONGLONG */
845 	return b;
846 }
847 
848 #define	FC_NORMAL	0
849 #define	FC_BLACK	30
850 #define	FC_RED		31
851 #define	FC_GREEN	32
852 #define	FC_YELLOW	33
853 #define	FC_BLUE		34
854 #define	FC_MAGENTA	35
855 #define	FC_CYAN		36
856 #define	FC_WHITE	37
857 
858 /*
859  * The curses color interface is nearly unusable since 1) it interferes
860  * with bold attributes and 2) there is no clean possibility to reset
861  * all colors to the previous (NOT black/white) values. This uses curses
862  * to check whether running on an ANSI-style color terminal, and if yes,
863  * returns appropriate attributes after.
864  */
865 static char *
fc_get(int color)866 fc_get(int color)
867 {
868 	static int status;
869 	static char sequence[8];
870 
871 	if (status == 0) {
872 #if defined (COLOR_BLACK) && !defined (USE_TERMCAP)
873 		char *cp = tparm(set_a_foreground, COLOR_BLACK);
874 
875 		if (cp && strcmp(cp, "\33[30m") == 0)
876 			status = 1;
877 		else
878 			status = -1;
879 #else	/* !COLOR_BLACK */
880 		char *cp = getenv("TERM");
881                 status = strncmp(cp, "rxvt", 4) == 0 ||
882 				strncmp(cp, "dtterm", 6) == 0 ||
883 	                        strncmp(cp, "xterm", 5) == 0;
884 #endif	/* !COLOR_BLACK */
885 	}
886 	if (status == -1)
887 		return "";
888 	snprintf(sequence, sizeof sequence, "\33[%um", color);
889 	return sequence;
890 }
891 
892 /*
893  * Display a nonprintable character.
894  */
895 static unsigned
nonprint(register int c,int doit)896 nonprint(register int c, int doit)
897 {
898 	register int d;
899 	unsigned n;
900 
901 	if (present('b')) {
902 		n = 4;
903 		if (doit) {
904 			char *nums = "01234567";
905 
906 			putchar('\\');
907 			putchar(nums[(c & ~077) >> 6]);
908 			c &= 077;
909 			d = c & 07;
910 			if (c > d)
911 				putchar(nums[(c - d) >> 3]);
912 			else
913 				putchar(nums[0]);
914 			putchar(nums[d]);
915 		}
916 	} else {
917 		n = 1;
918 		if (doit)
919 			putchar('?');
920 	}
921 	return n;
922 }
923 
924 /*
925  * Print a name with control characters as '?' (unless -q).  The terminal is
926  * assumed to be eight bit clean.
927  */
928 static unsigned
printname(const char * name,struct file * f,int doit)929 printname(const char *name, struct file *f, int doit)
930 {
931 	int c, q = present('q'), bold = 0;
932 	unsigned n = 0;
933 	char *color = NULL;
934 #if defined (USE_TERMCAP)
935 	static char	tspace[1000];
936 	char	*tptr = tspace;
937 	static char	*Bold, *Normal;
938 	static int	washere;
939 
940 	if (washere == 0 && tinfostat == 1) {
941 		washere = 1;
942 		Bold = tgetstr("md", &tptr);
943 		Normal = tgetstr("me", &tptr);
944 	}
945 #endif	/* USE_TERMCAP */
946 
947 	if (f != NULL && doit == 0 && f->namlen != 0)
948 		return f->namlen;
949 	if (doit && f != NULL && tinfostat == 1) {
950 		if ((f->mode & S_IFMT) == S_IFDIR) {
951 			color = fc_get(FC_BLUE);
952 			bold++;
953 		} else if ((f->mode & S_IFMT) == S_IFCHR ||
954 				(f->mode & S_IFMT) == S_IFBLK) {
955 			color = fc_get(FC_YELLOW);
956 			bold++;
957 #ifdef	S_IFLNK
958 		} else if ((f->mode & S_IFMT) == S_IFLNK) {
959 			color = fc_get(FC_CYAN);
960 			bold++;
961 #endif
962 #ifdef	S_IFSOCK
963 		} else if ((f->mode & S_IFMT) == S_IFSOCK) {
964 			color = fc_get(FC_MAGENTA);
965 			bold++;
966 #endif
967 #ifdef	S_IFDOOR
968 		} else if ((f->mode & S_IFMT) == S_IFDOOR) {
969 			color = fc_get(FC_MAGENTA);
970 			bold++;
971 #endif
972 #ifdef	S_IFNAM
973 		} else if ((f->mode & S_IFMT) == S_IFNAM) {
974 			color = fc_get(FC_MAGENTA);
975 #endif
976 #ifdef	S_IFNWK
977 		} else if ((f->mode & S_IFMT) == S_IFNWK) {
978 			color = fc_get(FC_MAGENTA);
979 #endif
980 #ifdef	S_IFIFO
981 		} else if ((f->mode & S_IFMT) == S_IFIFO) {
982 			color = fc_get(FC_YELLOW);
983 #endif
984 		} else if (f->mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
985 			color = fc_get(FC_GREEN);
986 			bold++;
987 		} else if ((f->mode & S_IFMT) == 0) {
988 			color = fc_get(FC_RED);
989 			bold++;
990 		}
991 		if (color) {
992 #ifndef	USE_TERMCAP
993 			if (bold)
994 				vidattr(A_BOLD);
995 #else	/* USE_TERMCAP */
996 			if (Bold)
997 				tputs(Bold, 1, putchar);
998 #endif	/* USE_TERMCAP */
999 			printf(color);
1000 		}
1001 	}
1002 	if (q || istty) {
1003 #ifdef	MB_CUR_MAX
1004 		wchar_t wc;
1005 
1006 		while (
1007 #define	ASCII
1008 #ifdef	ASCII
1009 				(*name & 0200) == 0 ?
1010 					c = 1, (wc = *name) != '\0' :
1011 #endif
1012 				(c = mbtowc(&wc, name, MB_CUR_MAX)) != 0) {
1013 			if (c != -1) {
1014 				if (iswprint(wc)
1015 #ifdef	UCB
1016 						|| wc == '\t' || wc == '\n'
1017 #else	/* !UCB */
1018 						&& wc != '\t'
1019 #endif	/* !UCB */
1020 						) {
1021 					if (doit) {
1022 						while (c-- > 0)
1023 							putchar(*name++ & 0377);
1024 					} else
1025 						name += c;
1026 					n += wcwidth(wc);
1027 				} else {
1028 					while (c-- > 0)
1029 						n += nonprint(*name++ & 0377,
1030 								doit);
1031 				}
1032 			} else
1033 				n += nonprint(*name++ & 0377, doit);
1034 		}
1035 #else	/* !MB_CUR_MAX */
1036 		while ((c = (*name++ & 0377)) != '\0') {
1037 			if (isprint(c)
1038 #ifdef	UCB
1039 					|| c == '\t' || c == '\n'
1040 #else	/* !UCB */
1041 					&& c != '\t'
1042 #endif	/* !UCB */
1043 					) {
1044 				if (doit)
1045 					putchar(c);
1046 				n++;
1047 			} else
1048 				n += nonprint(c, doit);
1049 		}
1050 #endif	/* !MB_CUR_MAX */
1051 	} else {
1052 		while ((c = (*name++ & 0377)) != '\0') {
1053 			if (doit)
1054 				putchar(c);
1055 			n++;
1056 		}
1057 	}
1058 	if (doit && color) {
1059 #if !defined (USE_TERMCAP)
1060 		if (bold)
1061 			vidattr(A_NORMAL);
1062 #else	/* USE_TERMCAP */
1063 		if (Normal)
1064 			tputs(Normal, 1, putchar);
1065 #endif	/* USE_TERMCAP */
1066 		printf(fc_get(FC_NORMAL));
1067 	}
1068 	if (f)
1069 		f->namlen = n;
1070 	return n;
1071 }
1072 
1073 static int
mark(struct file * f,int doit)1074 mark(struct file *f, int doit)
1075 {
1076 	int c;
1077 
1078 	if (!(field & FL_MARK))
1079 		return 0;
1080 	switch (f->mode & S_IFMT) {
1081 	case S_IFDIR:	c = '/'; break;
1082 #ifdef S_IFIFO
1083 	case S_IFIFO:	c = '|'; break;
1084 #endif
1085 #ifdef S_IFLNK
1086 	case S_IFLNK:	c = '@'; break;
1087 #endif
1088 #ifdef S_IFSOCK
1089 	case S_IFSOCK:	c = '='; break;
1090 #endif
1091 #ifdef S_IFDOOR
1092 	case S_IFDOOR:	c = '>'; break;
1093 #endif
1094 	case S_IFREG:
1095 		if (f->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
1096 			c = '*';
1097 			break;
1098 		}
1099 	default:
1100 		c = 0;
1101 	}
1102 	if (doit && c != 0)
1103 		putchar(c);
1104 	return c;
1105 }
1106 
1107 static int colwidth[MAXCOLS];	/* Need colwidth[i] spaces to print column i. */
1108 static int sizwidth[MAXCOLS];	/* Spaces for the size field in a -X print. */
1109 static int namwidth[MAXCOLS];	/* Name field. */
1110 
1111 /*
1112  * Set *aw to the larger of it and w.  Then return it.
1113  */
1114 static int
maxise(int * aw,int w)1115 maxise(int *aw, int w)
1116 {
1117 	if (w > *aw) *aw = w;
1118 	return *aw;
1119 }
1120 
1121 static int nsp = 0;	/* This many spaces have not been printed yet. */
1122 #define spaces(n)	(nsp = (n))
1123 #define terpri()	(nsp = 0, putchar('\n'))		/* No trailing spaces */
1124 
1125 /*
1126  * Either compute the number of spaces needed to print file f (doit == 0) or
1127  * really print it (doit == 1).
1128  */
1129 static int
print1(struct file * f,int col,int doit)1130 print1(struct file *f, int col, int doit)
1131 {
1132 	int width = 0, n;
1133 
1134 	n = printname(f->name, f, 0);
1135 	if (present('m')) {
1136 		if (present('p') && (f->mode & S_IFMT) == S_IFDIR)
1137 			width++;
1138 		if (present('F'))
1139 			width++;
1140 	}
1141 	while (nsp > 0) {
1142 		putchar(' ');
1143 		nsp--;
1144 	}/* Fill gap between two columns */
1145 	if (field & FL_INODE) {
1146 		char	dummy[2];
1147 		if (doit)
1148 #ifdef	LONGLONG
1149 			printf("%*llu ", present('m') ? 1 : 5,
1150 					(long long)f->ino);
1151 #else
1152 			printf("%*lu ", present('m') ? 1 : 5,
1153 					(long)f->ino);
1154 #endif
1155 		else
1156 #ifdef	LONGLONG
1157 			width += snprintf(dummy, sizeof dummy,
1158 					"%*llu ", present('m') ? 1 : 5,
1159 					(long long)f->ino);
1160 #else
1161 			width += snprintf(dummy, sizeof dummy,
1162 					"%*lu ", present('m') ? 1 : 5,
1163 					(long)f->ino);
1164 #endif
1165 	}
1166 	if (field & FL_BLOCKS) {
1167 		char	dummy[2];
1168 		if (doit) {
1169 			if (present('h'))
1170 				printf("%s ", hfmt(f->blocks * 512,
1171 							!present('m')));
1172 			else
1173 #ifdef	LONGLONG
1174 				printf("%*llu ", present('m') ? 1 : 4,
1175 					(long long)blockcount(f->blocks));
1176 #else
1177 				printf("%*lu ", present('m') ? 1 : 4,
1178 					(long)blockcount(f->blocks));
1179 #endif
1180 		} else {
1181 			if (present('h'))
1182 				width += 6;
1183 			else
1184 #ifdef	LONGLONG
1185 				width += snprintf(dummy, sizeof dummy, "%*llu ",
1186 					present('m') ? 1 : 4,
1187 					(long long)f->blocks);
1188 #else
1189 				width += snprintf(dummy, sizeof dummy, "%*lu ",
1190 					present('m') ? 1 : 4,
1191 					(long)f->blocks);
1192 #endif
1193 		}
1194 	}
1195 	if (field & FL_MODE) {
1196 		if (doit) {
1197 			printf("%s ", permissions(f));
1198 		} else {
1199 			width += 11;
1200 		}
1201 	}
1202 	if (field & FL_LONG) {
1203 		if (doit) {
1204 			printf("%2u ", (unsigned) f->nlink);
1205 			if (!(field & FL_GROUP)) {
1206 				printf("%-8s ", uidname(f->uid));
1207 			}
1208 			if (!(field & FL_OWNER)) {
1209 				printf("%-8s ", gidname(f->gid));
1210 			}
1211 			switch (f->mode & S_IFMT) {
1212 			case S_IFBLK:
1213 			case S_IFCHR:
1214 #ifdef S_IFMPB
1215 			case S_IFMPB:
1216 #endif
1217 #ifdef S_IFMPC
1218 			case S_IFMPC:
1219 #endif
1220 				printf("%3lu,%3lu ",
1221 					(long)major(f->rdev),
1222 					(long)minor(f->rdev));
1223 				break;
1224 			default:
1225 				if (present('h'))
1226 					printf("%5s ", hfmt(f->size, 1));
1227 				else
1228 #ifdef	LONGLONG
1229 					printf("%7llu ", (long long)f->size);
1230 #else
1231 					printf("%7lu ", (long)f->size);
1232 #endif
1233 			}
1234 			printf("%s ", timestamp(f));
1235 		} else {
1236 			width += (field & FL_GROUP) ? 34 : 43;
1237 		}
1238 	}
1239 	if (doit) {
1240 		printname(f->name, f, 1);
1241 		if (mark(f, 1) != 0)
1242 			n++;
1243 		if (present('p') && (f->mode & S_IFMT) == S_IFDIR) {
1244 			n++;
1245 			if (doit)
1246 				putchar('/');
1247 		}
1248 #ifdef S_IFLNK
1249 		if ((field & FL_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
1250 			char *buf;
1251 			int sz, r, didx;
1252 
1253 			sz = f->size ? f->size : PATH_MAX;
1254 			buf = salloc(sz + 1);
1255 			addpath(&didx, f->name);
1256 			if ((r = readlink(path, buf, sz)) < 0)
1257 				r = 0;
1258 			delpath(didx);
1259 			buf[r] = 0;
1260 			printf(" -> ");
1261 			printname(buf, NULL, 1);
1262 			free(buf);
1263 			n += 4 + r;
1264 		}
1265 #endif
1266 		if (!present('m'))
1267 			spaces(namwidth[col] - n);
1268 	} else {
1269 		if (mark(f, 0) != 0)
1270 			n++;
1271 #ifdef S_IFLNK
1272 		if ((field & FL_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
1273 			n += 4 + (int) f->size;
1274 		}
1275 #endif
1276 		if (!present('m')) {
1277 			width += maxise(&namwidth[col], n + NSEP);
1278 			maxise(&colwidth[col], width);
1279 		}
1280 	}
1281 	return n + width;
1282 }
1283 
1284 /*
1285  * Return number of files in the list.
1286  */
1287 static int
countfiles(struct file * flist)1288 countfiles(struct file *flist)
1289 {
1290 	int n = 0;
1291 
1292 	while (flist != nil) {
1293 		n++;
1294 		flist = flist->next;
1295 	}
1296 	return n;
1297 }
1298 
1299 static struct file *filecol[MAXCOLS];	/*
1300 					 * filecol[i] is list of files
1301 					 * for column i.
1302 					 */
1303 static int nfiles, nlines;	/* # files to print, # of lines needed. */
1304 
1305 /*
1306  * Chop list of files up in columns.  Note that 3 columns are used for 5 files
1307  * even though nplin may be 4, filecol[3] will simply be nil.
1308  */
1309 static void
columnise(struct file ** flist,struct file * fsav,int nplin)1310 columnise(struct file **flist, struct file *fsav, int nplin)
1311 {
1312 	struct file *f = *flist;
1313 	int i, j;
1314 
1315 	nlines = (nfiles + nplin - 1) / nplin;	/* nlines needed for nfiles */
1316 	filecol[0] = f;
1317 	if (!present('x')) {
1318 		for (i = 1; i < nplin; i++) {
1319 			/* Give nlines files to each column. */
1320 			for (j = 0; j < nlines && f != nil; j++)
1321 				f = f->next;
1322 			filecol[i] = f;
1323 		}
1324 	} else {
1325 		/*
1326 		 * Ok, this is an ugly hack. We use the mechanisms for '-C'
1327 		 * and thus have to change the file list order.
1328 		 */
1329 		struct file *curcol[MAXCOLS];
1330 
1331 		resord(fsav);
1332 		*flist = fsav;
1333 		f = *flist;
1334 		for (i = 0; i < nplin && f; i++) {
1335 			filecol[i] = curcol[i] = f;
1336 			f->flag &= ~ENDCOL;
1337 			f = f->next;
1338 		}
1339 		while (f != NULL) {
1340 			for (i = 0; i < nplin && f; i++) {
1341 				curcol[i]->next = f;
1342 				curcol[i] = f;
1343 				f->flag &= ~ENDCOL;
1344 				f = f->next;
1345 			}
1346 		}
1347 		for (i = 1; i < nplin; i++) {
1348 			curcol[i - 1]->next = filecol[i];
1349 			curcol[i - 1]->flag |= ENDCOL;
1350 		}
1351 		curcol[nplin - 1]->next = NULL;
1352 	}
1353 }
1354 
1355 /*
1356  * Try (doit == 0), or really print the list of files over nplin columns.
1357  * Return true if it can be done in nplin columns or if nplin == 1.
1358  */
1359 static int
print(struct file ** flist,struct file * fsav,int nplin,int doit)1360 print(struct file **flist, struct file *fsav, int nplin, int doit)
1361 {
1362 	register struct file *f;
1363 	register int i, totlen;
1364 
1365 	if (present('m')) {
1366 		if (!doit)
1367 			return 1;
1368 		totlen = 0;
1369 		for (f = *flist; f; f = f->next) {
1370 			i = print1(f, 0, 0) + 2;
1371 			totlen += i;
1372 			if (totlen > ncols+1) {
1373 				putchar('\n');
1374 				totlen = i;
1375 			} else if (f != *flist)
1376 				putchar(' ');
1377 			print1(f, 0, 1);
1378 			if (f->next)
1379 				putchar(',');
1380 		}
1381 		putchar('\n');
1382 		return 1;
1383 	}
1384 	columnise(flist, fsav, nplin);
1385 	if (!doit) {
1386 		if (nplin == 1)
1387 			return 1;	/* No need to try 1 column. */
1388 		for (i = 0; i < nplin; i++) {
1389 			colwidth[i] = sizwidth[i] = namwidth[i] = 0;
1390 		}
1391 	}
1392 	while (--nlines >= 0) {
1393 		totlen = 0;
1394 		for (i = 0; i < nplin; i++) {
1395 			if ((f = filecol[i]) != nil) {
1396 				if (f->flag & ENDCOL)
1397 					filecol[i] = NULL;
1398 				else
1399 					filecol[i] = f->next;
1400 				print1(f, i, doit);
1401 			}
1402 			if (!doit && nplin>1) {
1403 				/* See if this line is not too long. */
1404 				totlen += colwidth[i];
1405 				if (totlen > ncols+NSEP)
1406 					return 0;
1407 			}
1408 		}
1409 		if (doit && !present('m'))
1410 			terpri();
1411 	}
1412 	return 1;
1413 }
1414 
1415 enum depth { SURFACE, SURFACE1, SUBMERGED };
1416 enum state { BOTTOM, SINKING, FLOATING };
1417 
1418 /*
1419  * Main workhorse of ls, it sorts and prints the list of files.  Flags:
1420  * depth: working with the command line / just one file / listing dir.
1421  * state: How "recursive" do we have to be.
1422  */
1423 static void
listfiles(struct file * flist,enum depth depth,enum state state,int level)1424 listfiles(struct file *flist, enum depth depth, enum state state, int level)
1425 {
1426 	struct file *dlist = nil, **afl = &flist, **adl = &dlist, *fsav;
1427 	int nplin, t;
1428 	static int white = 1;	/* Nothing printed yet. */
1429 
1430 	/*
1431 	 * Flush everything previously printed, so new error output will
1432 	 * not intermix with files listed earlier.
1433 	 */
1434 	fflush(stdout);
1435 	if (field != 0 || state != BOTTOM) {	/* Need stat(2) info. */
1436 		while (*afl != nil) {
1437 			static struct stat st;
1438 			int didx;
1439 #ifdef S_IFLNK
1440 			int (*status)(const char *file, struct stat *stp) =
1441 				depth == SURFACE1 && (field & FL_LONG) == 0
1442 #ifdef	SU3
1443 						&& !present('F')
1444 #endif	/* SU3 */
1445 					|| present('L')
1446 					|| present('H') && depth != SUBMERGED ?
1447 						stat : lstat;
1448 #else
1449 #define status	stat
1450 #endif
1451 
1452 /* Basic disk block size is 512 except for one niche O.S. */
1453 
1454 			addpath(&didx, (*afl)->name);
1455 			if ((t = status(path, &st)) < 0
1456 #ifdef S_IFLNK
1457 				&& (status == lstat || lstat(path, &st) < 0)
1458 #endif
1459 			) {
1460 				if (depth != SUBMERGED || errno != ENOENT)
1461 					report((*afl)->name);
1462 #ifdef	SU3
1463 			fail:
1464 #endif	/* SU3 */
1465 				delfile(popfile(afl));
1466 			} else {
1467 #ifdef	SU3
1468 				if (t < 0 && errno == ELOOP && (present('H') ||
1469 							present('L'))) {
1470 					report((*afl)->name);
1471 					goto fail;
1472 				}
1473 				if (present('L')) {
1474 					int	i;
1475 					for (i = 0; i < level; i++) {
1476 						if (st.st_dev==visited[i].v_dev
1477 						    && st.st_ino==
1478 						      visited[i].v_ino) {
1479 							fprintf(stderr, "link "
1480 								"loop at %s\n",
1481 								path);
1482 							ex = 1;
1483 							goto fail;
1484 						}
1485 					}
1486 					if (level >= vismax) {
1487 						vismax += 20;
1488 						visited = srealloc(visited,
1489 								sizeof *visited
1490 								* vismax);
1491 					}
1492 					visited[level].v_dev = st.st_dev;
1493 					visited[level].v_ino = st.st_ino;
1494 				}
1495 #endif	/* SU3 */
1496 				if (((field & FL_MARK) || tinfostat == 1 ||
1497 						present('H')) &&
1498 						!present('L') &&
1499 						status != lstat &&
1500 						(st.st_mode & S_IFMT)
1501 							!= S_IFDIR) {
1502 					struct stat	lst;
1503 
1504 					if (lstat(path, &lst) == 0)
1505 						st = lst;
1506 				}
1507 				setstat(*afl, &st);
1508 				afl = &(*afl)->next;
1509 			}
1510 			delpath(didx);
1511 		}
1512 	}
1513 	sort(&flist);
1514 	if (depth == SUBMERGED && (field & (FL_BLOCKS | FL_LONG))) {
1515 		printf("total ");
1516 		if (present('h'))
1517 			printf("%s\n", hfmt(countblocks(flist) * 512, 0));
1518 		else
1519 #ifdef	LONGLONG
1520 			printf("%lld\n", (long long)blockcount(countblocks(flist)));
1521 #else
1522 			printf("%ld\n", (long)blockcount(countblocks(flist)));
1523 #endif
1524 	}
1525 	if (state == SINKING || depth == SURFACE1) {
1526 	/* Don't list directories themselves, list their contents later. */
1527 		afl = &flist;
1528 		while (*afl != nil) {
1529 			if (((*afl)->mode & S_IFMT) == S_IFDIR) {
1530 				pushfile(adl, popfile(afl));
1531 				adl = &(*adl)->next;
1532 			} else {
1533 				afl = &(*afl)->next;
1534 			}
1535 		}
1536 	}
1537 	if ((nfiles = countfiles(flist)) > 0) {
1538 		/* Print files in how many columns? */
1539 		nplin = !present('C') && !present('x') ?
1540 			1 : nfiles < MAXCOLS ? nfiles : MAXCOLS;
1541 		fsav = flist;
1542 		if (present('x'))
1543 			savord(flist);
1544 		while (!print(&flist, fsav, nplin, 0))
1545 			nplin--;	/* Try first */
1546 		print(&flist, fsav, nplin, 1);		/* Then do it! */
1547 		white = 0;
1548 	}
1549 	while (flist != nil) {	/* Destroy file list */
1550 		if (state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR) {
1551 			/* But keep these directories for ls -R. */
1552 			pushfile(adl, popfile(&flist));
1553 			adl = &(*adl)->next;
1554 		} else {
1555 			delfile(popfile(&flist));
1556 		}
1557 	}
1558 	while (dlist != nil) {	/* List directories */
1559 		if (dotflag(dlist->name) != 'a' || depth != SUBMERGED) {
1560 			int didx;
1561 
1562 			addpath(&didx, dlist->name);
1563 
1564 			flist = nil;
1565 			if (adddir(&flist, path)) {
1566 				if (depth != SURFACE1) {
1567 					if (!white)
1568 						putchar('\n');
1569 					white = 0;
1570 				}
1571 				if (depth != SURFACE1 || present('R'))
1572 					printf("%s:\n", path);
1573 				listfiles(flist, SUBMERGED,
1574 					state == FLOATING ? FLOATING : BOTTOM,
1575 					level + 1);
1576 			}
1577 			delpath(didx);
1578 		}
1579 		delfile(popfile(&dlist));
1580 	}
1581 }
1582 
1583 #ifndef	UCB
1584 static void
usage(void)1585 usage(void)
1586 {
1587 	if (personality == PER_LS)
1588 		fprintf(stderr, "usage: %s -1RadCxmnlogrtucpFbqisfL [files]\n",
1589 				arg0);
1590 	exit(2);
1591 }
1592 #else	/* UCB */
1593 #define	usage()
1594 #endif	/* UCB */
1595 
1596 int
main(int argc,char ** argv)1597 main(int argc, char **argv)
1598 {
1599 	struct file *flist = nil, **aflist = &flist;
1600 	enum depth depth;
1601 	struct winsize ws;
1602 	int i;
1603 	char *cp;
1604 
1605 #ifdef	__GLIBC__
1606 	putenv("POSIXLY_CORRECT=1");
1607 #endif
1608 	setlocale(LC_COLLATE, "");
1609 	setlocale(LC_CTYPE, "");
1610 	setlocale(LC_TIME, "");
1611 #ifndef	UCB
1612 	if (getenv("SYSV3") != NULL)
1613 		sysv3 = 1;
1614 #endif	/* !UCB */
1615 	uid = geteuid();
1616 	gid = getegid();
1617 	arg0 = basename(argv[0]);
1618 	if (strcmp(arg0, "dir") == 0) {
1619 		flags['s'] = flags['U'] = 1;
1620 		personality = PER_DIR;
1621 	} else if (strcmp(arg0, "lc") == 0) {
1622 		flags['C'] = 1;
1623 		istty = 1;
1624 	}
1625 	if (istty || isatty(1)) {
1626 		istty = 1;
1627 #if !defined (USE_TERMCAP)
1628 		setupterm(NULL, 1, &tinfostat);
1629 #else	/* USE_TERMCAP */
1630 		{
1631 			char	buf[2048];
1632 			if ((cp = getenv("TERM")) != NULL)
1633 				if (tgetent(buf, cp) > 0)
1634 					tinfostat = 1;
1635 		}
1636 #endif	/* USE_TERMCAP */
1637 		field |= FL_STATUS;
1638 	}
1639 	while ((i = getopt(argc, argv, personalities[personality].per_opt))
1640 			!= EOF) {
1641 		switch (i) {
1642 		case '?':
1643 			usage();
1644 			break;
1645 
1646 		case 'O':
1647 			flags['U'] = 0;
1648 			break;
1649 		case '1':
1650 			flags[i] = 1;
1651 			flags['C'] = 0;
1652 			break;
1653 		case 'L':
1654 			flags[i] = 1;
1655 			flags['H'] = 0;
1656 			break;
1657 		case 'H':
1658 			flags[i] = 1;
1659 			flags['L'] = 0;
1660 			break;
1661 #if defined (SUS) || defined (SU3)
1662 		case 'C':
1663 			flags[i] = 1;
1664 			flags['l'] = flags['m'] = flags['x'] = flags ['o'] =
1665 				flags['g'] = flags['n'] = flags['1'] = 0;
1666 			break;
1667 		case 'm':
1668 			flags[i] = 1;
1669 			flags['l'] = flags['C'] = flags['x'] = flags ['o'] =
1670 				flags['g'] = flags['n'] = flags['1'] = 0;
1671 			break;
1672 		case 'l':
1673 			flags[i] = 1;
1674 			flags['m'] = flags['C'] = flags['x'] = flags ['o'] =
1675 				flags['g'] = 0;
1676 			break;
1677 		case 'x':
1678 			flags[i] = 1;
1679 			flags['m'] = flags['C'] = flags['l'] = flags ['o'] =
1680 				flags['g'] = flags['n'] = flags['1'] = 0;
1681 			break;
1682 		case 'g':
1683 			flags[i] = 1;
1684 			flags['m'] = flags['C'] = flags['l'] = flags ['x'] =
1685 				flags['o'] = 0;
1686 			break;
1687 		case 'o':
1688 			flags[i] = 1;
1689 			flags['m'] = flags['C'] = flags['l'] = flags ['x'] =
1690 				flags['g'] = 0;
1691 			break;
1692 		case 'n':
1693 			flags[i] = 1;
1694 			flags['m'] = flags['C'] = flags ['x'] = 0;
1695 			break;
1696 #endif	/* !SUS, !SU3 */
1697 		default:
1698 			flags[i] = 1;
1699 		}
1700 	}
1701 #ifdef	UCB
1702 	if (present('l')) {
1703 		if (present('g'))
1704 			flags['g'] = 0;
1705 		else
1706 			flags['o'] = 1;
1707 	} else if (present('g'))
1708 		flags['g'] = 0;
1709 #endif
1710 #ifdef	UCB
1711 	if (SUPER_ID == 0 || present('a'))
1712 		flags['A'] = 1;
1713 #else	/* !UCB */
1714 	if (present('a'))
1715 		flags['A'] = 1;
1716 #endif	/* !UCB */
1717 	if (present('i'))
1718 		field |= FL_INODE;
1719 	if (present('s'))
1720 		field |= FL_BLOCKS;
1721 	if (present('t'))
1722 		field |= FL_BYTIME;
1723 	if (present('u'))
1724 		field |= FL_ATIME;
1725 	if (present('c'))
1726 		field |= FL_CTIME;
1727 	if (present('l') || present('n') || present('g') || present('o')) {
1728 		field = field | FL_MODE | FL_LONG;
1729 		flags['m'] = flags['C'] = flags['x'] = 0;
1730 	}
1731 	if (present('g'))
1732 		field = field | FL_MODE | FL_LONG | FL_GROUP;
1733 	if (present('o'))
1734 		field = field | FL_MODE | FL_LONG | FL_OWNER;
1735 	if (present('F'))
1736 		field |= FL_MARK;
1737 	if (present('d'))
1738 		field |= FL_DIR;
1739 	if (present('f')) {
1740 		field &= ~(FL_LONG|FL_BYTIME|FL_BLOCKS|FL_MODE|FL_MARK|FL_DIR);
1741 		flags['o'] = flags['g'] = flags['l'] = flags['t'] = flags['s'] =
1742 			flags['r'] = flags['d'] = flags['F'] = flags['R'] =
1743 			flags['p'] = 0;
1744 		flags['a'] = flags['A'] = 1;
1745 		field |= FL_STATUS;
1746 	}
1747 	if (!present('1') && !present('C') && !present('l') &&
1748 			!present('o') && !present('g') && !present('m') &&
1749 			(istty && !sysv3 || present('x')))
1750 		flags['C'] = 1;
1751 	if (istty)
1752 		flags['q'] = 1;
1753 	if ((cp = getenv("COLUMNS")) != NULL) {
1754 		ncols = atoi(cp);
1755 	} else if ((present('C') || present('x') || present('m')) && istty) {
1756 		if (ioctl(1, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
1757 			ncols = ws.ws_col - 1;
1758 #if !defined (USE_TERMCAP)
1759 		else if (tinfostat == 1 && columns > 0)
1760 			ncols = columns;
1761 #endif	/* !USE_TERMCAP */
1762 	}
1763 	depth = SURFACE;
1764 	if (optind == argc) {
1765 		if (!(field & FL_DIR))
1766 			depth = SURFACE1;
1767 		pushfile(aflist, newfile("."));
1768 	} else {
1769 		if (optind+1 == argc && !(field & FL_DIR))
1770 			depth = SURFACE1;
1771 		while (optind < argc) {
1772 			if (present('f')) {
1773 				struct stat st;
1774 
1775 				if (stat(argv[optind], &st) == 0 &&
1776 						(st.st_mode & S_IFMT)
1777 							!= S_IFDIR) {
1778 #ifdef	UCB
1779 					fprintf(stderr, "%s unreadable\n",
1780 							argv[optind]);
1781 #else
1782 					fprintf(stderr, "%s: Not a directory\n",
1783 						argv[optind]);
1784 #endif
1785 					ex = 1;
1786 					optind++;
1787 					continue;
1788 				}
1789 			}
1790 			pushfile(aflist, newfile(argv[optind++]));
1791 			aflist = &(*aflist)->next;
1792 		}
1793 	}
1794 	listfiles(flist, depth,
1795 		(field & FL_DIR) ? BOTTOM : present('R') ? FLOATING : SINKING,
1796 		0);
1797 	return ex;
1798 }
1799