ls.c (9566B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/stat.h> 3 #include <sys/types.h> 4 #ifndef major 5 #include <sys/sysmacros.h> 6 #endif 7 8 #include <dirent.h> 9 #include <grp.h> 10 #include <pwd.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <time.h> 15 #include <unistd.h> 16 17 #include "utf.h" 18 #include "util.h" 19 20 struct entry { 21 char *name; 22 mode_t mode, tmode; 23 nlink_t nlink; 24 uid_t uid; 25 gid_t gid; 26 off_t size; 27 struct timespec t; 28 dev_t dev; 29 dev_t rdev; 30 ino_t ino, tino; 31 }; 32 33 static struct { 34 dev_t dev; 35 ino_t ino; 36 } tree[PATH_MAX]; 37 38 static int ret = 0; 39 static int Aflag = 0; 40 static int aflag = 0; 41 static int cflag = 0; 42 static int dflag = 0; 43 static int Fflag = 0; 44 static int fflag = 0; 45 static int Hflag = 0; 46 static int hflag = 0; 47 static int iflag = 0; 48 static int Lflag = 0; 49 static int lflag = 0; 50 static int nflag = 0; 51 static int pflag = 0; 52 static int qflag = 0; 53 static int Rflag = 0; 54 static int rflag = 0; 55 static int Uflag = 0; 56 static int uflag = 0; 57 static int first = 1; 58 static char sort = 0; 59 static int showdirs; 60 61 static void ls(const char *, const struct entry *, int); 62 63 static void 64 mkent(struct entry *ent, char *path, int dostat, int follow) 65 { 66 struct stat st; 67 68 ent->name = path; 69 if (!dostat) 70 return; 71 if ((follow ? stat : lstat)(path, &st) < 0) 72 eprintf("%s %s:", follow ? "stat" : "lstat", path); 73 ent->mode = st.st_mode; 74 ent->nlink = st.st_nlink; 75 ent->uid = st.st_uid; 76 ent->gid = st.st_gid; 77 ent->size = st.st_size; 78 if (cflag) 79 ent->t = st.st_ctim; 80 else if (uflag) 81 ent->t = st.st_atim; 82 else 83 ent->t = st.st_mtim; 84 ent->dev = st.st_dev; 85 ent->rdev = st.st_rdev; 86 ent->ino = st.st_ino; 87 if (S_ISLNK(ent->mode)) { 88 if (stat(path, &st) == 0) { 89 ent->tmode = st.st_mode; 90 ent->dev = st.st_dev; 91 ent->tino = st.st_ino; 92 } else { 93 ent->tmode = ent->tino = 0; 94 } 95 } 96 } 97 98 static char * 99 indicator(mode_t mode) 100 { 101 if (pflag || Fflag) 102 if (S_ISDIR(mode)) 103 return "/"; 104 105 if (Fflag) { 106 if (S_ISLNK(mode)) 107 return "@"; 108 else if (S_ISFIFO(mode)) 109 return "|"; 110 else if (S_ISSOCK(mode)) 111 return "="; 112 else if (mode & S_IXUSR || mode & S_IXGRP || mode & S_IXOTH) 113 return "*"; 114 } 115 116 return ""; 117 } 118 119 static void 120 printname(const char *name) 121 { 122 const char *c; 123 Rune r; 124 size_t l; 125 126 for (c = name; *c; c += l) { 127 l = chartorune(&r, c); 128 if (!qflag || isprintrune(r)) 129 fwrite(c, 1, l, stdout); 130 else 131 putchar('?'); 132 } 133 } 134 135 static void 136 output(const struct entry *ent) 137 { 138 struct group *gr; 139 struct passwd *pw; 140 struct tm *tm; 141 ssize_t len; 142 char *fmt, buf[BUFSIZ], pwname[_SC_LOGIN_NAME_MAX], 143 grname[_SC_LOGIN_NAME_MAX], mode[] = "----------"; 144 145 if (iflag) 146 printf("%lu ", (unsigned long)ent->ino); 147 if (!lflag) { 148 printname(ent->name); 149 puts(indicator(ent->mode)); 150 return; 151 } 152 if (S_ISREG(ent->mode)) 153 mode[0] = '-'; 154 else if (S_ISBLK(ent->mode)) 155 mode[0] = 'b'; 156 else if (S_ISCHR(ent->mode)) 157 mode[0] = 'c'; 158 else if (S_ISDIR(ent->mode)) 159 mode[0] = 'd'; 160 else if (S_ISFIFO(ent->mode)) 161 mode[0] = 'p'; 162 else if (S_ISLNK(ent->mode)) 163 mode[0] = 'l'; 164 else if (S_ISSOCK(ent->mode)) 165 mode[0] = 's'; 166 else 167 mode[0] = '?'; 168 169 if (ent->mode & S_IRUSR) mode[1] = 'r'; 170 if (ent->mode & S_IWUSR) mode[2] = 'w'; 171 if (ent->mode & S_IXUSR) mode[3] = 'x'; 172 if (ent->mode & S_IRGRP) mode[4] = 'r'; 173 if (ent->mode & S_IWGRP) mode[5] = 'w'; 174 if (ent->mode & S_IXGRP) mode[6] = 'x'; 175 if (ent->mode & S_IROTH) mode[7] = 'r'; 176 if (ent->mode & S_IWOTH) mode[8] = 'w'; 177 if (ent->mode & S_IXOTH) mode[9] = 'x'; 178 179 if (ent->mode & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S'; 180 if (ent->mode & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S'; 181 if (ent->mode & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T'; 182 183 if (!nflag && (pw = getpwuid(ent->uid))) 184 snprintf(pwname, sizeof(pwname), "%s", pw->pw_name); 185 else 186 snprintf(pwname, sizeof(pwname), "%d", ent->uid); 187 188 if (!nflag && (gr = getgrgid(ent->gid))) 189 snprintf(grname, sizeof(grname), "%s", gr->gr_name); 190 else 191 snprintf(grname, sizeof(grname), "%d", ent->gid); 192 193 if (time(NULL) > ent->t.tv_sec + (180 * 24 * 60 * 60)) /* 6 months ago? */ 194 fmt = "%b %d %Y"; 195 else 196 fmt = "%b %d %H:%M"; 197 198 if ((tm = localtime(&ent->t.tv_sec))) 199 strftime(buf, sizeof(buf), fmt, tm); 200 else 201 snprintf(buf, sizeof(buf), "%lld", (long long)(ent->t.tv_sec)); 202 printf("%s %4ld %-8.8s %-8.8s ", mode, (long)ent->nlink, pwname, grname); 203 204 if (S_ISBLK(ent->mode) || S_ISCHR(ent->mode)) 205 printf("%4u, %4u ", major(ent->rdev), minor(ent->rdev)); 206 else if (hflag) 207 printf("%10s ", humansize(ent->size)); 208 else 209 printf("%10lu ", (unsigned long)ent->size); 210 printf("%s ", buf); 211 printname(ent->name); 212 fputs(indicator(ent->mode), stdout); 213 if (S_ISLNK(ent->mode)) { 214 if ((len = readlink(ent->name, buf, sizeof(buf) - 1)) < 0) 215 eprintf("readlink %s:", ent->name); 216 buf[len] = '\0'; 217 printf(" -> %s%s", buf, indicator(ent->tmode)); 218 } 219 putchar('\n'); 220 } 221 222 static int 223 entcmp(const void *va, const void *vb) 224 { 225 int cmp = 0; 226 const struct entry *a = va, *b = vb; 227 228 switch (sort) { 229 case 'S': 230 cmp = b->size - a->size; 231 break; 232 case 't': 233 if (!(cmp = b->t.tv_sec - a->t.tv_sec)) 234 cmp = b->t.tv_nsec - a->t.tv_nsec; 235 break; 236 } 237 238 if (!cmp) 239 cmp = strcmp(a->name, b->name); 240 241 return rflag ? 0 - cmp : cmp; 242 } 243 244 static void 245 lsdir(const char *path, const struct entry *dir) 246 { 247 DIR *dp; 248 struct entry *ent, *ents = NULL; 249 struct dirent *d; 250 size_t i, n = 0; 251 char prefix[PATH_MAX]; 252 253 if (!(dp = opendir(dir->name))) { 254 ret = 1; 255 weprintf("opendir %s%s:", path, dir->name); 256 return; 257 } 258 if (chdir(dir->name) < 0) 259 eprintf("chdir %s:", dir->name); 260 261 while ((d = readdir(dp))) { 262 if (d->d_name[0] == '.' && !aflag && !Aflag) 263 continue; 264 else if (Aflag) 265 if (strcmp(d->d_name, ".") == 0 || 266 strcmp(d->d_name, "..") == 0) 267 continue; 268 269 ents = ereallocarray(ents, ++n, sizeof(*ents)); 270 mkent(&ents[n - 1], estrdup(d->d_name), Fflag || iflag || 271 lflag || pflag || Rflag || sort, Lflag); 272 } 273 274 closedir(dp); 275 276 if (!Uflag) 277 qsort(ents, n, sizeof(*ents), entcmp); 278 279 if (path[0] || showdirs) { 280 fputs(path, stdout); 281 printname(dir->name); 282 puts(":"); 283 } 284 for (i = 0; i < n; i++) 285 output(&ents[i]); 286 287 if (Rflag) { 288 if (snprintf(prefix, PATH_MAX, "%s%s/", path, dir->name) >= 289 PATH_MAX) 290 eprintf("path too long: %s%s\n", path, dir->name); 291 292 for (i = 0; i < n; i++) { 293 ent = &ents[i]; 294 if (strcmp(ent->name, ".") == 0 || 295 strcmp(ent->name, "..") == 0) 296 continue; 297 if (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode) && !Lflag) 298 continue; 299 300 ls(prefix, ent, 1); 301 } 302 } 303 304 for (i = 0; i < n; ++i) 305 free(ents[i].name); 306 free(ents); 307 } 308 309 static int 310 visit(const struct entry *ent) 311 { 312 dev_t dev; 313 ino_t ino; 314 int i; 315 316 dev = ent->dev; 317 ino = S_ISLNK(ent->mode) ? ent->tino : ent->ino; 318 319 for (i = 0; i < PATH_MAX && tree[i].ino; ++i) { 320 if (ino == tree[i].ino && dev == tree[i].dev) 321 return -1; 322 } 323 324 tree[i].ino = ino; 325 tree[i].dev = dev; 326 327 return i; 328 } 329 330 static void 331 ls(const char *path, const struct entry *ent, int listdir) 332 { 333 int treeind; 334 char cwd[PATH_MAX]; 335 336 if (!listdir) { 337 output(ent); 338 } else if (S_ISDIR(ent->mode) || 339 (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode))) { 340 if ((treeind = visit(ent)) < 0) { 341 ret = 1; 342 weprintf("%s%s: Already visited\n", path, ent->name); 343 return; 344 } 345 346 if (!getcwd(cwd, PATH_MAX)) 347 eprintf("getcwd:"); 348 349 if (first) 350 first = 0; 351 else 352 putchar('\n'); 353 354 lsdir(path, ent); 355 tree[treeind].ino = 0; 356 357 if (chdir(cwd) < 0) 358 eprintf("chdir %s:", cwd); 359 } 360 } 361 362 static void 363 usage(void) 364 { 365 eprintf("usage: %s [-1AacdFfHhiLlnpqRrtUu] [file ...]\n", argv0); 366 } 367 368 int 369 main(int argc, char *argv[]) 370 { 371 struct entry ent, *dents, *fents; 372 size_t i, ds, fs; 373 374 ARGBEGIN { 375 case '1': 376 /* force output to 1 entry per line */ 377 qflag = 1; 378 break; 379 case 'A': 380 Aflag = 1; 381 break; 382 case 'a': 383 aflag = 1; 384 break; 385 case 'c': 386 cflag = 1; 387 uflag = 0; 388 break; 389 case 'd': 390 dflag = 1; 391 break; 392 case 'f': 393 aflag = 1; 394 fflag = 1; 395 Uflag = 1; 396 break; 397 case 'F': 398 Fflag = 1; 399 break; 400 case 'H': 401 Hflag = 1; 402 break; 403 case 'h': 404 hflag = 1; 405 break; 406 case 'i': 407 iflag = 1; 408 break; 409 case 'L': 410 Lflag = 1; 411 break; 412 case 'l': 413 lflag = 1; 414 break; 415 case 'n': 416 lflag = 1; 417 nflag = 1; 418 break; 419 case 'p': 420 pflag = 1; 421 break; 422 case 'q': 423 qflag = 1; 424 break; 425 case 'R': 426 Rflag = 1; 427 break; 428 case 'r': 429 rflag = 1; 430 break; 431 case 'S': 432 sort = 'S'; 433 break; 434 case 't': 435 sort = 't'; 436 break; 437 case 'U': 438 Uflag = 1; 439 break; 440 case 'u': 441 uflag = 1; 442 cflag = 0; 443 break; 444 default: 445 usage(); 446 } ARGEND 447 448 switch (argc) { 449 case 0: /* fallthrough */ 450 *--argv = ".", ++argc; 451 case 1: 452 mkent(&ent, argv[0], 1, Hflag || Lflag); 453 ls("", &ent, (!dflag && S_ISDIR(ent.mode)) || 454 (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) && 455 !(dflag || Fflag || lflag))); 456 457 break; 458 default: 459 for (i = ds = fs = 0, fents = dents = NULL; i < argc; ++i) { 460 mkent(&ent, argv[i], 1, Hflag || Lflag); 461 462 if ((!dflag && S_ISDIR(ent.mode)) || 463 (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) && 464 !(dflag || Fflag || lflag))) { 465 dents = ereallocarray(dents, ++ds, sizeof(*dents)); 466 memcpy(&dents[ds - 1], &ent, sizeof(ent)); 467 } else { 468 fents = ereallocarray(fents, ++fs, sizeof(*fents)); 469 memcpy(&fents[fs - 1], &ent, sizeof(ent)); 470 } 471 } 472 473 showdirs = ds > 1 || (ds && fs); 474 475 qsort(fents, fs, sizeof(ent), entcmp); 476 qsort(dents, ds, sizeof(ent), entcmp); 477 478 for (i = 0; i < fs; ++i) 479 ls("", &fents[i], 0); 480 free(fents); 481 if (fs && ds) 482 putchar('\n'); 483 for (i = 0; i < ds; ++i) 484 ls("", &dents[i], 1); 485 free(dents); 486 } 487 488 return (fshut(stdout, "<stdout>") | ret); 489 }