diff options
Diffstat (limited to 'coreutils')
-rw-r--r-- | coreutils/ls.c | 478 |
1 files changed, 240 insertions, 238 deletions
diff --git a/coreutils/ls.c b/coreutils/ls.c index 52c2c4db7..907955077 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -326,7 +326,7 @@ struct dnode { // (such as nanosecond-resolution timespamps) // and padding, which we also don't want to store. // We also can pre-parse dev_t dn_rdev (in glibc, it's huge). -// On 32-bit uclibc: dnode size went from 112 to 84 bytes +// On 32-bit uclibc: dnode size went from 112 to 84 bytes. // /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */ @@ -376,61 +376,8 @@ struct globals { } while (0) -static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) -{ - struct stat statbuf; - struct dnode *cur; +/*** Output code ***/ - cur = xzalloc(sizeof(*cur)); - cur->fullname = fullname; - cur->name = name; - - if ((option_mask32 & OPT_L) || force_follow) { -#if ENABLE_SELINUX - if (is_selinux_enabled()) { - getfilecon(fullname, &cur->sid); - } -#endif - if (stat(fullname, &statbuf)) { - bb_simple_perror_msg(fullname); - G.exit_code = EXIT_FAILURE; - free(cur); - return NULL; - } - cur->dn_mode_stat = statbuf.st_mode; - } else { -#if ENABLE_SELINUX - if (is_selinux_enabled()) { - lgetfilecon(fullname, &cur->sid); - } -#endif - if (lstat(fullname, &statbuf)) { - bb_simple_perror_msg(fullname); - G.exit_code = EXIT_FAILURE; - free(cur); - return NULL; - } - cur->dn_mode_lstat = statbuf.st_mode; - } - - /* cur->dstat = statbuf: */ - cur->dn_mode = statbuf.st_mode ; - cur->dn_size = statbuf.st_size ; -#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES - cur->dn_atime = statbuf.st_atime ; - cur->dn_mtime = statbuf.st_mtime ; - cur->dn_ctime = statbuf.st_ctime ; -#endif - cur->dn_ino = statbuf.st_ino ; - cur->dn_blocks = statbuf.st_blocks; - cur->dn_nlink = statbuf.st_nlink ; - cur->dn_uid = statbuf.st_uid ; - cur->dn_gid = statbuf.st_gid ; - cur->dn_rdev_maj = major(statbuf.st_rdev); - cur->dn_rdev_min = minor(statbuf.st_rdev); - - return cur; -} /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file @@ -493,155 +440,6 @@ static char append_char(mode_t mode) } #endif -static unsigned count_dirs(struct dnode **dn, int which) -{ - unsigned dirs, all; - - if (!dn) - return 0; - - dirs = all = 0; - for (; *dn; dn++) { - const char *name; - - all++; - if (!S_ISDIR((*dn)->dn_mode)) - continue; - - name = (*dn)->name; - if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ - /* or if it's not . or .. */ - || name[0] != '.' - || (name[1] && (name[1] != '.' || name[2])) - ) { - dirs++; - } - } - return which != SPLIT_FILE ? dirs : all - dirs; -} - -/* get memory to hold an array of pointers */ -static struct dnode **dnalloc(unsigned num) -{ - if (num < 1) - return NULL; - - num++; /* so that we have terminating NULL */ - return xzalloc(num * sizeof(struct dnode *)); -} - -#if ENABLE_FEATURE_LS_RECURSIVE -static void dfree(struct dnode **dnp) -{ - unsigned i; - - if (dnp == NULL) - return; - - for (i = 0; dnp[i]; i++) { - struct dnode *cur = dnp[i]; - if (cur->fname_allocated) - free((char*)cur->fullname); - free(cur); - } - free(dnp); -} -#else -#define dfree(...) ((void)0) -#endif - -/* Returns NULL-terminated malloced vector of pointers (or NULL) */ -static struct dnode **splitdnarray(struct dnode **dn, int which) -{ - unsigned dncnt, d; - struct dnode **dnp; - - if (dn == NULL) - return NULL; - - /* count how many dirs or files there are */ - dncnt = count_dirs(dn, which); - - /* allocate a file array and a dir array */ - dnp = dnalloc(dncnt); - - /* copy the entrys into the file or dir array */ - for (d = 0; *dn; dn++) { - if (S_ISDIR((*dn)->dn_mode)) { - const char *name; - - if (which == SPLIT_FILE) - continue; - - name = (*dn)->name; - if ((which & SPLIT_DIR) /* any dir... */ - /* ... or not . or .. */ - || name[0] != '.' - || (name[1] && (name[1] != '.' || name[2])) - ) { - dnp[d++] = *dn; - } - } else - if (which == SPLIT_FILE) { - dnp[d++] = *dn; - } - } - return dnp; -} - -#if ENABLE_FEATURE_LS_SORTFILES -static int sortcmp(const void *a, const void *b) -{ - struct dnode *d1 = *(struct dnode **)a; - struct dnode *d2 = *(struct dnode **)b; - unsigned sort_opts = G.all_fmt & SORT_MASK; - off_t dif; - - dif = 0; /* assume SORT_NAME */ - // TODO: use pre-initialized function pointer - // instead of branch forest - if (sort_opts == SORT_SIZE) { - dif = (d2->dn_size - d1->dn_size); - } else if (sort_opts == SORT_ATIME) { - dif = (d2->dn_atime - d1->dn_atime); - } else if (sort_opts == SORT_CTIME) { - dif = (d2->dn_ctime - d1->dn_ctime); - } else if (sort_opts == SORT_MTIME) { - dif = (d2->dn_mtime - d1->dn_mtime); - } else if (sort_opts == SORT_DIR) { - dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); - /* } else if (sort_opts == SORT_VERSION) { */ - /* } else if (sort_opts == SORT_EXT) { */ - } - if (dif == 0) { - /* sort by name, or tie_breaker for other sorts */ - if (ENABLE_LOCALE_SUPPORT) - dif = strcoll(d1->name, d2->name); - else - dif = strcmp(d1->name, d2->name); - } - - /* Make dif fit into an int */ - if (sizeof(dif) > sizeof(int)) { - enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; - /* shift leaving only "int" worth of bits */ - if (dif != 0) { - dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); - } - } - - return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; -} - -static void dnsort(struct dnode **dn, int size) -{ - qsort(dn, size, sizeof(*dn), sortcmp); -} -#else -#define dnsort(dn, size) ((void)0) -#endif - - static unsigned calc_name_len(const char *name) { unsigned len; @@ -664,7 +462,6 @@ static unsigned calc_name_len(const char *name) return len; } - /* Return the number of used columns. * Note that only STYLE_COLUMNAR uses return value. * STYLE_SINGLE and STYLE_LONG don't care. @@ -703,7 +500,7 @@ static unsigned print_name(const char *name) * Note that only STYLE_COLUMNAR uses return value, * STYLE_SINGLE and STYLE_LONG don't care. */ -static NOINLINE unsigned list_single(const struct dnode *dn) +static NOINLINE unsigned display_single(const struct dnode *dn) { unsigned column = 0; char *lpath; @@ -854,7 +651,7 @@ static NOINLINE unsigned list_single(const struct dnode *dn) return column; } -static void showfiles(struct dnode **dn, unsigned nfiles) +static void display_files(struct dnode **dn, unsigned nfiles) { unsigned i, ncols, nrows, row, nc; unsigned column; @@ -902,7 +699,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles) column += nexttab + 1; } nexttab = column + column_width; - column += list_single(dn[i]); + column += display_single(dn[i]); } } putchar('\n'); @@ -911,38 +708,214 @@ static void showfiles(struct dnode **dn, unsigned nfiles) } -#if ENABLE_DESKTOP -/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html - * If any of the -l, -n, -s options is specified, each list - * of files within the directory shall be preceded by a - * status line indicating the number of file system blocks - * occupied by files in the directory in 512-byte units if - * the -k option is not specified, or 1024-byte units if the - * -k option is specified, rounded up to the next integral - * number of units. - */ -/* by Jorgen Overgaard (jorgen AT antistaten.se) */ -static off_t calculate_blocks(struct dnode **dn) +/*** Dir scanning code ***/ + +static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) { - uoff_t blocks = 1; - if (dn) { - while (*dn) { - /* st_blocks is in 512 byte blocks */ - blocks += (*dn)->dn_blocks; - dn++; + struct stat statbuf; + struct dnode *cur; + + cur = xzalloc(sizeof(*cur)); + cur->fullname = fullname; + cur->name = name; + + if ((option_mask32 & OPT_L) || force_follow) { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + getfilecon(fullname, &cur->sid); + } +#endif + if (stat(fullname, &statbuf)) { + bb_simple_perror_msg(fullname); + G.exit_code = EXIT_FAILURE; + free(cur); + return NULL; + } + cur->dn_mode_stat = statbuf.st_mode; + } else { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + lgetfilecon(fullname, &cur->sid); + } +#endif + if (lstat(fullname, &statbuf)) { + bb_simple_perror_msg(fullname); + G.exit_code = EXIT_FAILURE; + free(cur); + return NULL; } + cur->dn_mode_lstat = statbuf.st_mode; } - /* Even though standard says use 512 byte blocks, coreutils use 1k */ - /* Actually, we round up by calculating (blocks + 1) / 2, - * "+ 1" was done when we initialized blocks to 1 */ - return blocks >> 1; + /* cur->dstat = statbuf: */ + cur->dn_mode = statbuf.st_mode ; + cur->dn_size = statbuf.st_size ; +#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES + cur->dn_atime = statbuf.st_atime ; + cur->dn_mtime = statbuf.st_mtime ; + cur->dn_ctime = statbuf.st_ctime ; +#endif + cur->dn_ino = statbuf.st_ino ; + cur->dn_blocks = statbuf.st_blocks; + cur->dn_nlink = statbuf.st_nlink ; + cur->dn_uid = statbuf.st_uid ; + cur->dn_gid = statbuf.st_gid ; + cur->dn_rdev_maj = major(statbuf.st_rdev); + cur->dn_rdev_min = minor(statbuf.st_rdev); + + return cur; } + +static unsigned count_dirs(struct dnode **dn, int which) +{ + unsigned dirs, all; + + if (!dn) + return 0; + + dirs = all = 0; + for (; *dn; dn++) { + const char *name; + + all++; + if (!S_ISDIR((*dn)->dn_mode)) + continue; + + name = (*dn)->name; + if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ + /* or if it's not . or .. */ + || name[0] != '.' + || (name[1] && (name[1] != '.' || name[2])) + ) { + dirs++; + } + } + return which != SPLIT_FILE ? dirs : all - dirs; +} + +/* get memory to hold an array of pointers */ +static struct dnode **dnalloc(unsigned num) +{ + if (num < 1) + return NULL; + + num++; /* so that we have terminating NULL */ + return xzalloc(num * sizeof(struct dnode *)); +} + +#if ENABLE_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp) +{ + unsigned i; + + if (dnp == NULL) + return; + + for (i = 0; dnp[i]; i++) { + struct dnode *cur = dnp[i]; + if (cur->fname_allocated) + free((char*)cur->fullname); + free(cur); + } + free(dnp); +} +#else +#define dfree(...) ((void)0) #endif +/* Returns NULL-terminated malloced vector of pointers (or NULL) */ +static struct dnode **splitdnarray(struct dnode **dn, int which) +{ + unsigned dncnt, d; + struct dnode **dnp; + + if (dn == NULL) + return NULL; + + /* count how many dirs or files there are */ + dncnt = count_dirs(dn, which); + + /* allocate a file array and a dir array */ + dnp = dnalloc(dncnt); + + /* copy the entrys into the file or dir array */ + for (d = 0; *dn; dn++) { + if (S_ISDIR((*dn)->dn_mode)) { + const char *name; + + if (which == SPLIT_FILE) + continue; + + name = (*dn)->name; + if ((which & SPLIT_DIR) /* any dir... */ + /* ... or not . or .. */ + || name[0] != '.' + || (name[1] && (name[1] != '.' || name[2])) + ) { + dnp[d++] = *dn; + } + } else + if (which == SPLIT_FILE) { + dnp[d++] = *dn; + } + } + return dnp; +} + +#if ENABLE_FEATURE_LS_SORTFILES +static int sortcmp(const void *a, const void *b) +{ + struct dnode *d1 = *(struct dnode **)a; + struct dnode *d2 = *(struct dnode **)b; + unsigned sort_opts = G.all_fmt & SORT_MASK; + off_t dif; + + dif = 0; /* assume SORT_NAME */ + // TODO: use pre-initialized function pointer + // instead of branch forest + if (sort_opts == SORT_SIZE) { + dif = (d2->dn_size - d1->dn_size); + } else if (sort_opts == SORT_ATIME) { + dif = (d2->dn_atime - d1->dn_atime); + } else if (sort_opts == SORT_CTIME) { + dif = (d2->dn_ctime - d1->dn_ctime); + } else if (sort_opts == SORT_MTIME) { + dif = (d2->dn_mtime - d1->dn_mtime); + } else if (sort_opts == SORT_DIR) { + dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); + /* } else if (sort_opts == SORT_VERSION) { */ + /* } else if (sort_opts == SORT_EXT) { */ + } + if (dif == 0) { + /* sort by name, or tie_breaker for other sorts */ + if (ENABLE_LOCALE_SUPPORT) + dif = strcoll(d1->name, d2->name); + else + dif = strcmp(d1->name, d2->name); + } + + /* Make dif fit into an int */ + if (sizeof(dif) > sizeof(int)) { + enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; + /* shift leaving only "int" worth of bits */ + if (dif != 0) { + dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); + } + } + + return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; +} + +static void dnsort(struct dnode **dn, int size) +{ + qsort(dn, size, sizeof(*dn), sortcmp); +} +#else +#define dnsort(dn, size) ((void)0) +#endif /* Returns NULL-terminated malloced vector of pointers (or NULL) */ -static struct dnode **list_dir(const char *path, unsigned *nfiles_p) +static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) { struct dnode *dn, *cur, **dnp; struct dirent *entry; @@ -1001,8 +974,36 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) return dnp; } +#if ENABLE_DESKTOP +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html + * If any of the -l, -n, -s options is specified, each list + * of files within the directory shall be preceded by a + * status line indicating the number of file system blocks + * occupied by files in the directory in 512-byte units if + * the -k option is not specified, or 1024-byte units if the + * -k option is specified, rounded up to the next integral + * number of units. + */ +/* by Jorgen Overgaard (jorgen AT antistaten.se) */ +static off_t calculate_blocks(struct dnode **dn) +{ + uoff_t blocks = 1; + if (dn) { + while (*dn) { + /* st_blocks is in 512 byte blocks */ + blocks += (*dn)->dn_blocks; + dn++; + } + } + + /* Even though standard says use 512 byte blocks, coreutils use 1k */ + /* Actually, we round up by calculating (blocks + 1) / 2, + * "+ 1" was done when we initialized blocks to 1 */ + return blocks >> 1; +} +#endif -static void showdirs(struct dnode **dn, int first) +static void scan_and_display_dirs_recur(struct dnode **dn, int first) { unsigned nfiles; struct dnode **subdnp; @@ -1014,7 +1015,7 @@ static void showdirs(struct dnode **dn, int first) first = 0; printf("%s:\n", (*dn)->fullname); } - subdnp = list_dir((*dn)->fullname, &nfiles); + subdnp = scan_one_dir((*dn)->fullname, &nfiles); #if ENABLE_DESKTOP if ((G.all_fmt & STYLE_MASK) == STYLE_LONG) printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); @@ -1022,7 +1023,8 @@ static void showdirs(struct dnode **dn, int first) if (nfiles > 0) { /* list all files at this level */ dnsort(subdnp, nfiles); - showfiles(subdnp, nfiles); + display_files(subdnp, nfiles); + if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_RECURSIVE) ) { @@ -1033,7 +1035,7 @@ static void showdirs(struct dnode **dn, int first) dndirs = count_dirs(subdnp, SPLIT_SUBDIR); if (dndirs > 0) { dnsort(dnd, dndirs); - showdirs(dnd, 0); + scan_and_display_dirs_recur(dnd, 0); /* free the array of dnode pointers to the dirs */ free(dnd); } @@ -1215,7 +1217,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv) if (G.all_fmt & DISP_NOLIST) { dnsort(dnp, nfiles); - showfiles(dnp, nfiles); + display_files(dnp, nfiles); } else { dnd = splitdnarray(dnp, SPLIT_DIR); dnf = splitdnarray(dnp, SPLIT_FILE); @@ -1223,13 +1225,13 @@ int ls_main(int argc UNUSED_PARAM, char **argv) dnfiles = nfiles - dndirs; if (dnfiles > 0) { dnsort(dnf, dnfiles); - showfiles(dnf, dnfiles); + display_files(dnf, dnfiles); if (ENABLE_FEATURE_CLEAN_UP) free(dnf); } if (dndirs > 0) { dnsort(dnd, dndirs); - showdirs(dnd, dnfiles == 0); + scan_and_display_dirs_recur(dnd, dnfiles == 0); if (ENABLE_FEATURE_CLEAN_UP) free(dnd); } |