source: trunk/minix/commands/simple/ls.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 25.4 KB
Line 
1/* ls 5.4 - List files. Author: Kees J. Bot
2 * 25 Apr 1989
3 *
4 * About the amount of bytes for heap + stack under Minix:
5 * Ls needs a average amount of 42 bytes per unserviced directory entry, so
6 * scanning 10 directory levels deep in an ls -R with 100 entries per directory
7 * takes 42000 bytes of heap. So giving ls 10000 bytes is tight, 20000 is
8 * usually enough, 40000 is pessimistic.
9 */
10
11/* The array l_ifmt[] is used in an 'ls -l' to map the type of a file to a
12 * letter. This is done so that ls can list any future file or device type
13 * other than symlinks, without recompilation. (Yes it's dirty.)
14 */
15char l_ifmt[] = "0pcCd?bB-?l?s???";
16
17#define ifmt(mode) l_ifmt[((mode) >> 12) & 0xF]
18
19#define nil 0
20#include <stdio.h>
21#include <string.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <stddef.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <dirent.h>
28#include <time.h>
29#include <pwd.h>
30#include <grp.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <termios.h>
35#include <sys/ioctl.h>
36
37#ifndef major
38#define major(dev) ((int) (((dev) >> 8) & 0xFF))
39#define minor(dev) ((int) (((dev) >> 0) & 0xFF))
40#endif
41
42#if !__minix
43#define SUPER_ID uid /* Let -A flag be default for SUPER_ID == 0. */
44#else
45#define SUPER_ID gid
46#endif
47
48#ifdef S_IFLNK
49int (*status)(const char *file, struct stat *stp);
50#else
51#define status stat
52#endif
53
54/* Basic disk block size is 512 except for one niche O.S. */
55#if __minix
56#define BLOCK 1024
57#else
58#define BLOCK 512
59#endif
60
61/* Assume other systems have st_blocks. */
62#if !__minix
63#define ST_BLOCKS 1
64#endif
65
66/* Some terminals ignore more than 80 characters on a line. Dumb ones wrap
67 * when the cursor hits the side. Nice terminals don't wrap until they have
68 * to print the 81st character. Whether we like it or not, no column 80.
69 */
70int ncols= 79;
71
72#define NSEP 3 /* # spaces between columns. */
73
74#define MAXCOLS 128 /* Max # of files per line. */
75
76char *arg0; /* Last component of argv[0]. */
77int uid, gid; /* callers id. */
78int ex= 0; /* Exit status to be. */
79int istty; /* Output is on a terminal. */
80
81/* Safer versions of malloc and realloc: */
82
83void heaperr(void)
84{
85 fprintf(stderr, "%s: Out of memory\n", arg0);
86 exit(-1);
87}
88
89void *allocate(size_t n)
90/* Deliver or die. */
91{
92 void *a;
93
94 if ((a= malloc(n)) == nil) heaperr();
95 return a;
96}
97
98void *reallocate(void *a, size_t n)
99{
100 if ((a= realloc(a, n)) == nil) heaperr();
101 return a;
102}
103
104char allowed[] = "acdfghilnpqrstu1ACDFLMRTX";
105char flags[sizeof(allowed)];
106
107char arg0flag[] = "cdfmrtx"; /* These in argv[0] go to upper case. */
108
109void setflags(char *flgs)
110{
111 int c;
112
113 while ((c= *flgs++) != 0) {
114 if (strchr(allowed, c) == nil) {
115 fprintf(stderr, "Usage: %s [-%s] [file ...]\n",
116 arg0, allowed);
117 exit(1);
118 } else
119 if (strchr(flags, c) == nil) {
120 flags[strlen(flags)] = c;
121 }
122 }
123}
124
125int present(int f)
126{
127 return f == 0 || strchr(flags, f) != nil;
128}
129
130void report(char *f)
131/* Like perror(3), but in the style: "ls: junk: No such file or directory. */
132{
133 fprintf(stderr, "%s: %s: %s\n", arg0, f, strerror(errno));
134 ex= 1;
135}
136
137/* Two functions, uidname and gidname, translate id's to readable names.
138 * All names are remembered to avoid searching the password file.
139 */
140#define NNAMES (1 << (sizeof(int) + sizeof(char *)))
141enum whatmap { PASSWD, GROUP };
142
143struct idname { /* Hash list of names. */
144 struct idname *next;
145 char *name;
146 uid_t id;
147} *uids[NNAMES], *gids[NNAMES];
148
149char *idname(unsigned id, enum whatmap map)
150/* Return name for a given user/group id. */
151{
152 struct idname *i;
153 struct idname **ids= &(map == PASSWD ? uids : gids)[id % NNAMES];
154
155 while ((i= *ids) != nil && id < i->id) ids= &i->next;
156
157 if (i == nil || id != i->id) {
158 /* Not found, go look in the password or group map. */
159 char *name= nil;
160 char noname[3 * sizeof(uid_t)];
161
162 if (!present('n')) {
163 if (map == PASSWD) {
164 struct passwd *pw= getpwuid(id);
165
166 if (pw != nil) name= pw->pw_name;
167 } else {
168 struct group *gr= getgrgid(id);
169
170 if (gr != nil) name= gr->gr_name;
171 }
172 }
173 if (name == nil) {
174 /* Can't find it, weird. Use numerical "name." */
175 sprintf(noname, "%u", id);
176 name= noname;
177 }
178
179 /* Add a new id-to-name cell. */
180 i= allocate(sizeof(*i));
181 i->id= id;
182 i->name= allocate(strlen(name) + 1);
183 strcpy(i->name, name);
184 i->next= *ids;
185 *ids= i;
186 }
187 return i->name;
188}
189
190#define uidname(uid) idname((uid), PASSWD)
191#define gidname(gid) idname((gid), GROUP)
192
193/* Path name construction, addpath adds a component, delpath removes it.
194 * The string path is used throughout the program as the file under examination.
195 */
196
197char *path; /* Path name constructed in path[]. */
198int plen= 0, pidx= 0; /* Lenght/index for path[]. */
199
200void addpath(int *didx, char *name)
201/* Add a component to path. (name may also be a full path at the first call)
202 * The index where the current path ends is stored in *pdi.
203 */
204{
205 if (plen == 0) path= (char *) allocate((plen= 32) * sizeof(path[0]));
206
207 if (pidx == 1 && path[0] == '.') pidx= 0; /* Remove "." */
208
209 *didx= pidx; /* Record point to go back to for delpath. */
210
211 if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
212
213 do {
214 if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
215 if (pidx == plen) {
216 path= (char *) reallocate((void *) path,
217 (plen*= 2) * sizeof(path[0]));
218 }
219 path[pidx++]= *name;
220 }
221 } while (*name++ != 0);
222
223 --pidx; /* Put pidx back at the null. The path[pidx++]= '/'
224 * statement will overwrite it at the next call.
225 */
226}
227
228#define delpath(didx) (path[pidx= didx]= 0) /* Remove component. */
229
230int field = 0; /* (used to be) Fields that must be printed. */
231 /* (now) Effects triggered by certain flags. */
232
233#define L_INODE 0x0001 /* -i */
234#define L_BLOCKS 0x0002 /* -s */
235#define L_EXTRA 0x0004 /* -X */
236#define L_MODE 0x0008 /* -lMX */
237#define L_LONG 0x0010 /* -l */
238#define L_GROUP 0x0020 /* -g */
239#define L_BYTIME 0x0040 /* -tuc */
240#define L_ATIME 0x0080 /* -u */
241#define L_CTIME 0x0100 /* -c */
242#define L_MARK 0x0200 /* -F */
243#define L_MARKDIR 0x0400 /* -p */
244#define L_TYPE 0x0800 /* -D */
245#define L_LONGTIME 0x1000 /* -T */
246#define L_DIR 0x2000 /* -d */
247#define L_KMG 0x4000 /* -h */
248
249struct file { /* A file plus stat(2) information. */
250 struct file *next; /* Lists are made of them. */
251 char *name; /* Null terminated name. */
252 ino_t ino;
253 mode_t mode;
254 uid_t uid;
255 gid_t gid;
256 nlink_t nlink;
257 dev_t rdev;
258 off_t size;
259 time_t mtime;
260 time_t atime;
261 time_t ctime;
262#if ST_BLOCKS
263 long blocks;
264#endif
265};
266
267void setstat(struct file *f, struct stat *stp)
268{
269 f->ino= stp->st_ino;
270 f->mode= stp->st_mode;
271 f->nlink= stp->st_nlink;
272 f->uid= stp->st_uid;
273 f->gid= stp->st_gid;
274 f->rdev= stp->st_rdev;
275 f->size= stp->st_size;
276 f->mtime= stp->st_mtime;
277 f->atime= stp->st_atime;
278 f->ctime= stp->st_ctime;
279#if ST_BLOCKS
280 f->blocks= stp->st_blocks;
281#endif
282}
283
284#define PAST (26*7*24*3600L) /* Half a year ago. */
285/* Between PAST and FUTURE from now a time is printed, otherwise a year. */
286#define FUTURE ( 1*7*24*3600L) /* One week. */
287
288static char *timestamp(struct file *f)
289/* Transform the right time field into something readable. */
290{
291 struct tm *tm;
292 time_t t;
293 static time_t now;
294 static int drift= 0;
295 static char date[] = "Jan 19 03:14:07 2038";
296 static char month[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
297
298 t= f->mtime;
299 if (field & L_ATIME) t= f->atime;
300 if (field & L_CTIME) t= f->ctime;
301
302 tm= localtime(&t);
303 if (--drift < 0) { time(&now); drift= 50; } /* limit time() calls */
304
305 if (field & L_LONGTIME) {
306 sprintf(date, "%.3s %2d %02d:%02d:%02d %d",
307 month + 3*tm->tm_mon,
308 tm->tm_mday,
309 tm->tm_hour, tm->tm_min, tm->tm_sec,
310 1900 + tm->tm_year);
311 } else
312 if (t < now - PAST || t > now + FUTURE) {
313 sprintf(date, "%.3s %2d %d",
314 month + 3*tm->tm_mon,
315 tm->tm_mday,
316 1900 + tm->tm_year);
317 } else {
318 sprintf(date, "%.3s %2d %02d:%02d",
319 month + 3*tm->tm_mon,
320 tm->tm_mday,
321 tm->tm_hour, tm->tm_min);
322 }
323 return date;
324}
325
326char *permissions(struct file *f)
327/* Compute long or short rwx bits. */
328{
329 static char rwx[] = "drwxr-x--x";
330
331 rwx[0] = ifmt(f->mode);
332 /* Note that rwx[0] is a guess for the more alien file types. It is
333 * correct for BSD4.3 and derived systems. I just don't know how
334 * "standardized" these numbers are.
335 */
336
337 if (field & L_EXTRA) { /* Short style */
338 int mode = f->mode, ucase= 0;
339
340 if (uid == f->uid) { /* What group of bits to use. */
341 /* mode<<= 0, */
342 ucase= (mode<<3) | (mode<<6);
343 /* Remember if group or others have permissions. */
344 } else
345 if (gid == f->gid) {
346 mode<<= 3;
347 } else {
348 mode<<= 6;
349 }
350 rwx[1]= mode&S_IRUSR ? (ucase&S_IRUSR ? 'R' : 'r') : '-';
351 rwx[2]= mode&S_IWUSR ? (ucase&S_IWUSR ? 'W' : 'w') : '-';
352
353 if (mode&S_IXUSR) {
354 static char sbit[]= { 'x', 'g', 'u', 's' };
355
356 rwx[3]= sbit[(f->mode&(S_ISUID|S_ISGID))>>10];
357 if (ucase&S_IXUSR) rwx[3] += 'A'-'a';
358 } else {
359 rwx[3]= f->mode&(S_ISUID|S_ISGID) ? '=' : '-';
360 }
361 rwx[4]= 0;
362 } else { /* Long form. */
363 char *p= rwx+1;
364 int mode= f->mode;
365
366 do {
367 p[0] = (mode & S_IRUSR) ? 'r' : '-';
368 p[1] = (mode & S_IWUSR) ? 'w' : '-';
369 p[2] = (mode & S_IXUSR) ? 'x' : '-';
370 mode<<= 3;
371 } while ((p+=3) <= rwx+7);
372
373 if (f->mode&S_ISUID) rwx[3]= f->mode&(S_IXUSR>>0) ? 's' : '=';
374 if (f->mode&S_ISGID) rwx[6]= f->mode&(S_IXUSR>>3) ? 's' : '=';
375 if (f->mode&S_ISVTX) rwx[9]= f->mode&(S_IXUSR>>6) ? 't' : '=';
376 }
377 return rwx;
378}
379
380void numeral(int i, char **pp)
381{
382 char itoa[3*sizeof(int)], *a=itoa;
383
384 do *a++ = i%10 + '0'; while ((i/=10) > 0);
385
386 do *(*pp)++ = *--a; while (a>itoa);
387}
388
389#define K 1024L /* A kilobyte counts in multiples of K */
390#define T 1000L /* A megabyte in T*K, a gigabyte in T*T*K */
391
392char *cxsize(struct file *f)
393/* Try and fail to turn a 32 bit size into 4 readable characters. */
394{
395 static char siz[] = "1.2m";
396 char *p= siz;
397 off_t z;
398
399 siz[1]= siz[2]= siz[3]= 0;
400
401 if (f->size <= 5*K) { /* <= 5K prints as is. */
402 numeral((int) f->size, &p);
403 return siz;
404 }
405 z= (f->size + K-1) / K;
406
407 if (z <= 999) { /* Print as 123k. */
408 numeral((int) z, &p);
409 *p = 'k'; /* Can't use 'K', looks bad */
410 } else
411 if (z*10 <= 99*T) { /* 1.2m (Try ls -X /dev/at0) */
412 z= (z*10 + T-1) / T; /* Force roundup */
413 numeral((int) z / 10, &p);
414 *p++ = '.';
415 numeral((int) z % 10, &p);
416 *p = 'm';
417 } else
418 if (z <= 999*T) { /* 123m */
419 numeral((int) ((z + T-1) / T), &p);
420 *p = 'm';
421 } else { /* 1.2g */
422 z= (z*10 + T*T-1) / (T*T);
423 numeral((int) z / 10, &p);
424 *p++ = '.';
425 numeral((int) z % 10, &p);
426 *p = 'g';
427 }
428 return siz;
429}
430
431/* Transform size of file to number of blocks. This was once a function that
432 * guessed the number of indirect blocks, but that nonsense has been removed.
433 */
434#if ST_BLOCKS
435#define nblocks(f) ((f)->blocks)
436#else
437#define nblocks(f) (((f)->size + BLOCK-1) / BLOCK)
438#endif
439
440/* From number of blocks to kilobytes. */
441#if BLOCK < 1024
442#define nblk2k(nb) (((nb) + (1024 / BLOCK - 1)) / (1024 / BLOCK))
443#else
444#define nblk2k(nb) ((nb) * (BLOCK / 1024))
445#endif
446
447static int (*CMP)(struct file *f1, struct file *f2);
448static int (*rCMP)(struct file *f1, struct file *f2);
449
450static void mergesort(struct file **al)
451/* This is either a stable mergesort, or thermal noise, I'm no longer sure.
452 * It must be called like this: if (L != nil && L->next != nil) mergesort(&L);
453 */
454{
455 /* static */ struct file *l1, **mid; /* Need not be local */
456 struct file *l2;
457
458 l1= *(mid= &(*al)->next);
459 do {
460 if ((l1= l1->next) == nil) break;
461 mid= &(*mid)->next;
462 } while ((l1= l1->next) != nil);
463
464 l2= *mid;
465 *mid= nil;
466
467 if ((*al)->next != nil) mergesort(al);
468 if (l2->next != nil) mergesort(&l2);
469
470 l1= *al;
471 for (;;) {
472 if ((*CMP)(l1, l2) <= 0) {
473 if ((l1= *(al= &l1->next)) == nil) {
474 *al= l2;
475 break;
476 }
477 } else {
478 *al= l2;
479 l2= *(al= &l2->next);
480 *al= l1;
481 if (l2 == nil) break;
482 }
483 }
484}
485
486int namecmp(struct file *f1, struct file *f2)
487{
488 return strcmp(f1->name, f2->name);
489}
490
491int mtimecmp(struct file *f1, struct file *f2)
492{
493 return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;
494}
495
496int atimecmp(struct file *f1, struct file *f2)
497{
498 return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;
499}
500
501int ctimecmp(struct file *f1, struct file *f2)
502{
503 return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;
504}
505
506int typecmp(struct file *f1, struct file *f2)
507{
508 return ifmt(f1->mode) - ifmt(f2->mode);
509}
510
511int revcmp(struct file *f1, struct file *f2) { return (*rCMP)(f2, f1); }
512
513static void sort(struct file **al)
514/* Sort the files according to the flags. */
515{
516 if (!present('f') && *al != nil && (*al)->next != nil) {
517 CMP= namecmp;
518
519 if (!(field & L_BYTIME)) {
520 /* Sort on name */
521
522 if (present('r')) { rCMP= CMP; CMP= revcmp; }
523 mergesort(al);
524 } else {
525 /* Sort on name first, then sort on time. */
526
527 mergesort(al);
528 if (field & L_CTIME) {
529 CMP= ctimecmp;
530 } else
531 if (field & L_ATIME) {
532 CMP= atimecmp;
533 } else {
534 CMP= mtimecmp;
535 }
536
537 if (present('r')) { rCMP= CMP; CMP= revcmp; }
538 mergesort(al);
539 }
540 /* Separate by file type if so desired. */
541
542 if (field & L_TYPE) {
543 CMP= typecmp;
544 mergesort(al);
545 }
546 }
547}
548
549struct file *newfile(char *name)
550/* Create file structure for given name. */
551{
552 struct file *new;
553
554 new= (struct file *) allocate(sizeof(*new));
555 new->name= strcpy((char *) allocate(strlen(name)+1), name);
556 return new;
557}
558
559void pushfile(struct file **flist, struct file *new)
560/* Add file to the head of a list. */
561{
562 new->next= *flist;
563 *flist= new;
564}
565
566void delfile(struct file *old)
567/* Release old file structure. */
568{
569 free((void *) old->name);
570 free((void *) old);
571}
572
573struct file *popfile(struct file **flist)
574/* Pop file off top of file list. */
575{
576 struct file *f;
577
578 f= *flist;
579 *flist= f->next;
580 return f;
581}
582
583int dotflag(char *name)
584/* Return flag that would make ls list this name: -a or -A. */
585{
586 if (*name++ != '.') return 0;
587
588 switch (*name++) {
589 case 0: return 'a'; /* "." */
590 case '.': if (*name == 0) return 'a'; /* ".." */
591 default: return 'A'; /* ".*" */
592 }
593}
594
595int adddir(struct file **aflist, char *name)
596/* Add directory entries of directory name to a file list. */
597{
598 DIR *d;
599 struct dirent *e;
600
601 if (access(name, 0) < 0) {
602 report(name);
603 return 0;
604 }
605
606 if ((d= opendir(name)) == nil) {
607 report(name);
608 return 0;
609 }
610 while ((e= readdir(d)) != nil) {
611 if (e->d_ino != 0 && present(dotflag(e->d_name))) {
612 pushfile(aflist, newfile(e->d_name));
613 aflist= &(*aflist)->next;
614 }
615 }
616 closedir(d);
617 return 1;
618}
619
620off_t countblocks(struct file *flist)
621/* Compute total block count for a list of files. */
622{
623 off_t cb = 0;
624
625 while (flist != nil) {
626 switch (flist->mode & S_IFMT) {
627 case S_IFDIR:
628 case S_IFREG:
629#ifdef S_IFLNK
630 case S_IFLNK:
631#endif
632 cb += nblocks(flist);
633 }
634 flist= flist->next;
635 }
636 return cb;
637}
638
639void printname(char *name)
640/* Print a name with control characters as '?' (unless -q). The terminal is
641 * assumed to be eight bit clean.
642 */
643{
644 int c, q= present('q');
645
646 while ((c= (unsigned char) *name++) != 0) {
647 if (q && (c < ' ' || c == 0177)) c= '?';
648 putchar(c);
649 }
650}
651
652int mark(struct file *f, int doit)
653{
654 int c;
655
656 c= 0;
657
658 if (field & L_MARK) {
659 switch (f->mode & S_IFMT) {
660 case S_IFDIR: c= '/'; break;
661#ifdef S_IFIFO
662 case S_IFIFO: c= '|'; break;
663#endif
664#ifdef S_IFLNK
665 case S_IFLNK: c= '@'; break;
666#endif
667#ifdef S_IFSOCK
668 case S_IFSOCK: c= '='; break;
669#endif
670 case S_IFREG:
671 if (f->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) c= '*';
672 break;
673 }
674 } else
675 if (field & L_MARKDIR) {
676 if (S_ISDIR(f->mode)) c= '/';
677 }
678
679 if (doit && c != 0) putchar(c);
680 return c;
681}
682
683/* Width of entire column, and of several fields. */
684enum { W_COL, W_INO, W_BLK, W_NLINK, W_UID, W_GID, W_SIZE, W_NAME, MAXFLDS };
685
686unsigned char fieldwidth[MAXCOLS][MAXFLDS];
687
688void maxise(unsigned char *aw, int w)
689/* Set *aw to the larger of it and w. */
690{
691 if (w > *aw) {
692 if (w > UCHAR_MAX) w= UCHAR_MAX;
693 *aw= w;
694 }
695}
696
697int numwidth(unsigned long n)
698/* Compute width of 'n' when printed. */
699{
700 int width= 0;
701
702 do { width++; } while ((n /= 10) > 0);
703 return width;
704}
705
706#if !__minix
707int numxwidth(unsigned long n)
708/* Compute width of 'n' when printed in hex. */
709{
710 int width= 0;
711
712 do { width++; } while ((n /= 16) > 0);
713 return width;
714}
715#endif
716
717static int nsp= 0; /* This many spaces have not been printed yet. */
718#define spaces(n) (nsp= (n))
719#define terpri() (nsp= 0, putchar('\n')) /* No trailing spaces */
720
721void print1(struct file *f, int col, int doit)
722/* Either compute the number of spaces needed to print file f (doit == 0) or
723 * really print it (doit == 1).
724 */
725{
726 int width= 0, n;
727 char *p;
728 unsigned char *f1width = fieldwidth[col];
729
730 while (nsp>0) { putchar(' '); nsp--; }/* Fill gap between two columns */
731
732 if (field & L_INODE) {
733 if (doit) {
734 printf("%*d ", f1width[W_INO], f->ino);
735 } else {
736 maxise(&f1width[W_INO], numwidth(f->ino));
737 width++;
738 }
739 }
740 if (field & L_BLOCKS) {
741 unsigned long nb= nblk2k(nblocks(f));
742 if (doit) {
743 printf("%*lu ", f1width[W_BLK], nb);
744 } else {
745 maxise(&f1width[W_BLK], numwidth(nb));
746 width++;
747 }
748 }
749 if (field & L_MODE) {
750 if (doit) {
751 printf("%s ", permissions(f));
752 } else {
753 width+= (field & L_EXTRA) ? 5 : 11;
754 }
755 }
756 if (field & L_EXTRA) {
757 p= cxsize(f);
758 n= strlen(p)+1;
759
760 if (doit) {
761 n= f1width[W_SIZE] - n;
762 while (n > 0) { putchar(' '); --n; }
763 printf("%s ", p);
764 } else {
765 maxise(&f1width[W_SIZE], n);
766 }
767 }
768 if (field & L_LONG) {
769 if (doit) {
770 printf("%*u ", f1width[W_NLINK], (unsigned) f->nlink);
771 } else {
772 maxise(&f1width[W_NLINK], numwidth(f->nlink));
773 width++;
774 }
775 if (!(field & L_GROUP)) {
776 if (doit) {
777 printf("%-*s ", f1width[W_UID],
778 uidname(f->uid));
779 } else {
780 maxise(&f1width[W_UID],
781 strlen(uidname(f->uid)));
782 width+= 2;
783 }
784 }
785 if (doit) {
786 printf("%-*s ", f1width[W_GID], gidname(f->gid));
787 } else {
788 maxise(&f1width[W_GID], strlen(gidname(f->gid)));
789 width+= 2;
790 }
791
792 switch (f->mode & S_IFMT) {
793 case S_IFBLK:
794 case S_IFCHR:
795#ifdef S_IFMPB
796 case S_IFMPB:
797#endif
798#ifdef S_IFMPC
799 case S_IFMPC:
800#endif
801#if __minix
802 if (doit) {
803 printf("%*d, %3d ", f1width[W_SIZE] - 5,
804 major(f->rdev), minor(f->rdev));
805 } else {
806 maxise(&f1width[W_SIZE],
807 numwidth(major(f->rdev)) + 5);
808 width++;
809 }
810#else /* !__minix */
811 if (doit) {
812 printf("%*lX ", f1width[W_SIZE],
813 (unsigned long) f->rdev);
814 } else {
815 maxise(&f1width[W_SIZE], numwidth(f->rdev));
816 width++;
817 }
818#endif /* !__minix */
819 break;
820 default:
821 if (field & L_KMG) {
822 p= cxsize(f);
823 n= strlen(p)+1;
824
825 if (doit) {
826 n= f1width[W_SIZE] - n;
827 while (n > 0) { putchar(' '); --n; }
828 printf("%s ", p);
829 } else {
830 maxise(&f1width[W_SIZE], n);
831 }
832 } else {
833 if (doit) {
834 printf("%*lu ", f1width[W_SIZE],
835 (unsigned long) f->size);
836 } else {
837 maxise(&f1width[W_SIZE],
838 numwidth(f->size));
839 width++;
840 }
841 }
842 }
843
844 if (doit) {
845 printf("%s ", timestamp(f));
846 } else {
847 width+= (field & L_LONGTIME) ? 21 : 13;
848 }
849 }
850
851 n= strlen(f->name);
852 if (doit) {
853 printname(f->name);
854 if (mark(f, 1) != 0) n++;
855#ifdef S_IFLNK
856 if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
857 char *buf;
858 int r, didx;
859
860 buf= (char *) allocate(((size_t) f->size + 1)
861 * sizeof(buf[0]));
862 addpath(&didx, f->name);
863 r= readlink(path, buf, (int) f->size);
864 delpath(didx);
865 if (r > 0) buf[r] = 0; else r=1, strcpy(buf, "?");
866 printf(" -> ");
867 printname(buf);
868 free((void *) buf);
869 n+= 4 + r;
870 }
871#endif
872 spaces(f1width[W_NAME] - n);
873 } else {
874 if (mark(f, 0) != 0) n++;
875#ifdef S_IFLNK
876 if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
877 n+= 4 + (int) f->size;
878 }
879#endif
880 maxise(&f1width[W_NAME], n + NSEP);
881
882 for (n= 1; n < MAXFLDS; n++) width+= f1width[n];
883 maxise(&f1width[W_COL], width);
884 }
885}
886
887int countfiles(struct file *flist)
888/* Return number of files in the list. */
889{
890 int n= 0;
891
892 while (flist != nil) { n++; flist= flist->next; }
893
894 return n;
895}
896
897struct file *filecol[MAXCOLS]; /* filecol[i] is list of files for column i. */
898int nfiles, nlines; /* # files to print, # of lines needed. */
899
900void columnise(struct file *flist, int nplin)
901/* Chop list of files up in columns. Note that 3 columns are used for 5 files
902 * even though nplin may be 4, filecol[3] will simply be nil.
903 */
904{
905 int i, j;
906
907 nlines= (nfiles + nplin - 1) / nplin; /* nlines needed for nfiles */
908
909 filecol[0]= flist;
910
911 for (i=1; i<nplin; i++) { /* Give nlines files to each column. */
912 for (j=0; j<nlines && flist != nil; j++) flist= flist->next;
913
914 filecol[i]= flist;
915 }
916}
917
918int print(struct file *flist, int nplin, int doit)
919/* Try (doit == 0), or really print the list of files over nplin columns.
920 * Return true if it can be done in nplin columns or if nplin == 1.
921 */
922{
923 register struct file *f;
924 register int col, fld, totlen;
925
926 columnise(flist, nplin);
927
928 if (!doit) {
929 for (col= 0; col < nplin; col++) {
930 for (fld= 0; fld < MAXFLDS; fld++) {
931 fieldwidth[col][fld]= 0;
932 }
933 }
934 }
935
936 while (--nlines >= 0) {
937 totlen= 0;
938
939 for (col= 0; col < nplin; col++) {
940 if ((f= filecol[col]) != nil) {
941 filecol[col]= f->next;
942 print1(f, col, doit);
943 }
944 if (!doit && nplin > 1) {
945 /* See if this line is not too long. */
946 if (fieldwidth[col][W_COL] == UCHAR_MAX) {
947 return 0;
948 }
949 totlen+= fieldwidth[col][W_COL];
950 if (totlen > ncols+NSEP) return 0;
951 }
952 }
953 if (doit) terpri();
954 }
955 return 1;
956}
957
958enum depth { SURFACE, SURFACE1, SUBMERGED };
959enum state { BOTTOM, SINKING, FLOATING };
960
961void listfiles(struct file *flist, enum depth depth, enum state state)
962/* Main workhorse of ls, it sorts and prints the list of files. Flags:
963 * depth: working with the command line / just one file / listing dir.
964 * state: How "recursive" do we have to be.
965 */
966{
967 struct file *dlist= nil, **afl= &flist, **adl= &dlist;
968 int nplin;
969 static int white = 1; /* Nothing printed yet. */
970
971 /* Flush everything previously printed, so new error output will
972 * not intermix with files listed earlier.
973 */
974 fflush(stdout);
975
976 if (field != 0 || state != BOTTOM) { /* Need stat(2) info. */
977 while (*afl != nil) {
978 static struct stat st;
979 int r, didx;
980
981 addpath(&didx, (*afl)->name);
982
983 if ((r= status(path, &st)) < 0
984#ifdef S_IFLNK
985 && (status == lstat || lstat(path, &st) < 0)
986#endif
987 ) {
988 if (depth != SUBMERGED || errno != ENOENT)
989 report((*afl)->name);
990 delfile(popfile(afl));
991 } else {
992 setstat(*afl, &st);
993 afl= &(*afl)->next;
994 }
995 delpath(didx);
996 }
997 }
998 sort(&flist);
999
1000 if (depth == SUBMERGED && (field & (L_BLOCKS | L_LONG))) {
1001 printf("total %ld\n", nblk2k(countblocks(flist)));
1002 }
1003
1004 if (state == SINKING || depth == SURFACE1) {
1005 /* Don't list directories themselves, list their contents later. */
1006 afl= &flist;
1007 while (*afl != nil) {
1008 if (((*afl)->mode & S_IFMT) == S_IFDIR) {
1009 pushfile(adl, popfile(afl));
1010 adl= &(*adl)->next;
1011 } else {
1012 afl= &(*afl)->next;
1013 }
1014 }
1015 }
1016
1017 if ((nfiles= countfiles(flist)) > 0) {
1018 /* Print files in how many columns? */
1019 nplin= !present('C') ? 1 : nfiles < MAXCOLS ? nfiles : MAXCOLS;
1020
1021 while (!print(flist, nplin, 0)) nplin--; /* Try first */
1022
1023 print(flist, nplin, 1); /* Then do it! */
1024 white = 0;
1025 }
1026
1027 while (flist != nil) { /* Destroy file list */
1028 if (state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR) {
1029 /* But keep these directories for ls -R. */
1030 pushfile(adl, popfile(&flist));
1031 adl= &(*adl)->next;
1032 } else {
1033 delfile(popfile(&flist));
1034 }
1035 }
1036
1037 while (dlist != nil) { /* List directories */
1038 if (dotflag(dlist->name) != 'a' || depth != SUBMERGED) {
1039 int didx;
1040
1041 addpath(&didx, dlist->name);
1042
1043 flist= nil;
1044 if (adddir(&flist, path)) {
1045 if (depth != SURFACE1) {
1046 if (!white) putchar('\n');
1047 printf("%s:\n", path);
1048 white = 0;
1049 }
1050 listfiles(flist, SUBMERGED,
1051 state == FLOATING ? FLOATING : BOTTOM);
1052 }
1053 delpath(didx);
1054 }
1055 delfile(popfile(&dlist));
1056 }
1057}
1058
1059int main(int argc, char **argv)
1060{
1061 struct file *flist= nil, **aflist= &flist;
1062 enum depth depth;
1063 char *lsflags;
1064 struct winsize ws;
1065
1066 uid= geteuid();
1067 gid= getegid();
1068
1069 if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++;
1070 argv++;
1071
1072 if (strcmp(arg0, "ls") != 0) {
1073 char *p= arg0+1;
1074
1075 while (*p != 0) {
1076 if (strchr(arg0flag, *p) != nil) *p += 'A' - 'a';
1077 p++;
1078 }
1079 setflags(arg0+1);
1080 }
1081 while (*argv != nil && (*argv)[0] == '-') {
1082 if ((*argv)[1] == '-' && (*argv)[2] == 0) {
1083 argv++;
1084 break;
1085 }
1086 setflags(*argv++ + 1);
1087 }
1088
1089 istty= isatty(1);
1090
1091 if (istty && (lsflags= getenv("LSOPTS")) != nil) {
1092 if (*lsflags == '-') lsflags++;
1093 setflags(lsflags);
1094 }
1095
1096 if (!present('1') && !present('C') && !present('l')
1097 && (istty || present('M') || present('X') || present('F'))
1098 ) setflags("C");
1099
1100 if (istty) setflags("q");
1101
1102 if (SUPER_ID == 0 || present('a')) setflags("A");
1103
1104 if (present('i')) field|= L_INODE;
1105 if (present('s')) field|= L_BLOCKS;
1106 if (present('M')) field|= L_MODE;
1107 if (present('X')) field|= L_EXTRA | L_MODE;
1108 if (present('t')) field|= L_BYTIME;
1109 if (present('u')) field|= L_ATIME;
1110 if (present('c')) field|= L_CTIME;
1111 if (present('l')) field|= L_MODE | L_LONG;
1112 if (present('g')) field|= L_MODE | L_LONG | L_GROUP;
1113 if (present('F')) field|= L_MARK;
1114 if (present('p')) field|= L_MARKDIR;
1115 if (present('D')) field|= L_TYPE;
1116 if (present('T')) field|= L_MODE | L_LONG | L_LONGTIME;
1117 if (present('d')) field|= L_DIR;
1118 if (present('h')) field|= L_KMG;
1119 if (field & L_LONG) field&= ~L_EXTRA;
1120
1121#ifdef S_IFLNK
1122 status= present('L') ? stat : lstat;
1123#endif
1124
1125 if (present('C')) {
1126 int t= istty ? 1 : open("/dev/tty", O_WRONLY);
1127
1128 if (t >= 0 && ioctl(t, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
1129 ncols= ws.ws_col - 1;
1130
1131 if (t != 1 && t != -1) close(t);
1132 }
1133
1134 depth= SURFACE;
1135
1136 if (*argv == nil) {
1137 if (!(field & L_DIR)) depth= SURFACE1;
1138 pushfile(aflist, newfile("."));
1139 } else {
1140 if (argv[1] == nil && !(field & L_DIR)) depth= SURFACE1;
1141
1142 do {
1143 pushfile(aflist, newfile(*argv++));
1144 aflist= &(*aflist)->next;
1145 } while (*argv!=nil);
1146 }
1147 listfiles(flist, depth,
1148 (field & L_DIR) ? BOTTOM : present('R') ? FLOATING : SINKING);
1149 return ex;
1150}
Note: See TracBrowser for help on using the repository browser.