aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coreutils/ls.c368
1 files changed, 210 insertions, 158 deletions
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 46abb6201..52c2c4db7 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -118,11 +118,11 @@
118enum { 118enum {
119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ 119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
120 120
121SPLIT_DIR = 1,
122SPLIT_FILE = 0, 121SPLIT_FILE = 0,
122SPLIT_DIR = 1,
123SPLIT_SUBDIR = 2, 123SPLIT_SUBDIR = 2,
124 124
125/* Bits in all_fmt: */ 125/* Bits in G.all_fmt: */
126 126
127/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ 127/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
128/* what file information will be listed */ 128/* what file information will be listed */
@@ -304,25 +304,63 @@ static const uint32_t opt_flags[] = {
304 304
305 305
306/* 306/*
307 * a directory entry and its stat info are stored here 307 * a directory entry and its stat info
308 */ 308 */
309struct dnode { 309struct dnode {
310 const char *name; /* the dir entry name */ 310 const char *name; /* usually basename, but think "ls -l dir/file" */
311 const char *fullname; /* the dir entry name */ 311 const char *fullname; /* full name (usable for stat etc) */
312 struct dnode *next; /* point at the next node */ 312 struct dnode *dn_next; /* for linked list */
313 smallint fname_allocated;
314 struct stat dstat; /* the file stat info */
315 IF_SELINUX(security_context_t sid;) 313 IF_SELINUX(security_context_t sid;)
314 smallint fname_allocated;
315
316 /* Used to avoid re-doing [l]stat at printout stage
317 * if we already collected needed data in scan stage:
318 */
319 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
320 mode_t dn_mode_stat; /* obtained with stat, or 0 */
321
322// struct stat dstat;
323// struct stat is huge. We don't need it in full.
324// At least we don't need st_dev and st_blksize,
325// but there are invisible fields as well
326// (such as nanosecond-resolution timespamps)
327// and padding, which we also don't want to store.
328// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
329// On 32-bit uclibc: dnode size went from 112 to 84 bytes
330//
331 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
332 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
333 off_t dn_size;
334#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
335 time_t dn_atime;
336 time_t dn_mtime;
337 time_t dn_ctime;
338#endif
339 ino_t dn_ino;
340 blkcnt_t dn_blocks;
341 nlink_t dn_nlink;
342 uid_t dn_uid;
343 gid_t dn_gid;
344 int dn_rdev_maj;
345 int dn_rdev_min;
346// dev_t dn_dev;
347// blksize_t dn_blksize;
316}; 348};
317 349
318struct globals { 350struct globals {
319#if ENABLE_FEATURE_LS_COLOR 351#if ENABLE_FEATURE_LS_COLOR
320 smallint show_color; 352 smallint show_color;
353# define G_show_color (G.show_color)
354#else
355# define G_show_color 0
321#endif 356#endif
322 smallint exit_code; 357 smallint exit_code;
323 unsigned all_fmt; 358 unsigned all_fmt;
324#if ENABLE_FEATURE_AUTOWIDTH 359#if ENABLE_FEATURE_AUTOWIDTH
325 unsigned terminal_width; // = TERMINAL_WIDTH; 360 unsigned terminal_width;
361# define G_terminal_width (G.terminal_width)
362#else
363# define G_terminal_width TERMINAL_WIDTH
326#endif 364#endif
327#if ENABLE_FEATURE_LS_TIMESTAMPS 365#if ENABLE_FEATURE_LS_TIMESTAMPS
328 /* Do time() just once. Saves one syscall per file for "ls -l" */ 366 /* Do time() just once. Saves one syscall per file for "ls -l" */
@@ -330,64 +368,67 @@ struct globals {
330#endif 368#endif
331} FIX_ALIASING; 369} FIX_ALIASING;
332#define G (*(struct globals*)&bb_common_bufsiz1) 370#define G (*(struct globals*)&bb_common_bufsiz1)
333#if ENABLE_FEATURE_LS_COLOR
334# define show_color (G.show_color )
335#else
336enum { show_color = 0 };
337#endif
338#define exit_code (G.exit_code )
339#define all_fmt (G.all_fmt )
340#if ENABLE_FEATURE_AUTOWIDTH
341# define terminal_width (G.terminal_width)
342#else
343enum {
344 terminal_width = TERMINAL_WIDTH,
345};
346#endif
347#define current_time_t (G.current_time_t)
348#define INIT_G() do { \ 371#define INIT_G() do { \
349 /* we have to zero it out because of NOEXEC */ \ 372 /* we have to zero it out because of NOEXEC */ \
350 memset(&G, 0, sizeof(G)); \ 373 memset(&G, 0, sizeof(G)); \
351 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \ 374 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
352 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \ 375 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
353} while (0) 376} while (0)
354 377
355 378
356static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) 379static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
357{ 380{
358 struct stat dstat; 381 struct stat statbuf;
359 struct dnode *cur; 382 struct dnode *cur;
360 IF_SELINUX(security_context_t sid = NULL;) 383
384 cur = xzalloc(sizeof(*cur));
385 cur->fullname = fullname;
386 cur->name = name;
361 387
362 if ((option_mask32 & OPT_L) || force_follow) { 388 if ((option_mask32 & OPT_L) || force_follow) {
363#if ENABLE_SELINUX 389#if ENABLE_SELINUX
364 if (is_selinux_enabled()) { 390 if (is_selinux_enabled()) {
365 getfilecon(fullname, &sid); 391 getfilecon(fullname, &cur->sid);
366 } 392 }
367#endif 393#endif
368 if (stat(fullname, &dstat)) { 394 if (stat(fullname, &statbuf)) {
369 bb_simple_perror_msg(fullname); 395 bb_simple_perror_msg(fullname);
370 exit_code = EXIT_FAILURE; 396 G.exit_code = EXIT_FAILURE;
371 return 0; 397 free(cur);
398 return NULL;
372 } 399 }
400 cur->dn_mode_stat = statbuf.st_mode;
373 } else { 401 } else {
374#if ENABLE_SELINUX 402#if ENABLE_SELINUX
375 if (is_selinux_enabled()) { 403 if (is_selinux_enabled()) {
376 lgetfilecon(fullname, &sid); 404 lgetfilecon(fullname, &cur->sid);
377 } 405 }
378#endif 406#endif
379 if (lstat(fullname, &dstat)) { 407 if (lstat(fullname, &statbuf)) {
380 bb_simple_perror_msg(fullname); 408 bb_simple_perror_msg(fullname);
381 exit_code = EXIT_FAILURE; 409 G.exit_code = EXIT_FAILURE;
382 return 0; 410 free(cur);
411 return NULL;
383 } 412 }
413 cur->dn_mode_lstat = statbuf.st_mode;
384 } 414 }
385 415
386 cur = xmalloc(sizeof(*cur)); 416 /* cur->dstat = statbuf: */
387 cur->fullname = fullname; 417 cur->dn_mode = statbuf.st_mode ;
388 cur->name = name; 418 cur->dn_size = statbuf.st_size ;
389 cur->dstat = dstat; 419#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
390 IF_SELINUX(cur->sid = sid;) 420 cur->dn_atime = statbuf.st_atime ;
421 cur->dn_mtime = statbuf.st_mtime ;
422 cur->dn_ctime = statbuf.st_ctime ;
423#endif
424 cur->dn_ino = statbuf.st_ino ;
425 cur->dn_blocks = statbuf.st_blocks;
426 cur->dn_nlink = statbuf.st_nlink ;
427 cur->dn_uid = statbuf.st_uid ;
428 cur->dn_gid = statbuf.st_gid ;
429 cur->dn_rdev_maj = major(statbuf.st_rdev);
430 cur->dn_rdev_min = minor(statbuf.st_rdev);
431
391 return cur; 432 return cur;
392} 433}
393 434
@@ -437,14 +478,14 @@ static char bold(mode_t mode)
437} 478}
438#endif 479#endif
439 480
440#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 481#if ENABLE_FEATURE_LS_FILETYPES
441static char append_char(mode_t mode) 482static char append_char(mode_t mode)
442{ 483{
443 if (!(all_fmt & LIST_FILETYPE)) 484 if (!(G.all_fmt & LIST_FILETYPE))
444 return '\0'; 485 return '\0';
445 if (S_ISDIR(mode)) 486 if (S_ISDIR(mode))
446 return '/'; 487 return '/';
447 if (!(all_fmt & LIST_CLASSIFY)) 488 if (!(G.all_fmt & LIST_CLASSIFY))
448 return '\0'; 489 return '\0';
449 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) 490 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
450 return '*'; 491 return '*';
@@ -464,12 +505,14 @@ static unsigned count_dirs(struct dnode **dn, int which)
464 const char *name; 505 const char *name;
465 506
466 all++; 507 all++;
467 if (!S_ISDIR((*dn)->dstat.st_mode)) 508 if (!S_ISDIR((*dn)->dn_mode))
468 continue; 509 continue;
510
469 name = (*dn)->name; 511 name = (*dn)->name;
470 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ 512 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
471 /* or if it's not . or .. */ 513 /* or if it's not . or .. */
472 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2])) 514 || name[0] != '.'
515 || (name[1] && (name[1] != '.' || name[2]))
473 ) { 516 ) {
474 dirs++; 517 dirs++;
475 } 518 }
@@ -524,18 +567,22 @@ static struct dnode **splitdnarray(struct dnode **dn, int which)
524 567
525 /* copy the entrys into the file or dir array */ 568 /* copy the entrys into the file or dir array */
526 for (d = 0; *dn; dn++) { 569 for (d = 0; *dn; dn++) {
527 if (S_ISDIR((*dn)->dstat.st_mode)) { 570 if (S_ISDIR((*dn)->dn_mode)) {
528 const char *name; 571 const char *name;
529 572
530 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) 573 if (which == SPLIT_FILE)
531 continue; 574 continue;
575
532 name = (*dn)->name; 576 name = (*dn)->name;
533 if ((which & SPLIT_DIR) 577 if ((which & SPLIT_DIR) /* any dir... */
534 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) 578 /* ... or not . or .. */
579 || name[0] != '.'
580 || (name[1] && (name[1] != '.' || name[2]))
535 ) { 581 ) {
536 dnp[d++] = *dn; 582 dnp[d++] = *dn;
537 } 583 }
538 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { 584 } else
585 if (which == SPLIT_FILE) {
539 dnp[d++] = *dn; 586 dnp[d++] = *dn;
540 } 587 }
541 } 588 }
@@ -547,22 +594,22 @@ static int sortcmp(const void *a, const void *b)
547{ 594{
548 struct dnode *d1 = *(struct dnode **)a; 595 struct dnode *d1 = *(struct dnode **)a;
549 struct dnode *d2 = *(struct dnode **)b; 596 struct dnode *d2 = *(struct dnode **)b;
550 unsigned sort_opts = all_fmt & SORT_MASK; 597 unsigned sort_opts = G.all_fmt & SORT_MASK;
551 off_t dif; 598 off_t dif;
552 599
553 dif = 0; /* assume SORT_NAME */ 600 dif = 0; /* assume SORT_NAME */
554 // TODO: use pre-initialized function pointer 601 // TODO: use pre-initialized function pointer
555 // instead of branch forest 602 // instead of branch forest
556 if (sort_opts == SORT_SIZE) { 603 if (sort_opts == SORT_SIZE) {
557 dif = (d2->dstat.st_size - d1->dstat.st_size); 604 dif = (d2->dn_size - d1->dn_size);
558 } else if (sort_opts == SORT_ATIME) { 605 } else if (sort_opts == SORT_ATIME) {
559 dif = (d2->dstat.st_atime - d1->dstat.st_atime); 606 dif = (d2->dn_atime - d1->dn_atime);
560 } else if (sort_opts == SORT_CTIME) { 607 } else if (sort_opts == SORT_CTIME) {
561 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime); 608 dif = (d2->dn_ctime - d1->dn_ctime);
562 } else if (sort_opts == SORT_MTIME) { 609 } else if (sort_opts == SORT_MTIME) {
563 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime); 610 dif = (d2->dn_mtime - d1->dn_mtime);
564 } else if (sort_opts == SORT_DIR) { 611 } else if (sort_opts == SORT_DIR) {
565 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode); 612 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
566 /* } else if (sort_opts == SORT_VERSION) { */ 613 /* } else if (sort_opts == SORT_VERSION) { */
567 /* } else if (sort_opts == SORT_EXT) { */ 614 /* } else if (sort_opts == SORT_EXT) { */
568 } 615 }
@@ -583,7 +630,7 @@ static int sortcmp(const void *a, const void *b)
583 } 630 }
584 } 631 }
585 632
586 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; 633 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
587} 634}
588 635
589static void dnsort(struct dnode **dn, int size) 636static void dnsort(struct dnode **dn, int size)
@@ -661,84 +708,84 @@ static NOINLINE unsigned list_single(const struct dnode *dn)
661 unsigned column = 0; 708 unsigned column = 0;
662 char *lpath; 709 char *lpath;
663#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 710#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
664 struct stat info; 711 struct stat statbuf;
665 char append; 712 char append;
666#endif 713#endif
667 714
668#if ENABLE_FEATURE_LS_FILETYPES 715#if ENABLE_FEATURE_LS_FILETYPES
669 append = append_char(dn->dstat.st_mode); 716 append = append_char(dn->dn_mode);
670#endif 717#endif
671 718
672 /* Do readlink early, so that if it fails, error message 719 /* Do readlink early, so that if it fails, error message
673 * does not appear *inside* the "ls -l" line */ 720 * does not appear *inside* the "ls -l" line */
674 lpath = NULL; 721 lpath = NULL;
675 if (all_fmt & LIST_SYMLINK) 722 if (G.all_fmt & LIST_SYMLINK)
676 if (S_ISLNK(dn->dstat.st_mode)) 723 if (S_ISLNK(dn->dn_mode))
677 lpath = xmalloc_readlink_or_warn(dn->fullname); 724 lpath = xmalloc_readlink_or_warn(dn->fullname);
678 725
679 if (all_fmt & LIST_INO) 726 if (G.all_fmt & LIST_INO)
680 column += printf("%7llu ", (long long) dn->dstat.st_ino); 727 column += printf("%7llu ", (long long) dn->dn_ino);
681//TODO: -h should affect -s too: 728//TODO: -h should affect -s too:
682 if (all_fmt & LIST_BLOCKS) 729 if (G.all_fmt & LIST_BLOCKS)
683 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1)); 730 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
684 if (all_fmt & LIST_MODEBITS) 731 if (G.all_fmt & LIST_MODEBITS)
685 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); 732 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
686 if (all_fmt & LIST_NLINKS) 733 if (G.all_fmt & LIST_NLINKS)
687 column += printf("%4lu ", (long) dn->dstat.st_nlink); 734 column += printf("%4lu ", (long) dn->dn_nlink);
688 if (all_fmt & LIST_ID_NUMERIC) { 735 if (G.all_fmt & LIST_ID_NUMERIC) {
689 if (option_mask32 & OPT_g) 736 if (option_mask32 & OPT_g)
690 column += printf("%-8u ", (int) dn->dstat.st_gid); 737 column += printf("%-8u ", (int) dn->dn_gid);
691 else 738 else
692 column += printf("%-8u %-8u ", 739 column += printf("%-8u %-8u ",
693 (int) dn->dstat.st_uid, 740 (int) dn->dn_uid,
694 (int) dn->dstat.st_gid); 741 (int) dn->dn_gid);
695 } 742 }
696#if ENABLE_FEATURE_LS_USERNAME 743#if ENABLE_FEATURE_LS_USERNAME
697 else if (all_fmt & LIST_ID_NAME) { 744 else if (G.all_fmt & LIST_ID_NAME) {
698 if (option_mask32 & OPT_g) { 745 if (option_mask32 & OPT_g) {
699 column += printf("%-8.8s ", 746 column += printf("%-8.8s ",
700 get_cached_groupname(dn->dstat.st_gid)); 747 get_cached_groupname(dn->dn_gid));
701 } else { 748 } else {
702 column += printf("%-8.8s %-8.8s ", 749 column += printf("%-8.8s %-8.8s ",
703 get_cached_username(dn->dstat.st_uid), 750 get_cached_username(dn->dn_uid),
704 get_cached_groupname(dn->dstat.st_gid)); 751 get_cached_groupname(dn->dn_gid));
705 } 752 }
706 } 753 }
707#endif 754#endif
708 if (all_fmt & LIST_SIZE) { 755 if (G.all_fmt & LIST_SIZE) {
709 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { 756 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
710 column += printf("%4u, %3u ", 757 column += printf("%4u, %3u ",
711 (int) major(dn->dstat.st_rdev), 758 dn->dn_rdev_maj,
712 (int) minor(dn->dstat.st_rdev)); 759 dn->dn_rdev_min);
713 } else { 760 } else {
714 if (option_mask32 & OPT_h) { 761 if (option_mask32 & OPT_h) {
715 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", 762 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
716 /* print st_size, show one fractional, use suffixes */ 763 /* print size, show one fractional, use suffixes */
717 make_human_readable_str(dn->dstat.st_size, 1, 0) 764 make_human_readable_str(dn->dn_size, 1, 0)
718 ); 765 );
719 } else { 766 } else {
720 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); 767 column += printf("%9"OFF_FMT"u ", dn->dn_size);
721 } 768 }
722 } 769 }
723 } 770 }
724#if ENABLE_FEATURE_LS_TIMESTAMPS 771#if ENABLE_FEATURE_LS_TIMESTAMPS
725 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { 772 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
726 char *filetime; 773 char *filetime;
727 time_t ttime = dn->dstat.st_mtime; 774 time_t ttime = dn->dn_mtime;
728 if (all_fmt & TIME_ACCESS) 775 if (G.all_fmt & TIME_ACCESS)
729 ttime = dn->dstat.st_atime; 776 ttime = dn->dn_atime;
730 if (all_fmt & TIME_CHANGE) 777 if (G.all_fmt & TIME_CHANGE)
731 ttime = dn->dstat.st_ctime; 778 ttime = dn->dn_ctime;
732 filetime = ctime(&ttime); 779 filetime = ctime(&ttime);
733 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ 780 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
734 if (all_fmt & LIST_FULLTIME) { /* -e */ 781 if (G.all_fmt & LIST_FULLTIME) { /* -e */
735 /* Note: coreutils 8.4 ls --full-time prints: 782 /* Note: coreutils 8.4 ls --full-time prints:
736 * 2009-07-13 17:49:27.000000000 +0200 783 * 2009-07-13 17:49:27.000000000 +0200
737 */ 784 */
738 column += printf("%.24s ", filetime); 785 column += printf("%.24s ", filetime);
739 } else { /* LIST_DATE_TIME */ 786 } else { /* LIST_DATE_TIME */
740 /* current_time_t ~== time(NULL) */ 787 /* G.current_time_t ~== time(NULL) */
741 time_t age = current_time_t - ttime; 788 time_t age = G.current_time_t - ttime;
742 printf("%.6s ", filetime + 4); /* "Jun 30" */ 789 printf("%.6s ", filetime + 4); /* "Jun 30" */
743 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { 790 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
744 /* hh:mm if less than 6 months old */ 791 /* hh:mm if less than 6 months old */
@@ -751,48 +798,52 @@ static NOINLINE unsigned list_single(const struct dnode *dn)
751 } 798 }
752#endif 799#endif
753#if ENABLE_SELINUX 800#if ENABLE_SELINUX
754 if (all_fmt & LIST_CONTEXT) { 801 if (G.all_fmt & LIST_CONTEXT) {
755 column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); 802 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
756 freecon(dn->sid); 803 freecon(dn->sid);
757 } 804 }
758#endif 805#endif
759 806
760#if ENABLE_FEATURE_LS_COLOR 807#if ENABLE_FEATURE_LS_COLOR
761 if (show_color) { 808 if (G_show_color) {
762 info.st_mode = 0; 809 mode_t mode = dn->dn_mode_lstat;
763 lstat(dn->fullname, &info); 810 if (!mode)
764 printf("\033[%u;%um", bold(info.st_mode), 811 if (lstat(dn->fullname, &statbuf) == 0)
765 fgcolor(info.st_mode)); 812 mode = statbuf.st_mode;
813 printf("\033[%u;%um", bold(mode), fgcolor(mode));
766 } 814 }
767#endif 815#endif
768 column += print_name(dn->name); 816 column += print_name(dn->name);
769 if (show_color) { 817 if (G_show_color) {
770 printf("\033[0m"); 818 printf("\033[0m");
771 } 819 }
772 820
773 if (lpath) { 821 if (lpath) {
774 printf(" -> "); 822 printf(" -> ");
775#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 823#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
776 info.st_mode = 0; 824 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
777 stat(dn->fullname, &info); 825 mode_t mode = dn->dn_mode_stat;
826 if (!mode)
827 if (stat(dn->fullname, &statbuf) == 0)
828 mode = statbuf.st_mode;
778# if ENABLE_FEATURE_LS_FILETYPES 829# if ENABLE_FEATURE_LS_FILETYPES
779 append = append_char(info.st_mode); 830 append = append_char(mode);
831# endif
832# if ENABLE_FEATURE_LS_COLOR
833 if (G_show_color) {
834 printf("\033[%u;%um", bold(mode), fgcolor(mode));
835 }
780# endif 836# endif
781#endif
782#if ENABLE_FEATURE_LS_COLOR
783 if (show_color) {
784 printf("\033[%u;%um", bold(info.st_mode),
785 fgcolor(info.st_mode));
786 } 837 }
787#endif 838#endif
788 column += print_name(lpath) + 4; 839 column += print_name(lpath) + 4;
789 if (show_color) { 840 free(lpath);
841 if (G_show_color) {
790 printf("\033[0m"); 842 printf("\033[0m");
791 } 843 }
792 free(lpath);
793 } 844 }
794#if ENABLE_FEATURE_LS_FILETYPES 845#if ENABLE_FEATURE_LS_FILETYPES
795 if (all_fmt & LIST_FILETYPE) { 846 if (G.all_fmt & LIST_FILETYPE) {
796 if (append) { 847 if (append) {
797 putchar(append); 848 putchar(append);
798 column++; 849 column++;
@@ -810,7 +861,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
810 unsigned nexttab; 861 unsigned nexttab;
811 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ 862 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
812 863
813 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ 864 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
814 ncols = 1; 865 ncols = 1;
815 } else { 866 } else {
816 /* find the longest file name, use that as the column width */ 867 /* find the longest file name, use that as the column width */
@@ -820,10 +871,10 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
820 column_width = len; 871 column_width = len;
821 } 872 }
822 column_width += 1 + 873 column_width += 1 +
823 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) 874 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
824 ((all_fmt & LIST_INO) ? 8 : 0) + 875 ((G.all_fmt & LIST_INO) ? 8 : 0) +
825 ((all_fmt & LIST_BLOCKS) ? 5 : 0); 876 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
826 ncols = (int) (terminal_width / column_width); 877 ncols = (unsigned)G_terminal_width / column_width;
827 } 878 }
828 879
829 if (ncols > 1) { 880 if (ncols > 1) {
@@ -840,7 +891,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
840 for (row = 0; row < nrows; row++) { 891 for (row = 0; row < nrows; row++) {
841 for (nc = 0; nc < ncols; nc++) { 892 for (nc = 0; nc < ncols; nc++) {
842 /* reach into the array based on the column and row */ 893 /* reach into the array based on the column and row */
843 if (all_fmt & DISP_ROWS) 894 if (G.all_fmt & DISP_ROWS)
844 i = (row * ncols) + nc; /* display across row */ 895 i = (row * ncols) + nc; /* display across row */
845 else 896 else
846 i = (nc * nrows) + row; /* display by column */ 897 i = (nc * nrows) + row; /* display by column */
@@ -877,7 +928,7 @@ static off_t calculate_blocks(struct dnode **dn)
877 if (dn) { 928 if (dn) {
878 while (*dn) { 929 while (*dn) {
879 /* st_blocks is in 512 byte blocks */ 930 /* st_blocks is in 512 byte blocks */
880 blocks += (*dn)->dstat.st_blocks; 931 blocks += (*dn)->dn_blocks;
881 dn++; 932 dn++;
882 } 933 }
883 } 934 }
@@ -901,7 +952,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
901 *nfiles_p = 0; 952 *nfiles_p = 0;
902 dir = warn_opendir(path); 953 dir = warn_opendir(path);
903 if (dir == NULL) { 954 if (dir == NULL) {
904 exit_code = EXIT_FAILURE; 955 G.exit_code = EXIT_FAILURE;
905 return NULL; /* could not open the dir */ 956 return NULL; /* could not open the dir */
906 } 957 }
907 dn = NULL; 958 dn = NULL;
@@ -912,11 +963,11 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
912 /* are we going to list the file- it may be . or .. or a hidden file */ 963 /* are we going to list the file- it may be . or .. or a hidden file */
913 if (entry->d_name[0] == '.') { 964 if (entry->d_name[0] == '.') {
914 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) 965 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
915 && !(all_fmt & DISP_DOT) 966 && !(G.all_fmt & DISP_DOT)
916 ) { 967 ) {
917 continue; 968 continue;
918 } 969 }
919 if (!(all_fmt & DISP_HIDDEN)) 970 if (!(G.all_fmt & DISP_HIDDEN))
920 continue; 971 continue;
921 } 972 }
922 fullname = concat_path_file(path, entry->d_name); 973 fullname = concat_path_file(path, entry->d_name);
@@ -926,7 +977,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
926 continue; 977 continue;
927 } 978 }
928 cur->fname_allocated = 1; 979 cur->fname_allocated = 1;
929 cur->next = dn; 980 cur->dn_next = dn;
930 dn = cur; 981 dn = cur;
931 nfiles++; 982 nfiles++;
932 } 983 }
@@ -942,7 +993,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
942 dnp = dnalloc(nfiles); 993 dnp = dnalloc(nfiles);
943 for (i = 0; /* i < nfiles - detected via !dn below */; i++) { 994 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
944 dnp[i] = dn; /* save pointer to node in array */ 995 dnp[i] = dn; /* save pointer to node in array */
945 dn = dn->next; 996 dn = dn->dn_next;
946 if (!dn) 997 if (!dn)
947 break; 998 break;
948 } 999 }
@@ -957,7 +1008,7 @@ static void showdirs(struct dnode **dn, int first)
957 struct dnode **subdnp; 1008 struct dnode **subdnp;
958 1009
959 for (; *dn; dn++) { 1010 for (; *dn; dn++) {
960 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { 1011 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
961 if (!first) 1012 if (!first)
962 bb_putchar('\n'); 1013 bb_putchar('\n');
963 first = 0; 1014 first = 0;
@@ -965,7 +1016,7 @@ static void showdirs(struct dnode **dn, int first)
965 } 1016 }
966 subdnp = list_dir((*dn)->fullname, &nfiles); 1017 subdnp = list_dir((*dn)->fullname, &nfiles);
967#if ENABLE_DESKTOP 1018#if ENABLE_DESKTOP
968 if ((all_fmt & STYLE_MASK) == STYLE_LONG) 1019 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
969 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); 1020 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
970#endif 1021#endif
971 if (nfiles > 0) { 1022 if (nfiles > 0) {
@@ -973,7 +1024,7 @@ static void showdirs(struct dnode **dn, int first)
973 dnsort(subdnp, nfiles); 1024 dnsort(subdnp, nfiles);
974 showfiles(subdnp, nfiles); 1025 showfiles(subdnp, nfiles);
975 if (ENABLE_FEATURE_LS_RECURSIVE 1026 if (ENABLE_FEATURE_LS_RECURSIVE
976 && (all_fmt & DISP_RECURSIVE) 1027 && (G.all_fmt & DISP_RECURSIVE)
977 ) { 1028 ) {
978 struct dnode **dnd; 1029 struct dnode **dnd;
979 unsigned dndirs; 1030 unsigned dndirs;
@@ -1031,13 +1082,13 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1031 init_unicode(); 1082 init_unicode();
1032 1083
1033 if (ENABLE_FEATURE_LS_SORTFILES) 1084 if (ENABLE_FEATURE_LS_SORTFILES)
1034 all_fmt = SORT_NAME; 1085 G.all_fmt = SORT_NAME;
1035 1086
1036#if ENABLE_FEATURE_AUTOWIDTH 1087#if ENABLE_FEATURE_AUTOWIDTH
1037 /* obtain the terminal width */ 1088 /* obtain the terminal width */
1038 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL); 1089 get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
1039 /* go one less... */ 1090 /* go one less... */
1040 terminal_width--; 1091 G_terminal_width--;
1041#endif 1092#endif
1042 1093
1043 /* process options */ 1094 /* process options */
@@ -1058,7 +1109,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1058 /* -w NUM: */ 1109 /* -w NUM: */
1059 IF_FEATURE_AUTOWIDTH(":w+"); 1110 IF_FEATURE_AUTOWIDTH(":w+");
1060 opt = getopt32(argv, ls_options 1111 opt = getopt32(argv, ls_options
1061 IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width) 1112 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1062 IF_FEATURE_LS_COLOR(, &color_opt) 1113 IF_FEATURE_LS_COLOR(, &color_opt)
1063 ); 1114 );
1064 for (i = 0; opt_flags[i] != (1U << 31); i++) { 1115 for (i = 0; opt_flags[i] != (1U << 31); i++) {
@@ -1066,27 +1117,27 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1066 uint32_t flags = opt_flags[i]; 1117 uint32_t flags = opt_flags[i];
1067 1118
1068 if (flags & STYLE_MASK) 1119 if (flags & STYLE_MASK)
1069 all_fmt &= ~STYLE_MASK; 1120 G.all_fmt &= ~STYLE_MASK;
1070 if (flags & SORT_MASK) 1121 if (flags & SORT_MASK)
1071 all_fmt &= ~SORT_MASK; 1122 G.all_fmt &= ~SORT_MASK;
1072 if (flags & TIME_MASK) 1123 if (flags & TIME_MASK)
1073 all_fmt &= ~TIME_MASK; 1124 G.all_fmt &= ~TIME_MASK;
1074 1125
1075 all_fmt |= flags; 1126 G.all_fmt |= flags;
1076 } 1127 }
1077 } 1128 }
1078 1129
1079#if ENABLE_FEATURE_LS_COLOR 1130#if ENABLE_FEATURE_LS_COLOR
1080 /* set show_color = 1/0 */ 1131 /* set G_show_color = 1/0 */
1081 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { 1132 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1082 char *p = getenv("LS_COLORS"); 1133 char *p = getenv("LS_COLORS");
1083 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1134 /* LS_COLORS is unset, or (not empty && not "none") ? */
1084 if (!p || (p[0] && strcmp(p, "none") != 0)) 1135 if (!p || (p[0] && strcmp(p, "none") != 0))
1085 show_color = 1; 1136 G_show_color = 1;
1086 } 1137 }
1087 if (opt & OPT_color) { 1138 if (opt & OPT_color) {
1088 if (color_opt[0] == 'n') 1139 if (color_opt[0] == 'n')
1089 show_color = 0; 1140 G_show_color = 0;
1090 else switch (index_in_substrings(color_str, color_opt)) { 1141 else switch (index_in_substrings(color_str, color_opt)) {
1091 case 3: 1142 case 3:
1092 case 4: 1143 case 4:
@@ -1095,34 +1146,34 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1095 case 0: 1146 case 0:
1096 case 1: 1147 case 1:
1097 case 2: 1148 case 2:
1098 show_color = 1; 1149 G_show_color = 1;
1099 } 1150 }
1100 } 1151 }
1101 } 1152 }
1102#endif 1153#endif
1103 1154
1104 /* sort out which command line options take precedence */ 1155 /* sort out which command line options take precedence */
1105 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST)) 1156 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1106 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ 1157 G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
1107 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1158 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1108 if (all_fmt & TIME_CHANGE) 1159 if (G.all_fmt & TIME_CHANGE)
1109 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; 1160 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1110 if (all_fmt & TIME_ACCESS) 1161 if (G.all_fmt & TIME_ACCESS)
1111 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; 1162 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1112 } 1163 }
1113 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ 1164 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1114 all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); 1165 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1115 1166
1116 /* choose a display format if one was not already specified by an option */ 1167 /* choose a display format if one was not already specified by an option */
1117 if (!(all_fmt & STYLE_MASK)) 1168 if (!(G.all_fmt & STYLE_MASK))
1118 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); 1169 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1119 1170
1120 argv += optind; 1171 argv += optind;
1121 if (!argv[0]) 1172 if (!argv[0])
1122 *--argv = (char*)"."; 1173 *--argv = (char*)".";
1123 1174
1124 if (argv[1]) 1175 if (argv[1])
1125 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ 1176 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1126 1177
1127 /* stuff the command line file names into a dnode array */ 1178 /* stuff the command line file names into a dnode array */
1128 dn = NULL; 1179 dn = NULL;
@@ -1130,8 +1181,8 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1130 do { 1181 do {
1131 cur = my_stat(*argv, *argv, 1182 cur = my_stat(*argv, *argv,
1132 /* follow links on command line unless -l, -s or -F: */ 1183 /* follow links on command line unless -l, -s or -F: */
1133 !((all_fmt & STYLE_MASK) == STYLE_LONG 1184 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1134 || (all_fmt & LIST_BLOCKS) 1185 || (G.all_fmt & LIST_BLOCKS)
1135 || (option_mask32 & OPT_F) 1186 || (option_mask32 & OPT_F)
1136 ) 1187 )
1137 /* ... or if -H: */ 1188 /* ... or if -H: */
@@ -1141,15 +1192,15 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1141 argv++; 1192 argv++;
1142 if (!cur) 1193 if (!cur)
1143 continue; 1194 continue;
1144 cur->fname_allocated = 0; 1195 /*cur->fname_allocated = 0; - already is */
1145 cur->next = dn; 1196 cur->dn_next = dn;
1146 dn = cur; 1197 dn = cur;
1147 nfiles++; 1198 nfiles++;
1148 } while (*argv); 1199 } while (*argv);
1149 1200
1150 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ 1201 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1151 if (nfiles == 0) 1202 if (nfiles == 0)
1152 return exit_code; 1203 return G.exit_code;
1153 1204
1154 /* now that we know how many files there are 1205 /* now that we know how many files there are
1155 * allocate memory for an array to hold dnode pointers 1206 * allocate memory for an array to hold dnode pointers
@@ -1157,12 +1208,12 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1157 dnp = dnalloc(nfiles); 1208 dnp = dnalloc(nfiles);
1158 for (i = 0; /* i < nfiles - detected via !dn below */; i++) { 1209 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1159 dnp[i] = dn; /* save pointer to node in array */ 1210 dnp[i] = dn; /* save pointer to node in array */
1160 dn = dn->next; 1211 dn = dn->dn_next;
1161 if (!dn) 1212 if (!dn)
1162 break; 1213 break;
1163 } 1214 }
1164 1215
1165 if (all_fmt & DISP_NOLIST) { 1216 if (G.all_fmt & DISP_NOLIST) {
1166 dnsort(dnp, nfiles); 1217 dnsort(dnp, nfiles);
1167 showfiles(dnp, nfiles); 1218 showfiles(dnp, nfiles);
1168 } else { 1219 } else {
@@ -1183,7 +1234,8 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1183 free(dnd); 1234 free(dnd);
1184 } 1235 }
1185 } 1236 }
1237
1186 if (ENABLE_FEATURE_CLEAN_UP) 1238 if (ENABLE_FEATURE_CLEAN_UP)
1187 dfree(dnp); 1239 dfree(dnp);
1188 return exit_code; 1240 return G.exit_code;
1189} 1241}