diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-01-31 05:15:38 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-01-31 05:15:38 +0100 |
commit | d8528b8e56bab7643722e4453121882d23c23c07 (patch) | |
tree | c742df066326cd571327b10d4cca3341c798d129 /coreutils | |
parent | ed910c750d7908a31262488e04d38b7bf3d75322 (diff) | |
download | busybox-w32-d8528b8e56bab7643722e4453121882d23c23c07.tar.gz busybox-w32-d8528b8e56bab7643722e4453121882d23c23c07.tar.bz2 busybox-w32-d8528b8e56bab7643722e4453121882d23c23c07.zip |
ls: unicode fixes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'coreutils')
-rw-r--r-- | coreutils/ls.c | 412 |
1 files changed, 217 insertions, 195 deletions
diff --git a/coreutils/ls.c b/coreutils/ls.c index 6c898b793..d004ce8b1 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c | |||
@@ -241,9 +241,6 @@ struct dnode { | |||
241 | IF_SELINUX(security_context_t sid;) | 241 | IF_SELINUX(security_context_t sid;) |
242 | }; | 242 | }; |
243 | 243 | ||
244 | static struct dnode **list_dir(const char *, unsigned *); | ||
245 | static unsigned list_single(const struct dnode *); | ||
246 | |||
247 | struct globals { | 244 | struct globals { |
248 | #if ENABLE_FEATURE_LS_COLOR | 245 | #if ENABLE_FEATURE_LS_COLOR |
249 | smallint show_color; | 246 | smallint show_color; |
@@ -528,31 +525,236 @@ static void dnsort(struct dnode **dn, int size) | |||
528 | #endif | 525 | #endif |
529 | 526 | ||
530 | 527 | ||
531 | static void showfiles(struct dnode **dn, unsigned nfiles) | 528 | static unsigned calc_name_len(const char *name) |
529 | { | ||
530 | unsigned len; | ||
531 | uni_stat_t uni_stat; | ||
532 | |||
533 | // TODO: quote tab as \t, etc, if -Q | ||
534 | name = printable_string(&uni_stat, name); | ||
535 | |||
536 | if (!(option_mask32 & OPT_Q)) { | ||
537 | return uni_stat.unicode_width; | ||
538 | } | ||
539 | |||
540 | len = 2 + uni_stat.unicode_width; | ||
541 | while (*name) { | ||
542 | if (*name == '"' || *name == '\\') { | ||
543 | len++; | ||
544 | } | ||
545 | name++; | ||
546 | } | ||
547 | return len; | ||
548 | } | ||
549 | |||
550 | |||
551 | /* Return the number of used columns. | ||
552 | * Note that only STYLE_COLUMNS uses return value. | ||
553 | * STYLE_SINGLE and STYLE_LONG don't care. | ||
554 | * coreutils 7.2 also supports: | ||
555 | * ls -b (--escape) = octal escapes (although it doesn't look like working) | ||
556 | * ls -N (--literal) = not escape at all | ||
557 | */ | ||
558 | static unsigned print_name(const char *name) | ||
559 | { | ||
560 | unsigned len; | ||
561 | uni_stat_t uni_stat; | ||
562 | |||
563 | // TODO: quote tab as \t, etc, if -Q | ||
564 | name = printable_string(&uni_stat, name); | ||
565 | |||
566 | if (!(option_mask32 & OPT_Q)) { | ||
567 | fputs(name, stdout); | ||
568 | return uni_stat.unicode_width; | ||
569 | } | ||
570 | |||
571 | len = 2 + uni_stat.unicode_width; | ||
572 | putchar('"'); | ||
573 | while (*name) { | ||
574 | if (*name == '"' || *name == '\\') { | ||
575 | putchar('\\'); | ||
576 | len++; | ||
577 | } | ||
578 | putchar(*name++); | ||
579 | } | ||
580 | putchar('"'); | ||
581 | return len; | ||
582 | } | ||
583 | |||
584 | /* Return the number of used columns. | ||
585 | * Note that only STYLE_COLUMNS uses return value, | ||
586 | * STYLE_SINGLE and STYLE_LONG don't care. | ||
587 | */ | ||
588 | static NOINLINE unsigned list_single(const struct dnode *dn) | ||
532 | { | 589 | { |
533 | unsigned i, ncols, nrows, row, nc; | ||
534 | unsigned column = 0; | 590 | unsigned column = 0; |
535 | unsigned nexttab = 0; | 591 | char *lpath = lpath; /* for compiler */ |
536 | unsigned column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */ | 592 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR |
593 | struct stat info; | ||
594 | char append; | ||
595 | #endif | ||
537 | 596 | ||
538 | /* Never happens: | 597 | /* Never happens: |
539 | if (dn == NULL || nfiles < 1) | 598 | if (dn->fullname == NULL) |
540 | return; | 599 | return 0; |
541 | */ | 600 | */ |
542 | 601 | ||
543 | if (all_fmt & STYLE_LONG) { | 602 | #if ENABLE_FEATURE_LS_FILETYPES |
603 | append = append_char(dn->dstat.st_mode); | ||
604 | #endif | ||
605 | |||
606 | /* Do readlink early, so that if it fails, error message | ||
607 | * does not appear *inside* the "ls -l" line */ | ||
608 | if (all_fmt & LIST_SYMLINK) | ||
609 | if (S_ISLNK(dn->dstat.st_mode)) | ||
610 | lpath = xmalloc_readlink_or_warn(dn->fullname); | ||
611 | |||
612 | if (all_fmt & LIST_INO) | ||
613 | column += printf("%7llu ", (long long) dn->dstat.st_ino); | ||
614 | if (all_fmt & LIST_BLOCKS) | ||
615 | column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1)); | ||
616 | if (all_fmt & LIST_MODEBITS) | ||
617 | column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); | ||
618 | if (all_fmt & LIST_NLINKS) | ||
619 | column += printf("%4lu ", (long) dn->dstat.st_nlink); | ||
620 | #if ENABLE_FEATURE_LS_USERNAME | ||
621 | if (all_fmt & LIST_ID_NAME) { | ||
622 | if (option_mask32 & OPT_g) { | ||
623 | column += printf("%-8.8s ", | ||
624 | get_cached_username(dn->dstat.st_uid)); | ||
625 | } else { | ||
626 | column += printf("%-8.8s %-8.8s ", | ||
627 | get_cached_username(dn->dstat.st_uid), | ||
628 | get_cached_groupname(dn->dstat.st_gid)); | ||
629 | } | ||
630 | } | ||
631 | #endif | ||
632 | if (all_fmt & LIST_ID_NUMERIC) { | ||
633 | if (option_mask32 & OPT_g) | ||
634 | column += printf("%-8u ", (int) dn->dstat.st_uid); | ||
635 | else | ||
636 | column += printf("%-8u %-8u ", | ||
637 | (int) dn->dstat.st_uid, | ||
638 | (int) dn->dstat.st_gid); | ||
639 | } | ||
640 | if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) { | ||
641 | if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { | ||
642 | column += printf("%4u, %3u ", | ||
643 | (int) major(dn->dstat.st_rdev), | ||
644 | (int) minor(dn->dstat.st_rdev)); | ||
645 | } else { | ||
646 | if (all_fmt & LS_DISP_HR) { | ||
647 | column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", | ||
648 | /* print st_size, show one fractional, use suffixes */ | ||
649 | make_human_readable_str(dn->dstat.st_size, 1, 0) | ||
650 | ); | ||
651 | } else { | ||
652 | column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); | ||
653 | } | ||
654 | } | ||
655 | } | ||
656 | #if ENABLE_FEATURE_LS_TIMESTAMPS | ||
657 | if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { | ||
658 | char *filetime; | ||
659 | time_t ttime = dn->dstat.st_mtime; | ||
660 | if (all_fmt & TIME_ACCESS) | ||
661 | ttime = dn->dstat.st_atime; | ||
662 | if (all_fmt & TIME_CHANGE) | ||
663 | ttime = dn->dstat.st_ctime; | ||
664 | filetime = ctime(&ttime); | ||
665 | /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ | ||
666 | if (all_fmt & LIST_FULLTIME) | ||
667 | column += printf("%.24s ", filetime); | ||
668 | else { /* LIST_DATE_TIME */ | ||
669 | /* current_time_t ~== time(NULL) */ | ||
670 | time_t age = current_time_t - ttime; | ||
671 | printf("%.6s ", filetime + 4); /* "Jun 30" */ | ||
672 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { | ||
673 | /* hh:mm if less than 6 months old */ | ||
674 | printf("%.5s ", filetime + 11); | ||
675 | } else { /* year. buggy if year > 9999 ;) */ | ||
676 | printf(" %.4s ", filetime + 20); | ||
677 | } | ||
678 | column += 13; | ||
679 | } | ||
680 | } | ||
681 | #endif | ||
682 | #if ENABLE_SELINUX | ||
683 | if (all_fmt & LIST_CONTEXT) { | ||
684 | column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); | ||
685 | freecon(dn->sid); | ||
686 | } | ||
687 | #endif | ||
688 | if (all_fmt & LIST_FILENAME) { | ||
689 | #if ENABLE_FEATURE_LS_COLOR | ||
690 | if (show_color) { | ||
691 | info.st_mode = 0; /* for fgcolor() */ | ||
692 | lstat(dn->fullname, &info); | ||
693 | printf("\033[%u;%um", bold(info.st_mode), | ||
694 | fgcolor(info.st_mode)); | ||
695 | } | ||
696 | #endif | ||
697 | column += print_name(dn->name); | ||
698 | if (show_color) { | ||
699 | printf("\033[0m"); | ||
700 | } | ||
701 | } | ||
702 | if (all_fmt & LIST_SYMLINK) { | ||
703 | if (S_ISLNK(dn->dstat.st_mode) && lpath) { | ||
704 | printf(" -> "); | ||
705 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | ||
706 | #if ENABLE_FEATURE_LS_COLOR | ||
707 | info.st_mode = 0; /* for fgcolor() */ | ||
708 | #endif | ||
709 | if (stat(dn->fullname, &info) == 0) { | ||
710 | append = append_char(info.st_mode); | ||
711 | } | ||
712 | #endif | ||
713 | #if ENABLE_FEATURE_LS_COLOR | ||
714 | if (show_color) { | ||
715 | printf("\033[%u;%um", bold(info.st_mode), | ||
716 | fgcolor(info.st_mode)); | ||
717 | } | ||
718 | #endif | ||
719 | column += print_name(lpath) + 4; | ||
720 | if (show_color) { | ||
721 | printf("\033[0m"); | ||
722 | } | ||
723 | free(lpath); | ||
724 | } | ||
725 | } | ||
726 | #if ENABLE_FEATURE_LS_FILETYPES | ||
727 | if (all_fmt & LIST_FILETYPE) { | ||
728 | if (append) { | ||
729 | putchar(append); | ||
730 | column++; | ||
731 | } | ||
732 | } | ||
733 | #endif | ||
734 | |||
735 | return column; | ||
736 | } | ||
737 | |||
738 | static void showfiles(struct dnode **dn, unsigned nfiles) | ||
739 | { | ||
740 | unsigned i, ncols, nrows, row, nc; | ||
741 | unsigned column = 0; | ||
742 | unsigned nexttab = 0; | ||
743 | unsigned column_width = 0; /* used only by STYLE_COLUMNS */ | ||
744 | |||
745 | if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ | ||
544 | ncols = 1; | 746 | ncols = 1; |
545 | } else { | 747 | } else { |
546 | /* find the longest file name, use that as the column width */ | 748 | /* find the longest file name, use that as the column width */ |
547 | for (i = 0; dn[i]; i++) { | 749 | for (i = 0; dn[i]; i++) { |
548 | int len = unicode_strlen(dn[i]->name); | 750 | int len = calc_name_len(dn[i]->name); |
549 | if (column_width < len) | 751 | if (column_width < len) |
550 | column_width = len; | 752 | column_width = len; |
551 | } | 753 | } |
552 | column_width += tabstops + | 754 | column_width += tabstops + |
553 | IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) | 755 | IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) |
554 | ((all_fmt & LIST_INO) ? 8 : 0) + | 756 | ((all_fmt & LIST_INO) ? 8 : 0) + |
555 | ((all_fmt & LIST_BLOCKS) ? 5 : 0); | 757 | ((all_fmt & LIST_BLOCKS) ? 5 : 0); |
556 | ncols = (int) (terminal_width / column_width); | 758 | ncols = (int) (terminal_width / column_width); |
557 | } | 759 | } |
558 | 760 | ||
@@ -618,6 +820,8 @@ static off_t calculate_blocks(struct dnode **dn) | |||
618 | #endif | 820 | #endif |
619 | 821 | ||
620 | 822 | ||
823 | static struct dnode **list_dir(const char *, unsigned *); | ||
824 | |||
621 | static void showdirs(struct dnode **dn, int first) | 825 | static void showdirs(struct dnode **dn, int first) |
622 | { | 826 | { |
623 | unsigned nfiles; | 827 | unsigned nfiles; |
@@ -733,188 +937,6 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
733 | } | 937 | } |
734 | 938 | ||
735 | 939 | ||
736 | static int print_name(const char *name) | ||
737 | { | ||
738 | if (option_mask32 & OPT_Q) { | ||
739 | #if ENABLE_FEATURE_ASSUME_UNICODE | ||
740 | unsigned len = 2 + unicode_strlen(name); | ||
741 | #else | ||
742 | unsigned len = 2; | ||
743 | #endif | ||
744 | putchar('"'); | ||
745 | while (*name) { | ||
746 | if (*name == '"') { | ||
747 | putchar('\\'); | ||
748 | len++; | ||
749 | } | ||
750 | putchar(*name++); | ||
751 | if (!ENABLE_FEATURE_ASSUME_UNICODE) | ||
752 | len++; | ||
753 | } | ||
754 | putchar('"'); | ||
755 | return len; | ||
756 | } | ||
757 | /* No -Q: */ | ||
758 | #if ENABLE_FEATURE_ASSUME_UNICODE | ||
759 | fputs(name, stdout); | ||
760 | return unicode_strlen(name); | ||
761 | #else | ||
762 | return printf("%s", name); | ||
763 | #endif | ||
764 | } | ||
765 | |||
766 | |||
767 | static NOINLINE unsigned list_single(const struct dnode *dn) | ||
768 | { | ||
769 | unsigned column = 0; | ||
770 | char *lpath = lpath; /* for compiler */ | ||
771 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | ||
772 | struct stat info; | ||
773 | char append; | ||
774 | #endif | ||
775 | |||
776 | /* Never happens: | ||
777 | if (dn->fullname == NULL) | ||
778 | return 0; | ||
779 | */ | ||
780 | |||
781 | #if ENABLE_FEATURE_LS_FILETYPES | ||
782 | append = append_char(dn->dstat.st_mode); | ||
783 | #endif | ||
784 | |||
785 | /* Do readlink early, so that if it fails, error message | ||
786 | * does not appear *inside* the "ls -l" line */ | ||
787 | if (all_fmt & LIST_SYMLINK) | ||
788 | if (S_ISLNK(dn->dstat.st_mode)) | ||
789 | lpath = xmalloc_readlink_or_warn(dn->fullname); | ||
790 | |||
791 | if (all_fmt & LIST_INO) | ||
792 | column += printf("%7llu ", (long long) dn->dstat.st_ino); | ||
793 | if (all_fmt & LIST_BLOCKS) | ||
794 | column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1)); | ||
795 | if (all_fmt & LIST_MODEBITS) | ||
796 | column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); | ||
797 | if (all_fmt & LIST_NLINKS) | ||
798 | column += printf("%4lu ", (long) dn->dstat.st_nlink); | ||
799 | #if ENABLE_FEATURE_LS_USERNAME | ||
800 | if (all_fmt & LIST_ID_NAME) { | ||
801 | if (option_mask32 & OPT_g) { | ||
802 | column += printf("%-8.8s ", | ||
803 | get_cached_username(dn->dstat.st_uid)); | ||
804 | } else { | ||
805 | column += printf("%-8.8s %-8.8s ", | ||
806 | get_cached_username(dn->dstat.st_uid), | ||
807 | get_cached_groupname(dn->dstat.st_gid)); | ||
808 | } | ||
809 | } | ||
810 | #endif | ||
811 | if (all_fmt & LIST_ID_NUMERIC) { | ||
812 | if (option_mask32 & OPT_g) | ||
813 | column += printf("%-8u ", (int) dn->dstat.st_uid); | ||
814 | else | ||
815 | column += printf("%-8u %-8u ", | ||
816 | (int) dn->dstat.st_uid, | ||
817 | (int) dn->dstat.st_gid); | ||
818 | } | ||
819 | if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) { | ||
820 | if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { | ||
821 | column += printf("%4u, %3u ", | ||
822 | (int) major(dn->dstat.st_rdev), | ||
823 | (int) minor(dn->dstat.st_rdev)); | ||
824 | } else { | ||
825 | if (all_fmt & LS_DISP_HR) { | ||
826 | column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", | ||
827 | /* print st_size, show one fractional, use suffixes */ | ||
828 | make_human_readable_str(dn->dstat.st_size, 1, 0) | ||
829 | ); | ||
830 | } else { | ||
831 | column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); | ||
832 | } | ||
833 | } | ||
834 | } | ||
835 | #if ENABLE_FEATURE_LS_TIMESTAMPS | ||
836 | if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { | ||
837 | char *filetime; | ||
838 | time_t ttime = dn->dstat.st_mtime; | ||
839 | if (all_fmt & TIME_ACCESS) | ||
840 | ttime = dn->dstat.st_atime; | ||
841 | if (all_fmt & TIME_CHANGE) | ||
842 | ttime = dn->dstat.st_ctime; | ||
843 | filetime = ctime(&ttime); | ||
844 | /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ | ||
845 | if (all_fmt & LIST_FULLTIME) | ||
846 | column += printf("%.24s ", filetime); | ||
847 | else { /* LIST_DATE_TIME */ | ||
848 | /* current_time_t ~== time(NULL) */ | ||
849 | time_t age = current_time_t - ttime; | ||
850 | printf("%.6s ", filetime + 4); /* "Jun 30" */ | ||
851 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { | ||
852 | /* hh:mm if less than 6 months old */ | ||
853 | printf("%.5s ", filetime + 11); | ||
854 | } else { /* year. buggy if year > 9999 ;) */ | ||
855 | printf(" %.4s ", filetime + 20); | ||
856 | } | ||
857 | column += 13; | ||
858 | } | ||
859 | } | ||
860 | #endif | ||
861 | #if ENABLE_SELINUX | ||
862 | if (all_fmt & LIST_CONTEXT) { | ||
863 | column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); | ||
864 | freecon(dn->sid); | ||
865 | } | ||
866 | #endif | ||
867 | if (all_fmt & LIST_FILENAME) { | ||
868 | #if ENABLE_FEATURE_LS_COLOR | ||
869 | if (show_color) { | ||
870 | info.st_mode = 0; /* for fgcolor() */ | ||
871 | lstat(dn->fullname, &info); | ||
872 | printf("\033[%u;%um", bold(info.st_mode), | ||
873 | fgcolor(info.st_mode)); | ||
874 | } | ||
875 | #endif | ||
876 | column += print_name(dn->name); | ||
877 | if (show_color) { | ||
878 | printf("\033[0m"); | ||
879 | } | ||
880 | } | ||
881 | if (all_fmt & LIST_SYMLINK) { | ||
882 | if (S_ISLNK(dn->dstat.st_mode) && lpath) { | ||
883 | printf(" -> "); | ||
884 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | ||
885 | #if ENABLE_FEATURE_LS_COLOR | ||
886 | info.st_mode = 0; /* for fgcolor() */ | ||
887 | #endif | ||
888 | if (stat(dn->fullname, &info) == 0) { | ||
889 | append = append_char(info.st_mode); | ||
890 | } | ||
891 | #endif | ||
892 | #if ENABLE_FEATURE_LS_COLOR | ||
893 | if (show_color) { | ||
894 | printf("\033[%u;%um", bold(info.st_mode), | ||
895 | fgcolor(info.st_mode)); | ||
896 | } | ||
897 | #endif | ||
898 | column += print_name(lpath) + 4; | ||
899 | if (show_color) { | ||
900 | printf("\033[0m"); | ||
901 | } | ||
902 | free(lpath); | ||
903 | } | ||
904 | } | ||
905 | #if ENABLE_FEATURE_LS_FILETYPES | ||
906 | if (all_fmt & LIST_FILETYPE) { | ||
907 | if (append) { | ||
908 | putchar(append); | ||
909 | column++; | ||
910 | } | ||
911 | } | ||
912 | #endif | ||
913 | |||
914 | return column; | ||
915 | } | ||
916 | |||
917 | |||
918 | int ls_main(int argc UNUSED_PARAM, char **argv) | 940 | int ls_main(int argc UNUSED_PARAM, char **argv) |
919 | { | 941 | { |
920 | struct dnode **dnd; | 942 | struct dnode **dnd; |