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