[9] | 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 | */
|
---|
| 15 | char 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
|
---|
| 49 | int (*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 | */
|
---|
| 70 | int ncols= 79;
|
---|
| 71 |
|
---|
| 72 | #define NSEP 3 /* # spaces between columns. */
|
---|
| 73 |
|
---|
| 74 | #define MAXCOLS 128 /* Max # of files per line. */
|
---|
| 75 |
|
---|
| 76 | char *arg0; /* Last component of argv[0]. */
|
---|
| 77 | int uid, gid; /* callers id. */
|
---|
| 78 | int ex= 0; /* Exit status to be. */
|
---|
| 79 | int istty; /* Output is on a terminal. */
|
---|
| 80 |
|
---|
| 81 | /* Safer versions of malloc and realloc: */
|
---|
| 82 |
|
---|
| 83 | void heaperr(void)
|
---|
| 84 | {
|
---|
| 85 | fprintf(stderr, "%s: Out of memory\n", arg0);
|
---|
| 86 | exit(-1);
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | void *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 |
|
---|
| 98 | void *reallocate(void *a, size_t n)
|
---|
| 99 | {
|
---|
| 100 | if ((a= realloc(a, n)) == nil) heaperr();
|
---|
| 101 | return a;
|
---|
| 102 | }
|
---|
| 103 |
|
---|
| 104 | char allowed[] = "acdfghilnpqrstu1ACDFLMRTX";
|
---|
| 105 | char flags[sizeof(allowed)];
|
---|
| 106 |
|
---|
| 107 | char arg0flag[] = "cdfmrtx"; /* These in argv[0] go to upper case. */
|
---|
| 108 |
|
---|
| 109 | void 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 |
|
---|
| 125 | int present(int f)
|
---|
| 126 | {
|
---|
| 127 | return f == 0 || strchr(flags, f) != nil;
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | void 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 *)))
|
---|
| 141 | enum whatmap { PASSWD, GROUP };
|
---|
| 142 |
|
---|
| 143 | struct idname { /* Hash list of names. */
|
---|
| 144 | struct idname *next;
|
---|
| 145 | char *name;
|
---|
| 146 | uid_t id;
|
---|
| 147 | } *uids[NNAMES], *gids[NNAMES];
|
---|
| 148 |
|
---|
| 149 | char *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 |
|
---|
| 197 | char *path; /* Path name constructed in path[]. */
|
---|
| 198 | int plen= 0, pidx= 0; /* Lenght/index for path[]. */
|
---|
| 199 |
|
---|
| 200 | void 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 |
|
---|
| 230 | int 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 |
|
---|
| 249 | struct 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 |
|
---|
| 267 | void 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 |
|
---|
| 288 | static 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 |
|
---|
| 326 | char *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 |
|
---|
| 380 | void 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 |
|
---|
| 392 | char *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 |
|
---|
| 447 | static int (*CMP)(struct file *f1, struct file *f2);
|
---|
| 448 | static int (*rCMP)(struct file *f1, struct file *f2);
|
---|
| 449 |
|
---|
| 450 | static 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 |
|
---|
| 486 | int namecmp(struct file *f1, struct file *f2)
|
---|
| 487 | {
|
---|
| 488 | return strcmp(f1->name, f2->name);
|
---|
| 489 | }
|
---|
| 490 |
|
---|
| 491 | int mtimecmp(struct file *f1, struct file *f2)
|
---|
| 492 | {
|
---|
| 493 | return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;
|
---|
| 494 | }
|
---|
| 495 |
|
---|
| 496 | int atimecmp(struct file *f1, struct file *f2)
|
---|
| 497 | {
|
---|
| 498 | return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;
|
---|
| 499 | }
|
---|
| 500 |
|
---|
| 501 | int ctimecmp(struct file *f1, struct file *f2)
|
---|
| 502 | {
|
---|
| 503 | return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;
|
---|
| 504 | }
|
---|
| 505 |
|
---|
| 506 | int typecmp(struct file *f1, struct file *f2)
|
---|
| 507 | {
|
---|
| 508 | return ifmt(f1->mode) - ifmt(f2->mode);
|
---|
| 509 | }
|
---|
| 510 |
|
---|
| 511 | int revcmp(struct file *f1, struct file *f2) { return (*rCMP)(f2, f1); }
|
---|
| 512 |
|
---|
| 513 | static 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 |
|
---|
| 549 | struct 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 |
|
---|
| 559 | void 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 |
|
---|
| 566 | void delfile(struct file *old)
|
---|
| 567 | /* Release old file structure. */
|
---|
| 568 | {
|
---|
| 569 | free((void *) old->name);
|
---|
| 570 | free((void *) old);
|
---|
| 571 | }
|
---|
| 572 |
|
---|
| 573 | struct 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 |
|
---|
| 583 | int 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 |
|
---|
| 595 | int 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 |
|
---|
| 620 | off_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 |
|
---|
| 639 | void 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 |
|
---|
| 652 | int 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. */
|
---|
| 684 | enum { W_COL, W_INO, W_BLK, W_NLINK, W_UID, W_GID, W_SIZE, W_NAME, MAXFLDS };
|
---|
| 685 |
|
---|
| 686 | unsigned char fieldwidth[MAXCOLS][MAXFLDS];
|
---|
| 687 |
|
---|
| 688 | void 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 |
|
---|
| 697 | int 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
|
---|
| 707 | int 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 |
|
---|
| 717 | static 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 |
|
---|
| 721 | void 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 |
|
---|
| 887 | int 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 |
|
---|
| 897 | struct file *filecol[MAXCOLS]; /* filecol[i] is list of files for column i. */
|
---|
| 898 | int nfiles, nlines; /* # files to print, # of lines needed. */
|
---|
| 899 |
|
---|
| 900 | void 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 |
|
---|
| 918 | int 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 |
|
---|
| 958 | enum depth { SURFACE, SURFACE1, SUBMERGED };
|
---|
| 959 | enum state { BOTTOM, SINKING, FLOATING };
|
---|
| 960 |
|
---|
| 961 | void 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 |
|
---|
| 1059 | int 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 | }
|
---|