📄 ls.c
字号:
/* ls 4.1 - List files. Author: Kees J. Bot * 25 Apr 1989 * * 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[] 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.) */char _ifmt[] = "0pcCd?bB-?l?s???";#define ifmt(mode) _ifmt[((mode) >> 12) & 0xF]#define nil 0#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <stddef.h>#include <stdlib.h>#include <unistd.h>#include <dirent.h>#include <time.h>#include <pwd.h>#include <grp.h>#include <errno.h>#include <fcntl.h>#include <termios.h>#include <sys/ioctl.h>#ifndef major#define major(dev) ((int) (((dev) >> 8) & 0xFF))#define minor(dev) ((int) (((dev) >> 0) & 0xFF))#endif#if !__minix#define SUPER_ID uid /* Let -A flag be default for SUPER_ID == 0. */#else#define SUPER_ID gid#endif#ifdef S_IFLNKint (*status)(const char *file, struct stat *stp);#else#define status stat#endif/* Basic disk block size is 512 except for one niche O.S. */#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. */int ncols= 79;#define NSEP 2 /* # spaces between columns. */#define MAXCOLS 150 /* Max # of files per line. */char *arg0; /* Last component of argv[0]. */int uid, gid; /* callers id. */int ex= 0; /* Exit status to be. */int istty; /* Output is on a terminal. *//* Safer versions of malloc and realloc: */void heaperr(void){ fprintf(stderr, "%s: Out of memory\n", arg0); exit(-1);}void *allocate(size_t n)/* Deliver or die. */{ void *a; if ((a= malloc(n)) == nil) heaperr(); return a;}#define reallocate rllct /* Same as realloc under some compilers. */void *reallocate(void *a, size_t n){ if ((a= realloc(a, n)) == nil) heaperr(); return a;}char allowed[] = "acdfgilnqrstu1ACFLMRTX";char flags[sizeof(allowed)];char arg0flag[] = "cfmrtx"; /* These in argv[0] go to upper case. */void setflags(char *flgs){ int c; while ((c= *flgs++) != 0) { if (strchr(allowed, c) == nil) { fprintf(stderr, "Usage: %s -[%s] [file ...]\n", arg0, allowed); exit(1); } else if (strchr(flags, c) == nil) { flags[strlen(flags)] = c; } }}int present(int f){ return f == 0 || strchr(flags, f) != nil;}void report(char *f)/* Like perror(3), but in the style: "ls: junk: No such file or directory. */{ fprintf(stderr, "%s: %s: %s\n", arg0, f, strerror(errno)); ex= 1;}/* 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 };struct idname { /* Hash list of names. */ struct idname *next; char *name; uid_t id;} *uids[NNAMES], *gids[NNAMES];char *idname(unsigned id, enum whatmap map)/* Return name for a given user/group id. */{ struct idname *i; struct idname **ids= &(map == PASSWD ? uids : gids)[id % NNAMES]; while ((i= *ids) != nil && id < i->id) ids= &i->next; if (i == nil || id != i->id) { /* Not found, go look in the password or group map. */ char *name= nil; char noname[3 * sizeof(uid_t)]; if (!present('n')) { if (map == PASSWD) { struct passwd *pw= getpwuid(id); if (pw != nil) name= pw->pw_name; } else { struct group *gr= getgrgid(id); if (gr != nil) name= gr->gr_name; } } if (name == nil) { /* Can't find it, weird. Use numerical "name." */ sprintf(noname, "%u", id); name= noname; } /* Add a new id-to-name cell. */ i= allocate(sizeof(*i)); i->id= id; i->name= allocate(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. */char *path; /* Path name constructed in path[]. */int plen= 0, pidx= 0; /* Lenght/index for path[]. */void addpath(int *didx, char *name)/* 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. */{ if (plen == 0) path= (char *) allocate((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= (char *) reallocate((void *) 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. */int field = 0; /* (used to be) Fields that must be printed. */ /* (now) Effects triggered by certain flags. */#define F_INODE 0x001 /* -i */#define F_BLOCKS 0x002 /* -s */#define F_EXTRA 0x004 /* -X */#define F_MODE 0x008 /* -lMX */#define F_LONG 0x010 /* -l */#define F_GROUP 0x020 /* -g */#define F_BYTIME 0x040 /* -tuc */#define F_ATIME 0x080 /* -u */#define F_CTIME 0x100 /* -c */#define F_MARK 0x200 /* -F */#define F_TYPE 0x400 /* -T */#define F_DIR 0x800 /* -d */struct file { /* A file plus stat(2) information. */ struct file *next; /* Lists are made of them. */ char *name; /* Null terminated name. */ ino_t ino; mode_t mode; uid_t uid; gid_t gid; nlink_t nlink; dev_t rdev; off_t size; time_t mtime; time_t atime; time_t ctime;#if ST_BLOCKS long blocks;#endif};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. */static char *timestamp(struct file *f)/* Transform the right time field into something readable. */{ struct tm *tm; time_t t; static time_t now; static int drift= 0; static char date[] = "Jan 19 2038"; static char month[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; t= f->mtime; if (field & F_ATIME) t= f->atime; if (field & F_CTIME) t= f->ctime; tm= localtime(&t); if (--drift < 0) { time(&now); drift= 50; } /* limit time() calls */ if (t < now - PAST || t > now + FUTURE) { sprintf(date, "%.3s %2d %4d", month + 3*tm->tm_mon, tm->tm_mday, 1900 + tm->tm_year); } else { sprintf(date, "%.3s %2d %02d:%02d", month + 3*tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min); } return date;}char *permissions(struct file *f)/* Compute long or short rwx bits. */{ static char rwx[] = "drwxr-x--x"; rwx[0] = ifmt(f->mode); /* 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. */ if (field & F_EXTRA) { /* Short style */ int mode = f->mode, ucase= 0; if (uid == f->uid) { /* What group of bits to use. */ /* mode<<= 0, */ ucase= (mode<<3) | (mode<<6); /* Remember if group or others have permissions. */ } else if (gid == f->gid) { mode<<= 3; } else { mode<<= 6; } rwx[1]= mode&S_IRUSR ? (ucase&S_IRUSR ? 'R' : 'r') : '-'; rwx[2]= mode&S_IWUSR ? (ucase&S_IWUSR ? 'W' : 'w') : '-'; if (mode&S_IXUSR) { static char sbit[]= { 'x', 'g', 'u', 's' }; rwx[3]= sbit[(f->mode&(S_ISUID|S_ISGID))>>10]; if (ucase&S_IXUSR) rwx[3] += 'A'-'a'; } else { rwx[3]= f->mode&(S_ISUID|S_ISGID) ? '=' : '-'; } rwx[4]= 0; } else { /* Long form. */ char *p= rwx+1; int mode= f->mode; 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' : '='; if (f->mode&S_ISGID) rwx[6]= f->mode&(S_IXUSR>>3) ? 's' : '='; if (f->mode&S_ISVTX) rwx[9]= f->mode&(S_IXUSR>>6) ? 't' : '='; } return rwx;}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);}#define K 1024L /* A kilobyte counts in multiples of K */#define T 1000L /* A megabyte in T*K, a gigabyte in T*T*K */char *cxsize(struct file *f)/* Try and fail to turn a 32 bit size into 4 readable characters. */{ static char siz[] = "1.2m"; char *p= siz; off_t z; siz[1]= siz[2]= siz[3]= 0; if (f->size <= 5*K) { /* <= 5K prints as is. */ numeral((int) f->size, &p); return siz; } z= (f->size + K-1) / K; if (z <= 999) { /* Print as 123k. */ numeral((int) z, &p); *p = 'k'; /* Can't use 'K', looks bad */ } else if (z*10 <= 99*T) { /* 1.2m (Try ls -X /dev/at0) */ z= (z*10 + T-1) / T; /* Force roundup */ numeral((int) z / 10, &p); *p++ = '.'; numeral((int) z % 10, &p); *p = 'm'; } else if (z <= 999*T) { /* 123m */ numeral((int) ((z + T-1) / T), &p); *p = 'm'; } else { /* 1.2g */ z= (z*10 + T*T-1) / (T*T); numeral((int) z / 10, &p); *p++ = '.'; numeral((int) z % 10, &p); *p = 'g'; } return siz;}/* Transform size of file to number of blocks. This was once a function that * guessed the number of indirect blocks, but that nonsense has been removed. */#if ST_BLOCKS#define nblocks(f) ((f)->blocks)#else#define nblocks(f) (((f)->size + BLOCK-1) / BLOCK)#endif/* From number of blocks to kilobytes. */#if BLOCK < 1024#define nblk2k(nb) (((nb) + (1024 / BLOCK - 1)) / (1024 / BLOCK))#else#define nblk2k(nb) ((nb) * (BLOCK / 1024))#endifstatic int (*CMP)(struct file *f1, struct file *f2);static int (*rCMP)(struct file *f1, struct file *f2);static void mergesort(struct file **al)/* This is either a stable mergesort, or thermal noise, I'm no longer sure. * It must be called like this: if (L != nil && L->next != nil) mergesort(&L); */{ /* static */ struct file *l1, **mid; /* Need not be local */ struct file *l2; l1= *(mid= &(*al)->next); do { if ((l1= l1->next) == nil) break; mid= &(*mid)->next; } while ((l1= l1->next) != nil); l2= *mid; *mid= nil; if ((*al)->next != nil) mergesort(al); if (l2->next != nil) mergesort(&l2); l1= *al; for (;;) { if ((*CMP)(l1, l2) <= 0) { if ((l1= *(al= &l1->next)) == nil) { *al= l2; break; } } else { *al= l2; l2= *(al= &l2->next); *al= l1; if (l2 == nil) break; } }}int namecmp(struct file *f1, struct file *f2){ return strcmp(f1->name, f2->name);}int mtimecmp(struct file *f1, struct file *f2){ return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;}int atimecmp(struct file *f1, struct file *f2){ return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;}int ctimecmp(struct file *f1, struct file *f2){ return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;}int typecmp(struct file *f1, struct file *f2){ return ifmt(f1->mode) - ifmt(f2->mode);}int revcmp(struct file *f1, struct file *f2) { return (*rCMP)(f2, f1); }static void sort(struct file **al)/* Sort the files according to the flags. */{ if (!present('f') && *al != nil && (*al)->next != nil) { CMP= namecmp; if (!(field & F_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 & F_CTIME) { CMP= ctimecmp; } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -