ls-stub.c (35347B)
- // SPDX-License-Identifier: BSD-3-Clause
- /* ls 4.1 - List files. Author: Kees J. Bot
- * 25 Apr 1989
- */
- /*
- * Changes by Gunnar Ritter, Freiburg i. Br., Germany, October 2001. Taken
- * from the MINIX sources:
- */
- /*
- * Copyright (c) 1987,1997, Prentice Hall All rights reserved.
- *
- * Redistribution and use of the MINIX operating system in source and binary
- * forms, with or without modification, are permitted provided that the
- * following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of Prentice Hall nor the names of the software authors or
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRENTICE HALL OR ANY
- * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * About the amount of bytes for heap + stack under Minix:
- * Ls needs a average amount of 42 bytes per unserviced directory entry, so
- * scanning 10 directory levels deep in an ls -R with 100 entries per directory
- * takes 42000 bytes of heap. So giving ls 10000 bytes is tight, 20000 is
- * usually enough, 40000 is pessimistic.
- */
- /* The array ifmt_c[] is used in an 'ls -l' to map the type of a file to a
- * letter. This is done so that ls can list any future file or device type
- * other than symlinks, without recompilation. (Yes it's dirty.)
- */
- static char ifmt_c[] = "-pc-d-b--nl-SD--";
- /* S_IFIFO
- * S_IFCHR
- * S_IFDIR
- * S_IFBLK
- * S_IFREG
- * S_IFNWK
- * S_IFLNK
- * S_IFSOCK
- * S_IFDOOR
- */
- #define ifmt(mode) ifmt_c[((mode) >> 12) & 0xF]
- #include <ctype.h>
- #include <dirent.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <grp.h>
- #include <libgen.h>
- #include <limits.h>
- #include <locale.h>
- #include <pwd.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <termios.h>
- #include <time.h>
- #include <unistd.h>
- #include <wchar.h>
- #include <wctype.h>
- #if defined(__linux__) || defined(_AIX)
- #include <sys/sysmacros.h>
- #endif /* __linux__ or _AIX, since sys/sysmacros.h
- * adds a definition of "major". */
- #ifndef S_IFNAM
- #define S_IFNAM 0x5000 /* XENIX special named file */
- #endif
- #ifndef S_INSEM
- #define S_INSEM 0x1 /* XENIX semaphore subtype of IFNAM */
- #endif
- #ifndef S_INSHD
- #define S_INSHD 0x2 /* XENIX shared data subtype of IFNAM */
- #endif
- #ifndef S_IFDOOR
- #define S_IFDOOR 0xD000 /* Solaris door */
- #endif
- #ifndef S_IFNWK
- #define S_IFNWK 0x9000 /* HP-UX network special */
- #endif
- #if !__minix
- #define SUPER_ID uid /* Let -A flag be default for SUPER_ID == 0. */
- #else
- #define SUPER_ID gid
- #endif
- #if __minix
- #define BLOCK 1024
- #else
- #define BLOCK 512
- #endif
- /* Assume other systems have st_blocks. */
- #if !__minix
- #define ST_BLOCKS 1
- #endif
- /* Some terminals ignore more than 80 characters on a line. Dumb ones wrap
- * when the cursor hits the side. Nice terminals don't wrap until they have
- * to print the 81st character. Wether we like it or not, no column 80.
- */
- static int ncols = 79;
- #define NSEP 2 /* # spaces between columns. */
- #define MAXCOLS 150 /* Max # of files per line. */
- static char *arg0; /* Last component of argv[0]. */
- static int uid, gid; /* callers id. */
- static int ex = 0; /* Exit status to be. */
- static int istty; /* Output is on a terminal. */
- static int tinfostat = -1; /* terminfo is initalized */
- #ifdef SU3
- static struct visit
- {
- ino_t v_ino;
- dev_t v_dev;
- } *visited;
- static int vismax; /* number of members in visited */
- #endif /* SU3 */
- static enum { PER_LS = 0, PER_DIR = 1 } personality = PER_LS;
- static struct
- {
- const char *per_opt;
- } personalities[] = {
- /* orig: acdfgilnqrstu1ACFLMRTX */
- #ifdef UCB
- {":1RaAdClgrtucFqisfLSUXhH"}, /* PER_LS */
- #else /* !UCB */
- {"1RadCxmnlogrtucpFbqisfLSUXhH"}, /* PER_LS */
- #endif /* !UCB */
- {"1RadCxmnlogrtucpFbqiOfLSXhH"} /* PER_DIR */
- };
- /* Safer versions of malloc and realloc: */
- static void
- heaperr(void)
- {
- write(2, "out of memory\n", 14);
- exit(077);
- }
- /*
- * Deliver or die.
- */
- static void *
- salloc(size_t n)
- {
- void *a;
- if((a = malloc(n)) == NULL) heaperr();
- return a;
- }
- static void *
- srealloc(void *a, size_t n)
- {
- if((a = realloc(a, n)) == NULL) heaperr();
- return a;
- }
- static char flags[0477];
- static int
- present(int f)
- {
- return f == 0 || flags[f] != 0;
- }
- /*
- * Like perror(3), but in the style: "ls: junk: No such file or directory.
- */
- static void
- report(const char *f)
- {
- #ifdef UCB
- fprintf(stderr, "%s not found\n", f);
- #else /* !UCB */
- fprintf(stderr, "%s: %s\n", f, strerror(errno));
- #endif /* !UCB */
- ex = 1;
- }
- #ifdef UCB
- #define blockcount(n) (((n) & 1 ? (n) + 1 : (n)) >> 1)
- #else
- #define blockcount(n) (n)
- #endif
- /*
- * Two functions, uidname and gidname, translate id's to readable names.
- * All names are remembered to avoid searching the password file.
- */
- #define NNAMES (1 << (sizeof(int) + sizeof(char *)))
- enum whatmap
- {
- PASSWD,
- GROUP
- };
- static struct idname
- { /* Hash list of names. */
- struct idname *next;
- char *name;
- uid_t id;
- } *uids[NNAMES], *gids[NNAMES];
- /*
- * Return name for a given user/group id.
- */
- static char *
- idname(unsigned id, enum whatmap map)
- {
- struct idname *i;
- struct idname **ids = &(map == PASSWD ? uids : gids)[id % NNAMES];
- while((i = *ids) != NULL && id < i->id)
- ids = &i->next;
- if(i == NULL || id != i->id)
- {
- /* Not found, go look in the password or group map. */
- char *name = NULL;
- char noname[3 * sizeof(uid_t)];
- if(!present('n'))
- {
- if(map == PASSWD)
- {
- struct passwd *pw = getpwuid(id);
- if(pw != NULL) name = pw->pw_name;
- }
- else
- {
- struct group *gr = getgrgid(id);
- if(gr != NULL) name = gr->gr_name;
- }
- }
- if(name == NULL)
- {
- /* Can't find it, weird. Use numerical "name." */
- sprintf(noname, "%lu", (long)id);
- name = noname;
- }
- /* Add a new id-to-name cell. */
- i = salloc(sizeof(*i));
- i->id = id;
- i->name = salloc(strlen(name) + 1);
- strcpy(i->name, name);
- i->next = *ids;
- *ids = i;
- }
- return i->name;
- }
- #define uidname(uid) idname((uid), PASSWD)
- #define gidname(gid) idname((gid), GROUP)
- /*
- * Path name construction, addpath adds a component, delpath removes it.
- * The string path is used throughout the program as the file under examination.
- */
- static char *path; /* Path name constructed in path[]. */
- static int plen = 0, pidx = 0; /* Lenght/index for path[]. */
- /*
- * Add a component to path. (name may also be a full path at the first call)
- * The index where the current path ends is stored in *pdi.
- */
- static void
- addpath(int *didx, char *name)
- {
- if(plen == 0) path = salloc((plen = 32) * sizeof(path[0]));
- if(pidx == 1 && path[0] == '.') pidx = 0; /* Remove "." */
- *didx = pidx; /* Record point to go back to for delpath. */
- if(pidx > 0 && path[pidx - 1] != '/') path[pidx++] = '/';
- do
- {
- if(*name != '/' || pidx == 0 || path[pidx - 1] != '/')
- {
- if(pidx == plen)
- {
- path = srealloc(path, (plen *= 2) * sizeof(path[0]));
- }
- path[pidx++] = *name;
- }
- } while(*name++ != 0);
- --pidx; /*
- * Put pidx back at the null. The path[pidx++] = '/'
- * statement will overwrite it at the next call.
- */
- }
- #define delpath(didx) (path[pidx = didx] = 0) /* Remove component. */
- static int field = 0; /* (used to be) Fields that must be printed. */
- /* (now) Effects triggered by certain flags. */
- #define FL_INODE 0x001 /* -i */
- #define FL_BLOCKS 0x002 /* -s */
- #define FL_UNUSED 0x004
- #define FL_MODE 0x008 /* -lMX */
- #define FL_LONG 0x010 /* -l */
- #define FL_GROUP 0x020 /* -g */
- #define FL_BYTIME 0x040 /* -tuc */
- #define FL_ATIME 0x080 /* -u */
- #define FL_CTIME 0x100 /* -c */
- #define FL_MARK 0x200 /* -F */
- /*#define FL_UNUSED 0x400 */
- #define FL_DIR 0x800 /* -d */
- #define FL_OWNER 0x1000 /* -o */
- #define FL_STATUS 0x2000
- #define ENDCOL 0x001 /* last printable column in -x mode */
- struct file
- { /* A file plus stat(2) information. */
- off_t size;
- #if ST_BLOCKS
- blkcnt_t blocks;
- #endif
- ino_t ino;
- struct file *next; /* Lists are made of them. */
- struct file *sord; /* Saved order of list. */
- char *name; /* Null terminated name. */
- time_t mtime;
- time_t atime;
- time_t ctime;
- mode_t mode;
- int flag;
- uid_t uid;
- gid_t gid;
- dev_t rdev;
- nlink_t nlink;
- unsigned short namlen;
- };
- static void
- setstat(struct file *f, struct stat *stp)
- {
- f->ino = stp->st_ino;
- f->mode = stp->st_mode;
- f->nlink = stp->st_nlink;
- f->uid = stp->st_uid;
- f->gid = stp->st_gid;
- f->rdev = stp->st_rdev;
- f->size = stp->st_size;
- f->mtime = stp->st_mtime;
- f->atime = stp->st_atime;
- f->ctime = stp->st_ctime;
- #if ST_BLOCKS
- f->blocks = stp->st_blocks;
- #endif
- }
- #define PAST (26 * 7 * 24 * 3600L) /* Half a year ago. */
- /* Between PAST and FUTURE from now a time is printed, otherwise a year. */
- #define FUTURE (15 * 60L) /* Fifteen minutes. */
- /*
- * Transform the right time field into something readable.
- */
- static char *
- timestamp(struct file *f)
- {
- struct tm *tm;
- time_t t;
- static time_t now;
- static int drift = 0;
- static char date[32];
- t = f->mtime;
- if(field & FL_ATIME) t = f->atime;
- if(field & FL_CTIME) t = f->ctime;
- tm = localtime(&t);
- if(--drift < 0)
- {
- time(&now);
- drift = 50;
- } /* limit time() calls */
- if(t < now - PAST || t > now + FUTURE)
- {
- strftime(date, sizeof date - 1, "%b %e %Y", tm);
- }
- else
- {
- strftime(date, sizeof date - 1, "%b %e %H:%M", tm);
- }
- return date;
- }
- /*
- * Compute long or short rwx bits.
- */
- static char *
- permissions(struct file *f)
- {
- /*
- * Note that rwx[0] is a guess for the more alien file types. It is
- * correct for BSD4.3 and derived systems. I just don't know how
- * "standardized" these numbers are.
- */
- static char rwx[] = "drwxr-x--x ";
- char *p = rwx + 1;
- int mode = f->mode;
- rwx[0] = ifmt(f->mode);
- if((f->mode & S_IFMT) == S_IFNAM)
- {
- if(f->rdev == S_INSEM)
- rwx[0] = 's';
- else if(f->rdev == S_INSHD)
- rwx[0] = 'm';
- else
- rwx[0] = '?';
- }
- do
- {
- p[0] = (mode & S_IRUSR) ? 'r' : '-';
- p[1] = (mode & S_IWUSR) ? 'w' : '-';
- p[2] = (mode & S_IXUSR) ? 'x' : '-';
- mode <<= 3;
- } while((p += 3) <= rwx + 7);
- if(f->mode & S_ISUID) rwx[3] = f->mode & (S_IXUSR >> 0) ? 's' : 'S';
- if(f->mode & S_ISGID)
- rwx[6] = f->mode & (S_IXGRP >> 0) ? 's' :
- #ifndef UCB
- (f->mode & S_IFMT) != S_IFDIR ?
- #if defined(SUS) || defined(SU3)
- 'L'
- #else /* !SUS, !SU3 */
- 'l'
- #endif /* !SUS, !SU3 */
- :
- #endif /* !UCB */
- 'S';
- if(f->mode & S_ISVTX) rwx[9] = f->mode & (S_IXUSR >> 6) ? 't' : 'T';
- /*
- * rwx[10] would be the "optional alternate access method flag",
- * leave as a space for now.
- */
- return rwx;
- }
- #ifdef notdef
- void
- numeral(int i, char **pp)
- {
- char itoa[3 * sizeof(int)], *a = itoa;
- do
- *a++ = i % 10 + '0';
- while((i /= 10) > 0);
- do
- *(*pp)++ = *--a;
- while(a > itoa);
- }
- #endif
- static char *
- extension(const char *fn)
- {
- const char *ep = "";
- while(*fn++)
- if(*fn == '.') ep = &fn[1];
- return (char *)ep;
- }
- static int (*CMP)(struct file *f1, struct file *f2);
- static int (*rCMP)(struct file *f1, struct file *f2);
- /*
- * This is either a stable mergesort, or thermal noise, I'm no longer sure.
- * It must be called like this: if (L != NULL && L->next != NULL) mergesort(&L);
- */
- static void
- _mergesort(struct file **al)
- {
- /* static */ struct file *l1, **mid; /* Need not be local */
- struct file *l2;
- l1 = *(mid = &(*al)->next);
- do
- {
- if((l1 = l1->next) == NULL) break;
- mid = &(*mid)->next;
- } while((l1 = l1->next) != NULL);
- l2 = *mid;
- *mid = NULL;
- if((*al)->next != NULL) _mergesort(al);
- if(l2->next != NULL) _mergesort(&l2);
- l1 = *al;
- for(;;)
- {
- if((*CMP)(l1, l2) <= 0)
- {
- if((l1 = *(al = &l1->next)) == NULL)
- {
- *al = l2;
- break;
- }
- }
- else
- {
- *al = l2;
- l2 = *(al = &l2->next);
- *al = l1;
- if(l2 == NULL) break;
- }
- }
- }
- static int
- namecmp(struct file *f1, struct file *f2)
- {
- return strcoll(f1->name, f2->name);
- }
- static int
- extcmp(struct file *f1, struct file *f2)
- {
- return strcoll(extension(f1->name), extension(f2->name));
- }
- static int
- mtimecmp(struct file *f1, struct file *f2)
- {
- return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;
- }
- static int
- atimecmp(struct file *f1, struct file *f2)
- {
- return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;
- }
- static int
- ctimecmp(struct file *f1, struct file *f2)
- {
- return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;
- }
- static int
- sizecmp(struct file *f1, struct file *f2)
- {
- return f1->size == f2->size ? 0 : f1->size > f2->size ? -1 : 1;
- }
- static int
- revcmp(struct file *f1, struct file *f2)
- {
- return (*rCMP)(f2, f1);
- }
- /*
- * Sort the files according to the flags.
- */
- static void
- sort(struct file **al)
- {
- if(present('U')) return;
- if(!present('f') && *al != NULL && (*al)->next != NULL)
- {
- CMP = namecmp;
- if(!(field & FL_BYTIME))
- {
- /* Sort on name */
- if(present('r'))
- {
- rCMP = CMP;
- CMP = revcmp;
- }
- _mergesort(al);
- }
- else
- {
- /* Sort on name first, then sort on time. */
- _mergesort(al);
- if(field & FL_CTIME)
- {
- CMP = ctimecmp;
- }
- else if(field & FL_ATIME)
- {
- CMP = atimecmp;
- }
- else
- {
- CMP = mtimecmp;
- }
- if(present('r'))
- {
- rCMP = CMP;
- CMP = revcmp;
- }
- _mergesort(al);
- }
- if(present('X'))
- {
- CMP = extcmp;
- if(present('r'))
- {
- rCMP = CMP;
- CMP = revcmp;
- }
- _mergesort(al);
- }
- if(present('S'))
- {
- CMP = sizecmp;
- if(present('r'))
- {
- rCMP = CMP;
- CMP = revcmp;
- }
- _mergesort(al);
- }
- }
- }
- /*
- * Create file structure for given name.
- */
- static struct file *
- newfile(char *name)
- {
- struct file *new;
- new = salloc(sizeof(*new));
- new->name = strcpy(salloc(strlen(name) + 1), name);
- new->namlen = 0;
- new->flag = 0;
- return new;
- }
- /*
- * Add file to the head of a list.
- */
- static void
- pushfile(struct file **flist, struct file *new)
- {
- new->next = *flist;
- *flist = new;
- }
- /*
- * Release old file structure.
- */
- static void
- delfile(struct file *old)
- {
- free(old->name);
- free(old);
- }
- /*
- * Pop file off top of file list.
- */
- static struct file *
- popfile(struct file **flist)
- {
- struct file *f;
- f = *flist;
- *flist = f->next;
- return f;
- }
- /*
- * Save the current file order.
- */
- static void
- savord(struct file *f)
- {
- while(f)
- {
- f->sord = f->next;
- f = f->next;
- }
- }
- /*
- * Restore the saved file order.
- */
- static void
- resord(struct file *f)
- {
- while(f)
- {
- f->next = f->sord;
- f = f->sord;
- }
- }
- /*
- * Return flag that would make ls list this name: -a or -A.
- */
- static int
- dotflag(char *name)
- {
- if(*name++ != '.') return 0;
- switch(*name++)
- {
- case 0:
- return 'a'; /* "." */
- case '.':
- if(*name == 0) return 'a'; /* ".." */
- default:
- return 'A'; /* ".*" */
- }
- }
- /*
- * Add directory entries of directory name to a file list.
- */
- static int
- adddir(struct file **aflist, char *name)
- {
- DIR *d;
- struct dirent *e;
- if(access(name, 0) < 0)
- {
- report(name);
- return 0;
- }
- if((d = opendir(name)) == NULL)
- {
- #ifdef UCB
- fprintf(stderr, "%s unreadable\n", name);
- #else
- fprintf(stderr, "can not access directory %s\n", name);
- #endif
- ex = 1;
- return 0;
- }
- while((e = readdir(d)) != NULL)
- {
- if(e->d_ino != 0 && present(dotflag(e->d_name)))
- {
- pushfile(aflist, newfile(e->d_name));
- aflist = &(*aflist)->next;
- }
- }
- closedir(d);
- return 1;
- }
- /*
- * Compute total block count for a list of files.
- */
- static off_t
- countblocks(struct file *flist)
- {
- off_t cb = 0;
- while(flist != NULL)
- {
- switch(flist->mode & S_IFMT)
- {
- case S_IFDIR:
- case S_IFREG:
- #ifdef S_IFLNK
- case S_IFLNK:
- #endif
- cb += flist->blocks;
- }
- flist = flist->next;
- }
- return cb;
- }
- static char *
- #ifdef LONGLONG
- hfmt(long long n, int fill)
- #else
- hfmt(long n, int fill)
- #endif
- {
- static char b[10];
- const char units[] = " KMGTPE", *up = units;
- int rest = 0;
- while(n > 1023)
- {
- rest = (n % 1024) / 128;
- n /= 1024;
- up++;
- }
- #ifdef LONGLONG
- if(up == units)
- snprintf(b, sizeof b, "%*llu", fill ? 5 : 1, n);
- else if(n < 10 && rest)
- snprintf(b, sizeof b, "%*llu.%u%c", fill ? 2 : 1, n, rest, *up);
- else
- snprintf(b, sizeof b, "%*llu%c", fill ? 4 : 1, n, *up);
- #else /* !LONGLONG */
- if(up == units)
- snprintf(b, sizeof b, "%*lu", fill ? 5 : 1, n);
- else if(n < 10 && rest)
- snprintf(b, sizeof b, "%*lu.%u%c", fill ? 2 : 1, n, rest, *up);
- else
- snprintf(b, sizeof b, "%*lu%c", fill ? 4 : 1, n, *up);
- #endif /* !LONGLONG */
- return b;
- }
- #define FC_NORMAL 0
- #define FC_BLACK 30
- #define FC_RED 31
- #define FC_GREEN 32
- #define FC_YELLOW 33
- #define FC_BLUE 34
- #define FC_MAGENTA 35
- #define FC_CYAN 36
- #define FC_WHITE 37
- /*
- * Display a nonprintable character.
- */
- static unsigned
- nonprint(register int c, int doit)
- {
- register int d;
- unsigned n;
- if(present('b'))
- {
- n = 4;
- if(doit)
- {
- char *nums = "01234567";
- putchar('\\');
- putchar(nums[(c & ~077) >> 6]);
- c &= 077;
- d = c & 07;
- if(c > d)
- putchar(nums[(c - d) >> 3]);
- else
- putchar(nums[0]);
- putchar(nums[d]);
- }
- }
- else
- {
- n = 1;
- if(doit) putchar('?');
- }
- return n;
- }
- /*
- * Print a name with control characters as '?' (unless -q). The terminal is
- * assumed to be eight bit clean.
- */
- static unsigned
- printname(const char *name, struct file *f, int doit)
- {
- int c, q = present('q');
- unsigned n = 0;
- if(f != NULL && doit == 0 && f->namlen != 0) return f->namlen;
- if(q || istty)
- {
- #ifdef MB_CUR_MAX
- wchar_t wc;
- while(
- #define ASCII
- #ifdef ASCII
- (*name & 0200) == 0 ? c = 1,
- (wc = *name) != '\0'
- :
- #endif
- (c = mbtowc(&wc, name, MB_CUR_MAX)) != 0)
- {
- if(c != -1)
- {
- if(iswprint(wc)
- #ifdef UCB
- || wc == '\t' || wc == '\n'
- #else /* !UCB */
- && wc != '\t'
- #endif /* !UCB */
- )
- {
- if(doit)
- {
- while(c-- > 0)
- putchar(*name++ & 0377);
- }
- else
- name += c;
- n += wcwidth(wc);
- }
- else
- {
- while(c-- > 0)
- n += nonprint(*name++ & 0377, doit);
- }
- }
- else
- n += nonprint(*name++ & 0377, doit);
- }
- #else /* !MB_CUR_MAX */
- while((c = (*name++ & 0377)) != '\0')
- {
- if(isprint(c)
- #ifdef UCB
- || c == '\t' || c == '\n'
- #else /* !UCB */
- && c != '\t'
- #endif /* !UCB */
- )
- {
- if(doit) putchar(c);
- n++;
- }
- else
- n += nonprint(c, doit);
- }
- #endif /* !MB_CUR_MAX */
- }
- else
- {
- while((c = (*name++ & 0377)) != '\0')
- {
- if(doit) putchar(c);
- n++;
- }
- }
- if(f) f->namlen = n;
- return n;
- }
- static int
- mark(struct file *f, int doit)
- {
- int c;
- if(!(field & FL_MARK)) return 0;
- switch(f->mode & S_IFMT)
- {
- case S_IFDIR:
- c = '/';
- break;
- #ifdef S_IFIFO
- case S_IFIFO:
- c = '|';
- break;
- #endif
- #ifdef S_IFLNK
- case S_IFLNK:
- c = '@';
- break;
- #endif
- #ifdef S_IFSOCK
- case S_IFSOCK:
- c = '=';
- break;
- #endif
- #ifdef S_IFDOOR
- case S_IFDOOR:
- c = '>';
- break;
- #endif
- case S_IFREG:
- if(f->mode & (S_IXUSR | S_IXGRP | S_IXOTH))
- {
- c = '*';
- break;
- }
- default:
- c = 0;
- }
- if(doit && c != 0) putchar(c);
- return c;
- }
- static int colwidth[MAXCOLS]; /* Need colwidth[i] spaces to print column i. */
- static int sizwidth[MAXCOLS]; /* Spaces for the size field in a -X print. */
- static int namwidth[MAXCOLS]; /* Name field. */
- /*
- * Set *aw to the larger of it and w. Then return it.
- */
- static int
- maxise(int *aw, int w)
- {
- if(w > *aw) *aw = w;
- return *aw;
- }
- static int nsp = 0; /* This many spaces have not been printed yet. */
- #define spaces(n) (nsp = (n))
- #define terpri() (nsp = 0, putchar('\n')) /* No trailing spaces */
- /*
- * Either compute the number of spaces needed to print file f (doit == 0) or
- * really print it (doit == 1).
- */
- static int
- print1(struct file *f, int col, int doit)
- {
- int width = 0, n;
- n = printname(f->name, f, 0);
- if(present('m'))
- {
- if(present('p') && (f->mode & S_IFMT) == S_IFDIR) width++;
- if(present('F')) width++;
- }
- while(nsp > 0)
- {
- putchar(' ');
- nsp--;
- } /* Fill gap between two columns */
- if(field & FL_INODE)
- {
- char dummy[2];
- if(doit)
- #ifdef LONGLONG
- printf("%*llu ", present('m') ? 1 : 5, (long long)f->ino);
- #else
- printf("%*lu ", present('m') ? 1 : 5, (long)f->ino);
- #endif
- else
- #ifdef LONGLONG
- width += snprintf(dummy, sizeof dummy, "%*llu ", present('m') ? 1 : 5, (long long)f->ino);
- #else
- width += snprintf(dummy, sizeof dummy, "%*lu ", present('m') ? 1 : 5, (long)f->ino);
- #endif
- }
- if(field & FL_BLOCKS)
- {
- char dummy[2];
- if(doit)
- {
- if(present('h'))
- printf("%s ", hfmt(f->blocks * 512, !present('m')));
- else
- #ifdef LONGLONG
- printf("%*llu ", present('m') ? 1 : 4, (long long)blockcount(f->blocks));
- #else
- printf("%*lu ", present('m') ? 1 : 4, (long)blockcount(f->blocks));
- #endif
- }
- else
- {
- if(present('h'))
- width += 6;
- else
- #ifdef LONGLONG
- width +=
- snprintf(dummy, sizeof dummy, "%*llu ", present('m') ? 1 : 4, (long long)f->blocks);
- #else
- width += snprintf(dummy, sizeof dummy, "%*lu ", present('m') ? 1 : 4, (long)f->blocks);
- #endif
- }
- }
- if(field & FL_MODE)
- {
- if(doit)
- {
- printf("%s ", permissions(f));
- }
- else
- {
- width += 11;
- }
- }
- if(field & FL_LONG)
- {
- if(doit)
- {
- printf("%2u ", (unsigned)f->nlink);
- if(!(field & FL_GROUP))
- {
- printf("%-8s ", uidname(f->uid));
- }
- if(!(field & FL_OWNER))
- {
- printf("%-8s ", gidname(f->gid));
- }
- switch(f->mode & S_IFMT)
- {
- case S_IFBLK:
- case S_IFCHR:
- #ifdef S_IFMPB
- case S_IFMPB:
- #endif
- #ifdef S_IFMPC
- case S_IFMPC:
- #endif
- printf("%3lu,%3lu ", (long)major(f->rdev), (long)minor(f->rdev));
- break;
- default:
- if(present('h'))
- printf("%5s ", hfmt(f->size, 1));
- else
- #ifdef LONGLONG
- printf("%7llu ", (long long)f->size);
- #else
- printf("%7lu ", (long)f->size);
- #endif
- }
- printf("%s ", timestamp(f));
- }
- else
- {
- width += (field & FL_GROUP) ? 34 : 43;
- }
- }
- if(doit)
- {
- printname(f->name, f, 1);
- if(mark(f, 1) != 0) n++;
- if(present('p') && (f->mode & S_IFMT) == S_IFDIR)
- {
- n++;
- if(doit) putchar('/');
- }
- #ifdef S_IFLNK
- if((field & FL_LONG) && (f->mode & S_IFMT) == S_IFLNK)
- {
- char *buf;
- int sz, r, didx;
- sz = f->size ? f->size : PATH_MAX;
- buf = salloc(sz + 1);
- addpath(&didx, f->name);
- if((r = readlink(path, buf, sz)) < 0) r = 0;
- delpath(didx);
- buf[r] = 0;
- printf(" -> ");
- printname(buf, NULL, 1);
- free(buf);
- n += 4 + r;
- }
- #endif
- if(!present('m')) spaces(namwidth[col] - n);
- }
- else
- {
- if(mark(f, 0) != 0) n++;
- #ifdef S_IFLNK
- if((field & FL_LONG) && (f->mode & S_IFMT) == S_IFLNK)
- {
- n += 4 + (int)f->size;
- }
- #endif
- if(!present('m'))
- {
- width += maxise(&namwidth[col], n + NSEP);
- maxise(&colwidth[col], width);
- }
- }
- return n + width;
- }
- /*
- * Return number of files in the list.
- */
- static int
- countfiles(struct file *flist)
- {
- int n = 0;
- while(flist != NULL)
- {
- n++;
- flist = flist->next;
- }
- return n;
- }
- static struct file *filecol[MAXCOLS]; /*
- * filecol[i] is list of files
- * for column i.
- */
- static int nfiles, nlines; /* # files to print, # of lines needed. */
- /*
- * Chop list of files up in columns. Note that 3 columns are used for 5 files
- * even though nplin may be 4, filecol[3] will simply be NULL.
- */
- static void
- columnise(struct file **flist, struct file *fsav, int nplin)
- {
- struct file *f = *flist;
- int i, j;
- nlines = (nfiles + nplin - 1) / nplin; /* nlines needed for nfiles */
- filecol[0] = f;
- if(!present('x'))
- {
- for(i = 1; i < nplin; i++)
- {
- /* Give nlines files to each column. */
- for(j = 0; j < nlines && f != NULL; j++)
- f = f->next;
- filecol[i] = f;
- }
- }
- else
- {
- /*
- * Ok, this is an ugly hack. We use the mechanisms for '-C'
- * and thus have to change the file list order.
- */
- struct file *curcol[MAXCOLS];
- resord(fsav);
- *flist = fsav;
- f = *flist;
- for(i = 0; i < nplin && f; i++)
- {
- filecol[i] = curcol[i] = f;
- f->flag &= ~ENDCOL;
- f = f->next;
- }
- while(f != NULL)
- {
- for(i = 0; i < nplin && f; i++)
- {
- curcol[i]->next = f;
- curcol[i] = f;
- f->flag &= ~ENDCOL;
- f = f->next;
- }
- }
- for(i = 1; i < nplin; i++)
- {
- curcol[i - 1]->next = filecol[i];
- curcol[i - 1]->flag |= ENDCOL;
- }
- curcol[nplin - 1]->next = NULL;
- }
- }
- /*
- * Try (doit == 0), or really print the list of files over nplin columns.
- * Return true if it can be done in nplin columns or if nplin == 1.
- */
- static int
- print(struct file **flist, struct file *fsav, int nplin, int doit)
- {
- register struct file *f;
- register int i, totlen;
- if(present('m'))
- {
- if(!doit) return 1;
- totlen = 0;
- for(f = *flist; f; f = f->next)
- {
- i = print1(f, 0, 0) + 2;
- totlen += i;
- if(totlen > ncols + 1)
- {
- putchar('\n');
- totlen = i;
- }
- else if(f != *flist)
- putchar(' ');
- print1(f, 0, 1);
- if(f->next) putchar(',');
- }
- putchar('\n');
- return 1;
- }
- columnise(flist, fsav, nplin);
- if(!doit)
- {
- if(nplin == 1) return 1; /* No need to try 1 column. */
- for(i = 0; i < nplin; i++)
- {
- colwidth[i] = sizwidth[i] = namwidth[i] = 0;
- }
- }
- while(--nlines >= 0)
- {
- totlen = 0;
- for(i = 0; i < nplin; i++)
- {
- if((f = filecol[i]) != NULL)
- {
- if(f->flag & ENDCOL)
- filecol[i] = NULL;
- else
- filecol[i] = f->next;
- print1(f, i, doit);
- }
- if(!doit && nplin > 1)
- {
- /* See if this line is not too long. */
- totlen += colwidth[i];
- if(totlen > ncols + NSEP) return 0;
- }
- }
- if(doit && !present('m')) terpri();
- }
- return 1;
- }
- enum depth
- {
- SURFACE,
- SURFACE1,
- SUBMERGED
- };
- enum state
- {
- BOTTOM,
- SINKING,
- FLOATING
- };
- /*
- * Main workhorse of ls, it sorts and prints the list of files. Flags:
- * depth: working with the command line / just one file / listing dir.
- * state: How "recursive" do we have to be.
- */
- static void
- listfiles(struct file *flist, enum depth depth, enum state state, int level)
- {
- struct file *dlist = NULL, **afl = &flist, **adl = &dlist, *fsav;
- int nplin, t;
- static int white = 1; /* Nothing printed yet. */
- /*
- * Flush everything previously printed, so new error output will
- * not intermix with files listed earlier.
- */
- fflush(stdout);
- if(field != 0 || state != BOTTOM)
- { /* Need stat(2) info. */
- while(*afl != NULL)
- {
- static struct stat st;
- int didx;
- #ifdef S_IFLNK
- int (*status)(const char *file, struct stat *stp) =
- depth == SURFACE1 && (field & FL_LONG) == 0
- #ifdef SU3
- && !present('F')
- #endif /* SU3 */
- || present('L') || present('H') && depth != SUBMERGED
- ? stat
- : lstat;
- #else
- #define status stat
- #endif
- /* Basic disk block size is 512 except for one niche O.S. */
- addpath(&didx, (*afl)->name);
- if((t = status(path, &st)) < 0
- #ifdef S_IFLNK
- && (status == lstat || lstat(path, &st) < 0)
- #endif
- )
- {
- if(depth != SUBMERGED || errno != ENOENT) report((*afl)->name);
- #ifdef SU3
- fail:
- #endif /* SU3 */
- delfile(popfile(afl));
- }
- else
- {
- #ifdef SU3
- if(t < 0 && errno == ELOOP && (present('H') || present('L')))
- {
- report((*afl)->name);
- goto fail;
- }
- if(present('L'))
- {
- int i;
- for(i = 0; i < level; i++)
- {
- if(st.st_dev == visited[i].v_dev && st.st_ino == visited[i].v_ino)
- {
- fprintf(stderr,
- "link "
- "loop at %s\n",
- path);
- ex = 1;
- goto fail;
- }
- }
- if(level >= vismax)
- {
- vismax += 20;
- visited = srealloc(visited, sizeof *visited * vismax);
- }
- visited[level].v_dev = st.st_dev;
- visited[level].v_ino = st.st_ino;
- }
- #endif /* SU3 */
- if(((field & FL_MARK) || tinfostat == 1 || present('H')) && !present('L') &&
- status != lstat && (st.st_mode & S_IFMT) != S_IFDIR)
- {
- struct stat lst;
- if(lstat(path, &lst) == 0) st = lst;
- }
- setstat(*afl, &st);
- afl = &(*afl)->next;
- }
- delpath(didx);
- }
- }
- sort(&flist);
- if(depth == SUBMERGED && (field & (FL_BLOCKS | FL_LONG)))
- {
- printf("total ");
- if(present('h'))
- printf("%s\n", hfmt(countblocks(flist) * 512, 0));
- else
- #ifdef LONGLONG
- printf("%lld\n", (long long)blockcount(countblocks(flist)));
- #else
- printf("%ld\n", (long)blockcount(countblocks(flist)));
- #endif
- }
- if(state == SINKING || depth == SURFACE1)
- {
- /* Don't list directories themselves, list their contents later. */
- afl = &flist;
- while(*afl != NULL)
- {
- if(((*afl)->mode & S_IFMT) == S_IFDIR)
- {
- pushfile(adl, popfile(afl));
- adl = &(*adl)->next;
- }
- else
- {
- afl = &(*afl)->next;
- }
- }
- }
- if((nfiles = countfiles(flist)) > 0)
- {
- /* Print files in how many columns? */
- nplin = !present('C') && !present('x') ? 1 : nfiles < MAXCOLS ? nfiles : MAXCOLS;
- fsav = flist;
- if(present('x')) savord(flist);
- while(!print(&flist, fsav, nplin, 0))
- nplin--; /* Try first */
- print(&flist, fsav, nplin, 1); /* Then do it! */
- white = 0;
- }
- while(flist != NULL)
- { /* Destroy file list */
- if(state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR)
- {
- /* But keep these directories for ls -R. */
- pushfile(adl, popfile(&flist));
- adl = &(*adl)->next;
- }
- else
- {
- delfile(popfile(&flist));
- }
- }
- while(dlist != NULL)
- { /* List directories */
- if(dotflag(dlist->name) != 'a' || depth != SUBMERGED)
- {
- int didx;
- addpath(&didx, dlist->name);
- flist = NULL;
- if(adddir(&flist, path))
- {
- if(depth != SURFACE1)
- {
- if(!white) putchar('\n');
- white = 0;
- }
- if(depth != SURFACE1 || present('R')) printf("%s:\n", path);
- listfiles(flist, SUBMERGED, state == FLOATING ? FLOATING : BOTTOM, level + 1);
- }
- delpath(didx);
- }
- delfile(popfile(&dlist));
- }
- }
- #ifndef UCB
- static void
- usage(void)
- {
- if(personality == PER_LS) fprintf(stderr, "usage: %s -1RadCxmnlogrtucpFbqisfL [files]\n", arg0);
- exit(2);
- }
- #else /* UCB */
- #define usage()
- #endif /* UCB */
- int
- main(int argc, char **argv)
- {
- struct file *flist = NULL, **aflist = &flist;
- enum depth depth;
- struct winsize ws;
- int i;
- char *cp;
- setlocale(LC_ALL, "");
- uid = geteuid();
- gid = getegid();
- arg0 = basename(argv[0]);
- if(strcmp(arg0, "dir") == 0)
- {
- flags['s'] = flags['U'] = 1;
- personality = PER_DIR;
- }
- else if(strcmp(arg0, "lc") == 0)
- {
- flags['C'] = 1;
- istty = 1;
- }
- if(istty || isatty(1))
- {
- istty = 1;
- field |= FL_STATUS;
- }
- while((i = getopt(argc, argv, personalities[personality].per_opt)) != EOF)
- {
- switch(i)
- {
- case '?':
- usage();
- break;
- case 'O':
- flags['U'] = 0;
- break;
- case '1':
- flags[i] = 1;
- flags['C'] = 0;
- break;
- case 'L':
- flags[i] = 1;
- flags['H'] = 0;
- break;
- case 'H':
- flags[i] = 1;
- flags['L'] = 0;
- break;
- #if defined(SUS) || defined(SU3)
- case 'C':
- flags[i] = 1;
- flags['l'] = flags['m'] = flags['x'] = flags['o'] = flags['g'] = flags['n'] = flags['1'] = 0;
- break;
- case 'm':
- flags[i] = 1;
- flags['l'] = flags['C'] = flags['x'] = flags['o'] = flags['g'] = flags['n'] = flags['1'] = 0;
- break;
- case 'l':
- flags[i] = 1;
- flags['m'] = flags['C'] = flags['x'] = flags['o'] = flags['g'] = 0;
- break;
- case 'x':
- flags[i] = 1;
- flags['m'] = flags['C'] = flags['l'] = flags['o'] = flags['g'] = flags['n'] = flags['1'] = 0;
- break;
- case 'g':
- flags[i] = 1;
- flags['m'] = flags['C'] = flags['l'] = flags['x'] = flags['o'] = 0;
- break;
- case 'o':
- flags[i] = 1;
- flags['m'] = flags['C'] = flags['l'] = flags['x'] = flags['g'] = 0;
- break;
- case 'n':
- flags[i] = 1;
- flags['m'] = flags['C'] = flags['x'] = 0;
- break;
- #endif /* !SUS, !SU3 */
- default:
- flags[i] = 1;
- }
- }
- #ifdef UCB
- if(present('l'))
- {
- if(present('g'))
- flags['g'] = 0;
- else
- flags['o'] = 1;
- }
- else if(present('g'))
- flags['g'] = 0;
- #endif
- #ifdef UCB
- if(SUPER_ID == 0 || present('a')) flags['A'] = 1;
- #else /* !UCB */
- if(present('a')) flags['A'] = 1;
- #endif /* !UCB */
- if(present('i')) field |= FL_INODE;
- if(present('s')) field |= FL_BLOCKS;
- if(present('t')) field |= FL_BYTIME;
- if(present('u')) field |= FL_ATIME;
- if(present('c')) field |= FL_CTIME;
- if(present('l') || present('n') || present('g') || present('o'))
- {
- field = field | FL_MODE | FL_LONG;
- flags['m'] = flags['C'] = flags['x'] = 0;
- }
- if(present('g')) field = field | FL_MODE | FL_LONG | FL_GROUP;
- if(present('o')) field = field | FL_MODE | FL_LONG | FL_OWNER;
- if(present('F')) field |= FL_MARK;
- if(present('d')) field |= FL_DIR;
- if(present('f'))
- {
- field &= ~(FL_LONG | FL_BYTIME | FL_BLOCKS | FL_MODE | FL_MARK | FL_DIR);
- flags['o'] = flags['g'] = flags['l'] = flags['t'] = flags['s'] = flags['r'] = flags['d'] =
- flags['F'] = flags['R'] = flags['p'] = 0;
- flags['a'] = flags['A'] = 1;
- field |= FL_STATUS;
- }
- if(!present('1') && !present('C') && !present('l') && !present('o') && !present('g') &&
- !present('m') && (istty || present('x')))
- flags['C'] = 1;
- if(istty) flags['q'] = 1;
- if((cp = getenv("COLUMNS")) != NULL)
- {
- ncols = atoi(cp);
- }
- else if((present('C') || present('x') || present('m')) && istty)
- {
- if(ioctl(1, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0) ncols = ws.ws_col - 1;
- }
- depth = SURFACE;
- if(optind == argc)
- {
- if(!(field & FL_DIR)) depth = SURFACE1;
- pushfile(aflist, newfile("."));
- }
- else
- {
- if(optind + 1 == argc && !(field & FL_DIR)) depth = SURFACE1;
- while(optind < argc)
- {
- if(present('f'))
- {
- struct stat st;
- if(stat(argv[optind], &st) == 0 && (st.st_mode & S_IFMT) != S_IFDIR)
- {
- #ifdef UCB
- fprintf(stderr, "%s unreadable\n", argv[optind]);
- #else
- fprintf(stderr, "%s: Not a directory\n", argv[optind]);
- #endif
- ex = 1;
- optind++;
- continue;
- }
- }
- pushfile(aflist, newfile(argv[optind++]));
- aflist = &(*aflist)->next;
- }
- }
- listfiles(flist, depth, (field & FL_DIR) ? BOTTOM : present('R') ? FLOATING : SINKING, 0);
- return ex;
- }