Changeset 3232 in MondoRescue for branches/3.2/mindi-busybox/coreutils/ls.c
- Timestamp:
- Jan 1, 2014, 12:47:38 AM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/3.2/mindi-busybox/coreutils/ls.c
r2725 r3232 1 1 /* vi: set sw=4 ts=4: */ 2 2 /* 3 * tiny-ls.c version 0.1.0: A minimalist 'ls'4 3 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com> 5 4 * … … 30 29 * ls sorts listing now, and supports almost all options. 31 30 */ 31 32 //usage:#define ls_trivial_usage 33 //usage: "[-1AaCxd" 34 //usage: IF_FEATURE_LS_FOLLOWLINKS("LH") 35 //usage: IF_FEATURE_LS_RECURSIVE("R") 36 //usage: IF_FEATURE_LS_FILETYPES("Fp") "lins" 37 //usage: IF_FEATURE_LS_TIMESTAMPS("e") 38 //usage: IF_FEATURE_HUMAN_READABLE("h") 39 //usage: IF_FEATURE_LS_SORTFILES("rSXv") 40 //usage: IF_FEATURE_LS_TIMESTAMPS("ctu") 41 //usage: IF_SELINUX("kKZ") "]" 42 //usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..." 43 //usage:#define ls_full_usage "\n\n" 44 //usage: "List directory contents\n" 45 //usage: "\n -1 One column output" 46 //usage: "\n -a Include entries which start with ." 47 //usage: "\n -A Like -a, but exclude . and .." 48 //usage: "\n -C List by columns" 49 //usage: "\n -x List by lines" 50 //usage: "\n -d List directory entries instead of contents" 51 //usage: IF_FEATURE_LS_FOLLOWLINKS( 52 //usage: "\n -L Follow symlinks" 53 //usage: "\n -H Follow symlinks on command line" 54 //usage: ) 55 //usage: IF_FEATURE_LS_RECURSIVE( 56 //usage: "\n -R Recurse" 57 //usage: ) 58 //usage: IF_FEATURE_LS_FILETYPES( 59 //usage: "\n -p Append / to dir entries" 60 //usage: "\n -F Append indicator (one of */=@|) to entries" 61 //usage: ) 62 //usage: "\n -l Long listing format" 63 //usage: "\n -i List inode numbers" 64 //usage: "\n -n List numeric UIDs and GIDs instead of names" 65 //usage: "\n -s List allocated blocks" 66 //usage: IF_FEATURE_LS_TIMESTAMPS( 67 //usage: "\n -e List full date and time" 68 //usage: ) 69 //usage: IF_FEATURE_HUMAN_READABLE( 70 //usage: "\n -h List sizes in human readable format (1K 243M 2G)" 71 //usage: ) 72 //usage: IF_FEATURE_LS_SORTFILES( 73 //usage: "\n -r Sort in reverse order" 74 //usage: "\n -S Sort by size" 75 //usage: "\n -X Sort by extension" 76 //usage: "\n -v Sort by version" 77 //usage: ) 78 //usage: IF_FEATURE_LS_TIMESTAMPS( 79 //usage: "\n -c With -l: sort by ctime" 80 //usage: "\n -t With -l: sort by mtime" 81 //usage: "\n -u With -l: sort by atime" 82 //usage: ) 83 //usage: IF_SELINUX( 84 //usage: "\n -k List security context" 85 //usage: "\n -K List security context in long format" 86 //usage: "\n -Z List security context and permission" 87 //usage: ) 88 //usage: IF_FEATURE_AUTOWIDTH( 89 //usage: "\n -w N Assume the terminal is N columns wide" 90 //usage: ) 91 //usage: IF_FEATURE_LS_COLOR( 92 //usage: "\n --color[={always,never,auto}] Control coloring" 93 //usage: ) 94 32 95 #include "libbb.h" 33 96 #include "unicode.h" … … 54 117 enum { 55 118 TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ 56 COLUMN_GAP = 2, /* includes the file type char */ 57 58 /* what is the overall style of the listing */ 59 STYLE_COLUMNS = 1 << 21, /* fill columns */ 60 STYLE_LONG = 2 << 21, /* one record per line, extended info */ 61 STYLE_SINGLE = 3 << 21, /* one record per line */ 62 STYLE_MASK = STYLE_SINGLE, 119 120 SPLIT_FILE = 0, 121 SPLIT_DIR = 1, 122 SPLIT_SUBDIR = 2, 123 124 /* Bits in G.all_fmt: */ 63 125 64 126 /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ … … 72 134 LIST_CONTEXT = 1 << 6, 73 135 LIST_SIZE = 1 << 7, 74 //LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE 75 LIST_DATE_TIME = 1 << 9, 76 LIST_FULLTIME = 1 << 10, 77 LIST_FILENAME = 1 << 11, 78 LIST_SYMLINK = 1 << 12, 79 LIST_FILETYPE = 1 << 13, 80 LIST_EXEC = 1 << 14, 81 LIST_MASK = (LIST_EXEC << 1) - 1, 136 LIST_DATE_TIME = 1 << 8, 137 LIST_FULLTIME = 1 << 9, 138 LIST_SYMLINK = 1 << 10, 139 LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */ 140 LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */ 141 LIST_MASK = (LIST_CLASSIFY << 1) - 1, 82 142 83 143 /* what files will be displayed */ 84 DISP_DIRNAME = 1 << 1 5, /* 2 or more items? label directories */85 DISP_HIDDEN = 1 << 1 6, /* show filenames starting with . */86 DISP_DOT = 1 << 1 7, /* show . and .. */87 DISP_NOLIST = 1 << 1 8, /* show directory as itself, not contents */88 DISP_RECURSIVE = 1 << 1 9, /* show directory and everything below it */89 DISP_ROWS = 1 << 20, /* print across rows */144 DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ 145 DISP_HIDDEN = 1 << 14, /* show filenames starting with . */ 146 DISP_DOT = 1 << 15, /* show . and .. */ 147 DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */ 148 DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */ 149 DISP_ROWS = 1 << 18, /* print across rows */ 90 150 DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1), 91 151 152 /* what is the overall style of the listing */ 153 STYLE_COLUMNAR = 1 << 19, /* many records per line */ 154 STYLE_LONG = 2 << 19, /* one record per line, extended info */ 155 STYLE_SINGLE = 3 << 19, /* one record per line */ 156 STYLE_MASK = STYLE_SINGLE, 157 158 /* which of the three times will be used */ 159 TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, 160 TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, 161 TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, 162 92 163 /* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */ 93 SORT_FORWARD = 0, /* sort in reverse order */ 94 SORT_REVERSE = 1 << 27, /* sort in reverse order */ 164 SORT_REVERSE = 1 << 23, 95 165 96 166 SORT_NAME = 0, /* sort by file name */ 97 SORT_SIZE = 1 << 28, /* sort by file size */ 98 SORT_ATIME = 2 << 28, /* sort by last access time */ 99 SORT_CTIME = 3 << 28, /* sort by last change time */ 100 SORT_MTIME = 4 << 28, /* sort by last modification time */ 101 SORT_VERSION = 5 << 28, /* sort by version */ 102 SORT_EXT = 6 << 28, /* sort by file name extension */ 103 SORT_DIR = 7 << 28, /* sort by file or directory */ 104 SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES, 105 106 /* which of the three times will be used */ 107 TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS, 108 TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS, 109 TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS, 110 111 FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS, 112 113 LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE, 114 115 LIST_SHORT = LIST_FILENAME, 167 SORT_SIZE = 1 << 24, /* sort by file size */ 168 SORT_ATIME = 2 << 24, /* sort by last access time */ 169 SORT_CTIME = 3 << 24, /* sort by last change time */ 170 SORT_MTIME = 4 << 24, /* sort by last modification time */ 171 SORT_VERSION = 5 << 24, /* sort by version */ 172 SORT_EXT = 6 << 24, /* sort by file name extension */ 173 SORT_DIR = 7 << 24, /* sort by file or directory */ 174 SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES, 175 116 176 LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \ 117 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK, 118 119 SPLIT_DIR = 1, 120 SPLIT_FILE = 0, 121 SPLIT_SUBDIR = 2, 177 LIST_DATE_TIME | LIST_SYMLINK, 122 178 }; 123 179 124 /* "[-]Cadil1", POSIX mandated options, busybox always supports */ 125 /* "[-]gnsx", POSIX non-mandated options, busybox always supports */ 126 /* "[-]Q" GNU option? busybox always supports */ 127 /* "[-]Ak" GNU options, busybox always supports */ 128 /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */ 129 /* "[-]p", POSIX non-mandated options, busybox optionally supports */ 130 /* "[-]SXvThw", GNU options, busybox optionally supports */ 131 /* "[-]K", SELinux mandated options, busybox optionally supports */ 132 /* "[-]e", I think we made this one up */ 180 /* -Cadil1 Std options, busybox always supports */ 181 /* -gnsxA Std options, busybox always supports */ 182 /* -Q GNU option, busybox always supports */ 183 /* -k SELinux option, busybox always supports (ignores if !SELinux) */ 184 /* Std has -k which means "show sizes in kbytes" */ 185 /* -LHRctur Std options, busybox optionally supports */ 186 /* -Fp Std options, busybox optionally supports */ 187 /* -SXvhTw GNU options, busybox optionally supports */ 188 /* -T WIDTH Ignored (we don't use tabs on output) */ 189 /* -KZ SELinux mandated options, busybox optionally supports */ 190 /* (coreutils 8.4 has no -K, remove it?) */ 191 /* -e I think we made this one up (looks similar to GNU --full-time) */ 192 /* We already used up all 32 bits, if we need to add more, candidates for removal: */ 193 /* -K, -T, -e (add --full-time instead) */ 133 194 static const char ls_options[] ALIGN1 = 134 "Cadil1gnsxQAk" /* 13 opts, total 13 */195 "Cadil1gnsxQAk" /* 13 opts, total 13 */ 135 196 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */ 136 197 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */ 137 198 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */ 138 IF_FEATURE_LS_ FOLLOWLINKS("L")/* 1, 24 */139 IF_ FEATURE_LS_RECURSIVE("R") /* 1, 25*/140 IF_FEATURE_ HUMAN_READABLE("h") /* 1, 26*/141 IF_ SELINUX("KZ") /* 2, 28*/142 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30*/143 ;199 IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */ 200 IF_SELINUX("KZ") /* 2, 26 */ 201 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */ 202 IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */ 203 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */ 204 /* with --color, we use all 32 bits */; 144 205 enum { 145 206 //OPT_C = (1 << 0), … … 156 217 //OPT_A = (1 << 11), 157 218 //OPT_k = (1 << 12), 158 OPTBIT_color = 13 159 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS 160 + 4 * ENABLE_FEATURE_LS_SORTFILES 161 + 2 * ENABLE_FEATURE_LS_FILETYPES 162 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS 163 + 1 * ENABLE_FEATURE_LS_RECURSIVE 164 + 1 * ENABLE_FEATURE_HUMAN_READABLE 165 + 2 * ENABLE_SELINUX 166 + 2 * ENABLE_FEATURE_AUTOWIDTH, 167 OPT_color = 1 << OPTBIT_color, 219 220 OPTBIT_c = 13, 221 OPTBIT_e, 222 OPTBIT_t, 223 OPTBIT_u, 224 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS, 225 OPTBIT_X, /* 18 */ 226 OPTBIT_r, 227 OPTBIT_v, 228 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, 229 OPTBIT_p, /* 22 */ 230 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, 231 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, 232 OPTBIT_Z, /* 25 */ 233 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX, 234 OPTBIT_H, /* 27 */ 235 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, 236 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, 237 OPTBIT_w, /* 30 */ 238 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH, 239 240 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS, 241 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS, 242 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS, 243 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS, 244 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES, 245 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES, 246 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES, 247 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES, 248 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, 249 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, 250 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE, 251 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX, 252 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX, 253 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS, 254 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS, 255 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE, 256 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH, 257 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH, 258 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR, 168 259 }; 169 260 170 enum { 171 LIST_MASK_TRIGGER = 0, 172 STYLE_MASK_TRIGGER = STYLE_MASK, 173 DISP_MASK_TRIGGER = DISP_ROWS, 174 SORT_MASK_TRIGGER = SORT_MASK, 261 /* TODO: simple toggles may be stored as OPT_xxx bits instead */ 262 static const uint32_t opt_flags[] = { 263 STYLE_COLUMNAR, /* C */ 264 DISP_HIDDEN | DISP_DOT, /* a */ 265 DISP_NOLIST, /* d */ 266 LIST_INO, /* i */ 267 LIST_LONG | STYLE_LONG, /* l */ 268 STYLE_SINGLE, /* 1 */ 269 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */ 270 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */ 271 LIST_BLOCKS, /* s */ 272 DISP_ROWS | STYLE_COLUMNAR, /* x */ 273 0, /* Q (quote filename) - handled via OPT_Q */ 274 DISP_HIDDEN, /* A */ 275 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */ 276 #if ENABLE_FEATURE_LS_TIMESTAMPS 277 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */ 278 LIST_FULLTIME, /* e */ 279 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */ 280 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */ 281 #endif 282 #if ENABLE_FEATURE_LS_SORTFILES 283 SORT_SIZE, /* S */ 284 SORT_EXT, /* X */ 285 SORT_REVERSE, /* r */ 286 SORT_VERSION, /* v */ 287 #endif 288 #if ENABLE_FEATURE_LS_FILETYPES 289 LIST_FILETYPE | LIST_CLASSIFY, /* F */ 290 LIST_FILETYPE, /* p */ 291 #endif 292 #if ENABLE_FEATURE_LS_RECURSIVE 293 DISP_RECURSIVE, /* R */ 294 #endif 295 #if ENABLE_SELINUX 296 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */ 297 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */ 298 #endif 299 (1U << 31) 300 /* options after Z are not processed through opt_flags */ 175 301 }; 176 302 177 /* TODO: simple toggles may be stored as OPT_xxx bits instead */178 static const unsigned opt_flags[] = {179 LIST_SHORT | STYLE_COLUMNS, /* C */180 DISP_HIDDEN | DISP_DOT, /* a */181 DISP_NOLIST, /* d */182 LIST_INO, /* i */183 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */184 LIST_SHORT | STYLE_SINGLE, /* 1 */185 0, /* g (don't show owner) - handled via OPT_g */186 LIST_ID_NUMERIC, /* n */187 LIST_BLOCKS, /* s */188 DISP_ROWS, /* x */189 0, /* Q (quote filename) - handled via OPT_Q */190 DISP_HIDDEN, /* A */191 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */192 #if ENABLE_FEATURE_LS_TIMESTAMPS193 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */194 LIST_FULLTIME, /* e */195 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */196 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */197 #endif198 #if ENABLE_FEATURE_LS_SORTFILES199 SORT_SIZE, /* S */200 SORT_EXT, /* X */201 SORT_REVERSE, /* r */202 SORT_VERSION, /* v */203 #endif204 #if ENABLE_FEATURE_LS_FILETYPES205 LIST_FILETYPE | LIST_EXEC, /* F */206 LIST_FILETYPE, /* p */207 #endif208 #if ENABLE_FEATURE_LS_FOLLOWLINKS209 FOLLOW_LINKS, /* L */210 #endif211 #if ENABLE_FEATURE_LS_RECURSIVE212 DISP_RECURSIVE, /* R */213 #endif214 #if ENABLE_FEATURE_HUMAN_READABLE215 LS_DISP_HR, /* h */216 #endif217 #if ENABLE_SELINUX218 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */219 #endif220 #if ENABLE_SELINUX221 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */222 #endif223 (1U<<31)224 /* options after Z are not processed through opt_flags:225 * T, w - ignored226 */227 };228 229 303 230 304 /* 231 * a directory entry and its stat info are stored here305 * a directory entry and its stat info 232 306 */ 233 307 struct dnode { 234 const char *name; /* the dir entry name */ 235 const char *fullname; /* the dir entry name */ 236 struct dnode *next; /* point at the next node */ 308 const char *name; /* usually basename, but think "ls -l dir/file" */ 309 const char *fullname; /* full name (usable for stat etc) */ 310 struct dnode *dn_next; /* for linked list */ 311 IF_SELINUX(security_context_t sid;) 237 312 smallint fname_allocated; 238 struct stat dstat; /* the file stat info */ 239 IF_SELINUX(security_context_t sid;) 313 314 /* Used to avoid re-doing [l]stat at printout stage 315 * if we already collected needed data in scan stage: 316 */ 317 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */ 318 mode_t dn_mode_stat; /* obtained with stat, or 0 */ 319 320 // struct stat dstat; 321 // struct stat is huge. We don't need it in full. 322 // At least we don't need st_dev and st_blksize, 323 // but there are invisible fields as well 324 // (such as nanosecond-resolution timespamps) 325 // and padding, which we also don't want to store. 326 // We also can pre-parse dev_t dn_rdev (in glibc, it's huge). 327 // On 32-bit uclibc: dnode size went from 112 to 84 bytes. 328 // 329 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ 330 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */ 331 off_t dn_size; 332 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES 333 time_t dn_atime; 334 time_t dn_mtime; 335 time_t dn_ctime; 336 #endif 337 ino_t dn_ino; 338 blkcnt_t dn_blocks; 339 nlink_t dn_nlink; 340 uid_t dn_uid; 341 gid_t dn_gid; 342 int dn_rdev_maj; 343 int dn_rdev_min; 344 // dev_t dn_dev; 345 // blksize_t dn_blksize; 240 346 }; 241 347 … … 243 349 #if ENABLE_FEATURE_LS_COLOR 244 350 smallint show_color; 351 # define G_show_color (G.show_color) 352 #else 353 # define G_show_color 0 245 354 #endif 246 355 smallint exit_code; 247 356 unsigned all_fmt; 248 357 #if ENABLE_FEATURE_AUTOWIDTH 249 unsigned tabstops; // = COLUMN_GAP; 250 unsigned terminal_width; // = TERMINAL_WIDTH; 358 unsigned terminal_width; 359 # define G_terminal_width (G.terminal_width) 360 #else 361 # define G_terminal_width TERMINAL_WIDTH 251 362 #endif 252 363 #if ENABLE_FEATURE_LS_TIMESTAMPS … … 256 367 } FIX_ALIASING; 257 368 #define G (*(struct globals*)&bb_common_bufsiz1) 258 #if ENABLE_FEATURE_LS_COLOR259 # define show_color (G.show_color )260 #else261 enum { show_color = 0 };262 #endif263 #define exit_code (G.exit_code )264 #define all_fmt (G.all_fmt )265 #if ENABLE_FEATURE_AUTOWIDTH266 # define tabstops (G.tabstops )267 # define terminal_width (G.terminal_width)268 #else269 enum {270 tabstops = COLUMN_GAP,271 terminal_width = TERMINAL_WIDTH,272 };273 #endif274 #define current_time_t (G.current_time_t)275 369 #define INIT_G() do { \ 276 370 /* we have to zero it out because of NOEXEC */ \ 277 371 memset(&G, 0, sizeof(G)); \ 278 IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \ 279 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \ 280 IF_FEATURE_LS_TIMESTAMPS(time(¤t_time_t);) \ 372 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \ 373 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ 281 374 } while (0) 282 375 283 376 284 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) 285 { 286 struct stat dstat; 287 struct dnode *cur; 288 IF_SELINUX(security_context_t sid = NULL;) 289 290 if ((all_fmt & FOLLOW_LINKS) || force_follow) { 291 #if ENABLE_SELINUX 292 if (is_selinux_enabled()) { 293 getfilecon(fullname, &sid); 294 } 295 #endif 296 if (stat(fullname, &dstat)) { 297 bb_simple_perror_msg(fullname); 298 exit_code = EXIT_FAILURE; 299 return 0; 300 } 301 } else { 302 #if ENABLE_SELINUX 303 if (is_selinux_enabled()) { 304 lgetfilecon(fullname, &sid); 305 } 306 #endif 307 if (lstat(fullname, &dstat)) { 308 bb_simple_perror_msg(fullname); 309 exit_code = EXIT_FAILURE; 310 return 0; 311 } 312 } 313 314 cur = xmalloc(sizeof(*cur)); 315 cur->fullname = fullname; 316 cur->name = name; 317 cur->dstat = dstat; 318 IF_SELINUX(cur->sid = sid;) 319 return cur; 320 } 377 /*** Output code ***/ 378 321 379 322 380 /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket … … 325 383 * and we use 0 for unknown and 15 for executables (see below) */ 326 384 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) 327 #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) 328 #define APPCHAR(mode) ("\0 |\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])385 /* un fi chr - dir - blk - file - link - sock - - exe */ 386 #define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)]) 329 387 /* 036 black foreground 050 black background 330 388 037 red foreground 051 red background … … 337 395 */ 338 396 #define COLOR(mode) ( \ 339 /*un fi chr dir blk file link sockexe */ \397 /*un fi chr - dir - blk - file - link - sock - - exe */ \ 340 398 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ 341 399 [TYPEINDEX(mode)]) … … 346 404 */ 347 405 #define ATTR(mode) ( \ 348 /*un fi chr dir blk file link sockexe */ \406 /*un fi chr - dir - blk - file- link- sock- - exe */ \ 349 407 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ 350 408 [TYPEINDEX(mode)]) … … 366 424 #endif 367 425 368 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR426 #if ENABLE_FEATURE_LS_FILETYPES 369 427 static char append_char(mode_t mode) 370 428 { 371 if (!( all_fmt & LIST_FILETYPE))429 if (!(G.all_fmt & LIST_FILETYPE)) 372 430 return '\0'; 373 431 if (S_ISDIR(mode)) 374 432 return '/'; 375 if (!( all_fmt & LIST_EXEC))433 if (!(G.all_fmt & LIST_CLASSIFY)) 376 434 return '\0'; 377 435 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) … … 381 439 #endif 382 440 383 static unsigned count_dirs(struct dnode **dn, int which)384 {385 unsigned dirs, all;386 387 if (!dn)388 return 0;389 390 dirs = all = 0;391 for (; *dn; dn++) {392 const char *name;393 394 all++;395 if (!S_ISDIR((*dn)->dstat.st_mode))396 continue;397 name = (*dn)->name;398 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */399 /* or if it's not . or .. */400 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))401 ) {402 dirs++;403 }404 }405 return which != SPLIT_FILE ? dirs : all - dirs;406 }407 408 /* get memory to hold an array of pointers */409 static struct dnode **dnalloc(unsigned num)410 {411 if (num < 1)412 return NULL;413 414 num++; /* so that we have terminating NULL */415 return xzalloc(num * sizeof(struct dnode *));416 }417 418 #if ENABLE_FEATURE_LS_RECURSIVE419 static void dfree(struct dnode **dnp)420 {421 unsigned i;422 423 if (dnp == NULL)424 return;425 426 for (i = 0; dnp[i]; i++) {427 struct dnode *cur = dnp[i];428 if (cur->fname_allocated)429 free((char*)cur->fullname);430 free(cur);431 }432 free(dnp);433 }434 #else435 #define dfree(...) ((void)0)436 #endif437 438 /* Returns NULL-terminated malloced vector of pointers (or NULL) */439 static struct dnode **splitdnarray(struct dnode **dn, int which)440 {441 unsigned dncnt, d;442 struct dnode **dnp;443 444 if (dn == NULL)445 return NULL;446 447 /* count how many dirs or files there are */448 dncnt = count_dirs(dn, which);449 450 /* allocate a file array and a dir array */451 dnp = dnalloc(dncnt);452 453 /* copy the entrys into the file or dir array */454 for (d = 0; *dn; dn++) {455 if (S_ISDIR((*dn)->dstat.st_mode)) {456 const char *name;457 458 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))459 continue;460 name = (*dn)->name;461 if ((which & SPLIT_DIR)462 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))463 ) {464 dnp[d++] = *dn;465 }466 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {467 dnp[d++] = *dn;468 }469 }470 return dnp;471 }472 473 #if ENABLE_FEATURE_LS_SORTFILES474 static int sortcmp(const void *a, const void *b)475 {476 struct dnode *d1 = *(struct dnode **)a;477 struct dnode *d2 = *(struct dnode **)b;478 unsigned sort_opts = all_fmt & SORT_MASK;479 off_t dif;480 481 dif = 0; /* assume SORT_NAME */482 // TODO: use pre-initialized function pointer483 // instead of branch forest484 if (sort_opts == SORT_SIZE) {485 dif = (d2->dstat.st_size - d1->dstat.st_size);486 } else if (sort_opts == SORT_ATIME) {487 dif = (d2->dstat.st_atime - d1->dstat.st_atime);488 } else if (sort_opts == SORT_CTIME) {489 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);490 } else if (sort_opts == SORT_MTIME) {491 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);492 } else if (sort_opts == SORT_DIR) {493 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);494 /* } else if (sort_opts == SORT_VERSION) { */495 /* } else if (sort_opts == SORT_EXT) { */496 }497 if (dif == 0) {498 /* sort by name, or tie_breaker for other sorts */499 if (ENABLE_LOCALE_SUPPORT)500 dif = strcoll(d1->name, d2->name);501 else502 dif = strcmp(d1->name, d2->name);503 }504 505 /* Make dif fit into an int */506 if (sizeof(dif) > sizeof(int)) {507 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };508 /* shift leaving only "int" worth of bits */509 if (dif != 0) {510 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);511 }512 }513 514 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;515 }516 517 static void dnsort(struct dnode **dn, int size)518 {519 qsort(dn, size, sizeof(*dn), sortcmp);520 }521 #else522 #define dnsort(dn, size) ((void)0)523 #endif524 525 526 441 static unsigned calc_name_len(const char *name) 527 442 { … … 546 461 } 547 462 548 549 463 /* Return the number of used columns. 550 * Note that only STYLE_COLUMN Suses return value.464 * Note that only STYLE_COLUMNAR uses return value. 551 465 * STYLE_SINGLE and STYLE_LONG don't care. 552 466 * coreutils 7.2 also supports: … … 582 496 583 497 /* Return the number of used columns. 584 * Note that only STYLE_COLUMN Suses return value,498 * Note that only STYLE_COLUMNAR uses return value, 585 499 * STYLE_SINGLE and STYLE_LONG don't care. 586 500 */ 587 static NOINLINE unsigned list_single(const struct dnode *dn)501 static NOINLINE unsigned display_single(const struct dnode *dn) 588 502 { 589 503 unsigned column = 0; 590 char *lpath = lpath; /* for compiler */504 char *lpath; 591 505 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 592 struct stat info;506 struct stat statbuf; 593 507 char append; 594 508 #endif 595 509 596 /* Never happens:597 if (dn->fullname == NULL)598 return 0;599 */600 601 510 #if ENABLE_FEATURE_LS_FILETYPES 602 append = append_char(dn->d stat.st_mode);511 append = append_char(dn->dn_mode); 603 512 #endif 604 513 605 514 /* Do readlink early, so that if it fails, error message 606 515 * does not appear *inside* the "ls -l" line */ 607 if (all_fmt & LIST_SYMLINK) 608 if (S_ISLNK(dn->dstat.st_mode)) 516 lpath = NULL; 517 if (G.all_fmt & LIST_SYMLINK) 518 if (S_ISLNK(dn->dn_mode)) 609 519 lpath = xmalloc_readlink_or_warn(dn->fullname); 610 520 611 if (all_fmt & LIST_INO) 612 column += printf("%7llu ", (long long) dn->dstat.st_ino); 613 if (all_fmt & LIST_BLOCKS) 614 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1)); 615 if (all_fmt & LIST_MODEBITS) 616 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); 617 if (all_fmt & LIST_NLINKS) 618 column += printf("%4lu ", (long) dn->dstat.st_nlink); 521 if (G.all_fmt & LIST_INO) 522 column += printf("%7llu ", (long long) dn->dn_ino); 523 //TODO: -h should affect -s too: 524 if (G.all_fmt & LIST_BLOCKS) 525 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); 526 if (G.all_fmt & LIST_MODEBITS) 527 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode)); 528 if (G.all_fmt & LIST_NLINKS) 529 column += printf("%4lu ", (long) dn->dn_nlink); 530 if (G.all_fmt & LIST_ID_NUMERIC) { 531 if (option_mask32 & OPT_g) 532 column += printf("%-8u ", (int) dn->dn_gid); 533 else 534 column += printf("%-8u %-8u ", 535 (int) dn->dn_uid, 536 (int) dn->dn_gid); 537 } 619 538 #if ENABLE_FEATURE_LS_USERNAME 620 if (all_fmt & LIST_ID_NAME) {539 else if (G.all_fmt & LIST_ID_NAME) { 621 540 if (option_mask32 & OPT_g) { 622 541 column += printf("%-8.8s ", 623 get_cached_groupname(dn->d stat.st_gid));542 get_cached_groupname(dn->dn_gid)); 624 543 } else { 625 544 column += printf("%-8.8s %-8.8s ", 626 get_cached_username(dn->dstat.st_uid), 627 get_cached_groupname(dn->dstat.st_gid)); 628 } 629 } 630 #endif 631 if (all_fmt & LIST_ID_NUMERIC) { 632 if (option_mask32 & OPT_g) 633 column += printf("%-8u ", (int) dn->dstat.st_gid); 634 else 635 column += printf("%-8u %-8u ", 636 (int) dn->dstat.st_uid, 637 (int) dn->dstat.st_gid); 638 } 639 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) { 640 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { 545 get_cached_username(dn->dn_uid), 546 get_cached_groupname(dn->dn_gid)); 547 } 548 } 549 #endif 550 if (G.all_fmt & LIST_SIZE) { 551 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) { 641 552 column += printf("%4u, %3u ", 642 (int) major(dn->dstat.st_rdev),643 (int) minor(dn->dstat.st_rdev));553 dn->dn_rdev_maj, 554 dn->dn_rdev_min); 644 555 } else { 645 if ( all_fmt & LS_DISP_HR) {556 if (option_mask32 & OPT_h) { 646 557 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", 647 /* print s t_size, show one fractional, use suffixes */648 make_human_readable_str(dn->d stat.st_size, 1, 0)558 /* print size, show one fractional, use suffixes */ 559 make_human_readable_str(dn->dn_size, 1, 0) 649 560 ); 650 561 } else { 651 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);562 column += printf("%9"OFF_FMT"u ", dn->dn_size); 652 563 } 653 564 } 654 565 } 655 566 #if ENABLE_FEATURE_LS_TIMESTAMPS 656 if ( all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {567 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { 657 568 char *filetime; 658 time_t ttime = dn->d stat.st_mtime;659 if ( all_fmt & TIME_ACCESS)660 ttime = dn->d stat.st_atime;661 if ( all_fmt & TIME_CHANGE)662 ttime = dn->d stat.st_ctime;569 time_t ttime = dn->dn_mtime; 570 if (G.all_fmt & TIME_ACCESS) 571 ttime = dn->dn_atime; 572 if (G.all_fmt & TIME_CHANGE) 573 ttime = dn->dn_ctime; 663 574 filetime = ctime(&ttime); 664 575 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ 665 if (all_fmt & LIST_FULLTIME) 576 if (G.all_fmt & LIST_FULLTIME) { /* -e */ 577 /* Note: coreutils 8.4 ls --full-time prints: 578 * 2009-07-13 17:49:27.000000000 +0200 579 */ 666 580 column += printf("%.24s ", filetime); 667 else { /* LIST_DATE_TIME */668 /* current_time_t ~== time(NULL) */669 time_t age = current_time_t - ttime;581 } else { /* LIST_DATE_TIME */ 582 /* G.current_time_t ~== time(NULL) */ 583 time_t age = G.current_time_t - ttime; 670 584 printf("%.6s ", filetime + 4); /* "Jun 30" */ 671 585 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { … … 680 594 #endif 681 595 #if ENABLE_SELINUX 682 if ( all_fmt & LIST_CONTEXT) {596 if (G.all_fmt & LIST_CONTEXT) { 683 597 column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); 684 598 freecon(dn->sid); 685 599 } 686 600 #endif 687 if (all_fmt & LIST_FILENAME) { 601 688 602 #if ENABLE_FEATURE_LS_COLOR 689 if (show_color) { 690 info.st_mode = 0; /* for fgcolor() */ 691 lstat(dn->fullname, &info); 692 printf("\033[%u;%um", bold(info.st_mode), 693 fgcolor(info.st_mode)); 694 } 695 #endif 696 column += print_name(dn->name); 697 if (show_color) { 603 if (G_show_color) { 604 mode_t mode = dn->dn_mode_lstat; 605 if (!mode) 606 if (lstat(dn->fullname, &statbuf) == 0) 607 mode = statbuf.st_mode; 608 printf("\033[%u;%um", bold(mode), fgcolor(mode)); 609 } 610 #endif 611 column += print_name(dn->name); 612 if (G_show_color) { 613 printf("\033[0m"); 614 } 615 616 if (lpath) { 617 printf(" -> "); 618 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 619 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) { 620 mode_t mode = dn->dn_mode_stat; 621 if (!mode) 622 if (stat(dn->fullname, &statbuf) == 0) 623 mode = statbuf.st_mode; 624 # if ENABLE_FEATURE_LS_FILETYPES 625 append = append_char(mode); 626 # endif 627 # if ENABLE_FEATURE_LS_COLOR 628 if (G_show_color) { 629 printf("\033[%u;%um", bold(mode), fgcolor(mode)); 630 } 631 # endif 632 } 633 #endif 634 column += print_name(lpath) + 4; 635 free(lpath); 636 if (G_show_color) { 698 637 printf("\033[0m"); 699 638 } 700 639 } 701 if (all_fmt & LIST_SYMLINK) {702 if (S_ISLNK(dn->dstat.st_mode) && lpath) {703 printf(" -> ");704 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR705 #if ENABLE_FEATURE_LS_COLOR706 info.st_mode = 0; /* for fgcolor() */707 #endif708 if (stat(dn->fullname, &info) == 0) {709 append = append_char(info.st_mode);710 }711 #endif712 #if ENABLE_FEATURE_LS_COLOR713 if (show_color) {714 printf("\033[%u;%um", bold(info.st_mode),715 fgcolor(info.st_mode));716 }717 #endif718 column += print_name(lpath) + 4;719 if (show_color) {720 printf("\033[0m");721 }722 free(lpath);723 }724 }725 640 #if ENABLE_FEATURE_LS_FILETYPES 726 if ( all_fmt & LIST_FILETYPE) {641 if (G.all_fmt & LIST_FILETYPE) { 727 642 if (append) { 728 643 putchar(append); … … 735 650 } 736 651 737 static void showfiles(struct dnode **dn, unsigned nfiles)652 static void display_files(struct dnode **dn, unsigned nfiles) 738 653 { 739 654 unsigned i, ncols, nrows, row, nc; 740 unsigned column = 0;741 unsigned nexttab = 0;742 unsigned column_width = 0; /* used only by STYLE_COLUMN S*/743 744 if ( all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */655 unsigned column; 656 unsigned nexttab; 657 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ 658 659 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ 745 660 ncols = 1; 746 661 } else { … … 751 666 column_width = len; 752 667 } 753 column_width += tabstops+754 IF_SELINUX( (( all_fmt & LIST_CONTEXT) ? 33 : 0) + )755 (( all_fmt & LIST_INO) ? 8 : 0) +756 (( all_fmt & LIST_BLOCKS) ? 5 : 0);757 ncols = ( int) (terminal_width / column_width);668 column_width += 1 + 669 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + ) 670 ((G.all_fmt & LIST_INO) ? 8 : 0) + 671 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0); 672 ncols = (unsigned)G_terminal_width / column_width; 758 673 } 759 674 … … 767 682 } 768 683 684 column = 0; 685 nexttab = 0; 769 686 for (row = 0; row < nrows; row++) { 770 687 for (nc = 0; nc < ncols; nc++) { 771 688 /* reach into the array based on the column and row */ 772 if ( all_fmt & DISP_ROWS)689 if (G.all_fmt & DISP_ROWS) 773 690 i = (row * ncols) + nc; /* display across row */ 774 691 else … … 777 694 if (column > 0) { 778 695 nexttab -= column; 779 printf("%*s ", nexttab, "");780 column += nexttab ;696 printf("%*s ", nexttab, ""); 697 column += nexttab + 1; 781 698 } 782 699 nexttab = column + column_width; 783 column += list_single(dn[i]);700 column += display_single(dn[i]); 784 701 } 785 702 } … … 789 706 } 790 707 708 709 /*** Dir scanning code ***/ 710 711 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) 712 { 713 struct stat statbuf; 714 struct dnode *cur; 715 716 cur = xzalloc(sizeof(*cur)); 717 cur->fullname = fullname; 718 cur->name = name; 719 720 if ((option_mask32 & OPT_L) || force_follow) { 721 #if ENABLE_SELINUX 722 if (is_selinux_enabled()) { 723 getfilecon(fullname, &cur->sid); 724 } 725 #endif 726 if (stat(fullname, &statbuf)) { 727 bb_simple_perror_msg(fullname); 728 G.exit_code = EXIT_FAILURE; 729 free(cur); 730 return NULL; 731 } 732 cur->dn_mode_stat = statbuf.st_mode; 733 } else { 734 #if ENABLE_SELINUX 735 if (is_selinux_enabled()) { 736 lgetfilecon(fullname, &cur->sid); 737 } 738 #endif 739 if (lstat(fullname, &statbuf)) { 740 bb_simple_perror_msg(fullname); 741 G.exit_code = EXIT_FAILURE; 742 free(cur); 743 return NULL; 744 } 745 cur->dn_mode_lstat = statbuf.st_mode; 746 } 747 748 /* cur->dstat = statbuf: */ 749 cur->dn_mode = statbuf.st_mode ; 750 cur->dn_size = statbuf.st_size ; 751 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES 752 cur->dn_atime = statbuf.st_atime ; 753 cur->dn_mtime = statbuf.st_mtime ; 754 cur->dn_ctime = statbuf.st_ctime ; 755 #endif 756 cur->dn_ino = statbuf.st_ino ; 757 cur->dn_blocks = statbuf.st_blocks; 758 cur->dn_nlink = statbuf.st_nlink ; 759 cur->dn_uid = statbuf.st_uid ; 760 cur->dn_gid = statbuf.st_gid ; 761 cur->dn_rdev_maj = major(statbuf.st_rdev); 762 cur->dn_rdev_min = minor(statbuf.st_rdev); 763 764 return cur; 765 } 766 767 static unsigned count_dirs(struct dnode **dn, int which) 768 { 769 unsigned dirs, all; 770 771 if (!dn) 772 return 0; 773 774 dirs = all = 0; 775 for (; *dn; dn++) { 776 const char *name; 777 778 all++; 779 if (!S_ISDIR((*dn)->dn_mode)) 780 continue; 781 782 name = (*dn)->name; 783 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ 784 /* or if it's not . or .. */ 785 || name[0] != '.' 786 || (name[1] && (name[1] != '.' || name[2])) 787 ) { 788 dirs++; 789 } 790 } 791 return which != SPLIT_FILE ? dirs : all - dirs; 792 } 793 794 /* get memory to hold an array of pointers */ 795 static struct dnode **dnalloc(unsigned num) 796 { 797 if (num < 1) 798 return NULL; 799 800 num++; /* so that we have terminating NULL */ 801 return xzalloc(num * sizeof(struct dnode *)); 802 } 803 804 #if ENABLE_FEATURE_LS_RECURSIVE 805 static void dfree(struct dnode **dnp) 806 { 807 unsigned i; 808 809 if (dnp == NULL) 810 return; 811 812 for (i = 0; dnp[i]; i++) { 813 struct dnode *cur = dnp[i]; 814 if (cur->fname_allocated) 815 free((char*)cur->fullname); 816 free(cur); 817 } 818 free(dnp); 819 } 820 #else 821 #define dfree(...) ((void)0) 822 #endif 823 824 /* Returns NULL-terminated malloced vector of pointers (or NULL) */ 825 static struct dnode **splitdnarray(struct dnode **dn, int which) 826 { 827 unsigned dncnt, d; 828 struct dnode **dnp; 829 830 if (dn == NULL) 831 return NULL; 832 833 /* count how many dirs or files there are */ 834 dncnt = count_dirs(dn, which); 835 836 /* allocate a file array and a dir array */ 837 dnp = dnalloc(dncnt); 838 839 /* copy the entrys into the file or dir array */ 840 for (d = 0; *dn; dn++) { 841 if (S_ISDIR((*dn)->dn_mode)) { 842 const char *name; 843 844 if (which == SPLIT_FILE) 845 continue; 846 847 name = (*dn)->name; 848 if ((which & SPLIT_DIR) /* any dir... */ 849 /* ... or not . or .. */ 850 || name[0] != '.' 851 || (name[1] && (name[1] != '.' || name[2])) 852 ) { 853 dnp[d++] = *dn; 854 } 855 } else 856 if (which == SPLIT_FILE) { 857 dnp[d++] = *dn; 858 } 859 } 860 return dnp; 861 } 862 863 #if ENABLE_FEATURE_LS_SORTFILES 864 static int sortcmp(const void *a, const void *b) 865 { 866 struct dnode *d1 = *(struct dnode **)a; 867 struct dnode *d2 = *(struct dnode **)b; 868 unsigned sort_opts = G.all_fmt & SORT_MASK; 869 off_t dif; 870 871 dif = 0; /* assume SORT_NAME */ 872 // TODO: use pre-initialized function pointer 873 // instead of branch forest 874 if (sort_opts == SORT_SIZE) { 875 dif = (d2->dn_size - d1->dn_size); 876 } else 877 if (sort_opts == SORT_ATIME) { 878 dif = (d2->dn_atime - d1->dn_atime); 879 } else 880 if (sort_opts == SORT_CTIME) { 881 dif = (d2->dn_ctime - d1->dn_ctime); 882 } else 883 if (sort_opts == SORT_MTIME) { 884 dif = (d2->dn_mtime - d1->dn_mtime); 885 } else 886 if (sort_opts == SORT_DIR) { 887 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); 888 } else 889 #if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1 890 if (sort_opts == SORT_VERSION) { 891 dif = strverscmp(d1->name, d2->name); 892 } else 893 #endif 894 if (sort_opts == SORT_EXT) { 895 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.')); 896 } 897 if (dif == 0) { 898 /* sort by name, use as tie breaker for other sorts */ 899 if (ENABLE_LOCALE_SUPPORT) 900 dif = strcoll(d1->name, d2->name); 901 else 902 dif = strcmp(d1->name, d2->name); 903 } 904 905 /* Make dif fit into an int */ 906 if (sizeof(dif) > sizeof(int)) { 907 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; 908 /* shift leaving only "int" worth of bits */ 909 if (dif != 0) { 910 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); 911 } 912 } 913 914 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; 915 } 916 917 static void dnsort(struct dnode **dn, int size) 918 { 919 qsort(dn, size, sizeof(*dn), sortcmp); 920 } 921 922 static void sort_and_display_files(struct dnode **dn, unsigned nfiles) 923 { 924 dnsort(dn, nfiles); 925 display_files(dn, nfiles); 926 } 927 #else 928 # define dnsort(dn, size) ((void)0) 929 # define sort_and_display_files(dn, nfiles) display_files(dn, nfiles) 930 #endif 931 932 /* Returns NULL-terminated malloced vector of pointers (or NULL) */ 933 static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) 934 { 935 struct dnode *dn, *cur, **dnp; 936 struct dirent *entry; 937 DIR *dir; 938 unsigned i, nfiles; 939 940 *nfiles_p = 0; 941 dir = warn_opendir(path); 942 if (dir == NULL) { 943 G.exit_code = EXIT_FAILURE; 944 return NULL; /* could not open the dir */ 945 } 946 dn = NULL; 947 nfiles = 0; 948 while ((entry = readdir(dir)) != NULL) { 949 char *fullname; 950 951 /* are we going to list the file- it may be . or .. or a hidden file */ 952 if (entry->d_name[0] == '.') { 953 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) 954 && !(G.all_fmt & DISP_DOT) 955 ) { 956 continue; 957 } 958 if (!(G.all_fmt & DISP_HIDDEN)) 959 continue; 960 } 961 fullname = concat_path_file(path, entry->d_name); 962 cur = my_stat(fullname, bb_basename(fullname), 0); 963 if (!cur) { 964 free(fullname); 965 continue; 966 } 967 cur->fname_allocated = 1; 968 cur->dn_next = dn; 969 dn = cur; 970 nfiles++; 971 } 972 closedir(dir); 973 974 if (dn == NULL) 975 return NULL; 976 977 /* now that we know how many files there are 978 * allocate memory for an array to hold dnode pointers 979 */ 980 *nfiles_p = nfiles; 981 dnp = dnalloc(nfiles); 982 for (i = 0; /* i < nfiles - detected via !dn below */; i++) { 983 dnp[i] = dn; /* save pointer to node in array */ 984 dn = dn->dn_next; 985 if (!dn) 986 break; 987 } 988 989 return dnp; 990 } 791 991 792 992 #if ENABLE_DESKTOP … … 807 1007 while (*dn) { 808 1008 /* st_blocks is in 512 byte blocks */ 809 blocks += (*dn)->d stat.st_blocks;1009 blocks += (*dn)->dn_blocks; 810 1010 dn++; 811 1011 } … … 819 1019 #endif 820 1020 821 822 static struct dnode **list_dir(const char *, unsigned *); 823 824 static void showdirs(struct dnode **dn, int first) 1021 static void scan_and_display_dirs_recur(struct dnode **dn, int first) 825 1022 { 826 1023 unsigned nfiles; 827 unsigned dndirs;828 1024 struct dnode **subdnp; 829 struct dnode **dnd;830 831 /* Never happens:832 if (dn == NULL || ndirs < 1) {833 return;834 }835 */836 1025 837 1026 for (; *dn; dn++) { 838 if ( all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {1027 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { 839 1028 if (!first) 840 1029 bb_putchar('\n'); … … 842 1031 printf("%s:\n", (*dn)->fullname); 843 1032 } 844 subdnp = list_dir((*dn)->fullname, &nfiles);1033 subdnp = scan_one_dir((*dn)->fullname, &nfiles); 845 1034 #if ENABLE_DESKTOP 846 if (( all_fmt & STYLE_MASK) == STYLE_LONG)1035 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG) 847 1036 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); 848 1037 #endif 849 1038 if (nfiles > 0) { 850 1039 /* list all files at this level */ 851 dnsort(subdnp, nfiles);852 showfiles(subdnp, nfiles); 1040 sort_and_display_files(subdnp, nfiles); 1041 853 1042 if (ENABLE_FEATURE_LS_RECURSIVE 854 && ( all_fmt & DISP_RECURSIVE)1043 && (G.all_fmt & DISP_RECURSIVE) 855 1044 ) { 1045 struct dnode **dnd; 1046 unsigned dndirs; 856 1047 /* recursive - list the sub-dirs */ 857 1048 dnd = splitdnarray(subdnp, SPLIT_SUBDIR); … … 859 1050 if (dndirs > 0) { 860 1051 dnsort(dnd, dndirs); 861 s howdirs(dnd, 0);1052 scan_and_display_dirs_recur(dnd, 0); 862 1053 /* free the array of dnode pointers to the dirs */ 863 1054 free(dnd); … … 868 1059 } 869 1060 } 870 }871 872 873 /* Returns NULL-terminated malloced vector of pointers (or NULL) */874 static struct dnode **list_dir(const char *path, unsigned *nfiles_p)875 {876 struct dnode *dn, *cur, **dnp;877 struct dirent *entry;878 DIR *dir;879 unsigned i, nfiles;880 881 /* Never happens:882 if (path == NULL)883 return NULL;884 */885 886 *nfiles_p = 0;887 dir = warn_opendir(path);888 if (dir == NULL) {889 exit_code = EXIT_FAILURE;890 return NULL; /* could not open the dir */891 }892 dn = NULL;893 nfiles = 0;894 while ((entry = readdir(dir)) != NULL) {895 char *fullname;896 897 /* are we going to list the file- it may be . or .. or a hidden file */898 if (entry->d_name[0] == '.') {899 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))900 && !(all_fmt & DISP_DOT)901 ) {902 continue;903 }904 if (!(all_fmt & DISP_HIDDEN))905 continue;906 }907 fullname = concat_path_file(path, entry->d_name);908 cur = my_stat(fullname, bb_basename(fullname), 0);909 if (!cur) {910 free(fullname);911 continue;912 }913 cur->fname_allocated = 1;914 cur->next = dn;915 dn = cur;916 nfiles++;917 }918 closedir(dir);919 920 if (dn == NULL)921 return NULL;922 923 /* now that we know how many files there are924 * allocate memory for an array to hold dnode pointers925 */926 *nfiles_p = nfiles;927 dnp = dnalloc(nfiles);928 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {929 dnp[i] = dn; /* save pointer to node in array */930 dn = dn->next;931 if (!dn)932 break;933 }934 935 return dnp;936 1061 } 937 1062 … … 973 1098 init_unicode(); 974 1099 975 all_fmt = LIST_SHORT |976 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));1100 if (ENABLE_FEATURE_LS_SORTFILES) 1101 G.all_fmt = SORT_NAME; 977 1102 978 1103 #if ENABLE_FEATURE_AUTOWIDTH 979 1104 /* obtain the terminal width */ 980 get_terminal_width_height(STDIN_FILENO, & terminal_width, NULL);1105 get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL); 981 1106 /* go one less... */ 982 terminal_width--;1107 G_terminal_width--; 983 1108 #endif 984 1109 985 1110 /* process options */ 986 1111 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;) 987 #if ENABLE_FEATURE_AUTOWIDTH 988 opt_complementary = "T+:w+"; /* -T N, -w N */ 989 opt = getopt32(argv, ls_options, &tabstops, &terminal_width 990 IF_FEATURE_LS_COLOR(, &color_opt)); 991 #else 992 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt)); 993 #endif 994 for (i = 0; opt_flags[i] != (1U<<31); i++) { 1112 opt_complementary = 1113 /* -e implies -l */ 1114 IF_FEATURE_LS_TIMESTAMPS("el") 1115 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: 1116 * in some pairs of opts, only last one takes effect: 1117 */ 1118 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */ 1119 // ":m-l:l-m" - we don't have -m 1120 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H") 1121 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */ 1122 ":C-1:1-C" /* bycols/oneline */ 1123 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */ 1124 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */ 1125 /* -w NUM: */ 1126 IF_FEATURE_AUTOWIDTH(":w+"); 1127 opt = getopt32(argv, ls_options 1128 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width) 1129 IF_FEATURE_LS_COLOR(, &color_opt) 1130 ); 1131 for (i = 0; opt_flags[i] != (1U << 31); i++) { 995 1132 if (opt & (1 << i)) { 996 unsigned flags = opt_flags[i]; 997 998 if (flags & LIST_MASK_TRIGGER) 999 all_fmt &= ~LIST_MASK; 1000 if (flags & STYLE_MASK_TRIGGER) 1001 all_fmt &= ~STYLE_MASK; 1002 if (flags & SORT_MASK_TRIGGER) 1003 all_fmt &= ~SORT_MASK; 1004 if (flags & DISP_MASK_TRIGGER) 1005 all_fmt &= ~DISP_MASK; 1133 uint32_t flags = opt_flags[i]; 1134 1135 if (flags & STYLE_MASK) 1136 G.all_fmt &= ~STYLE_MASK; 1137 if (flags & SORT_MASK) 1138 G.all_fmt &= ~SORT_MASK; 1006 1139 if (flags & TIME_MASK) 1007 all_fmt &= ~TIME_MASK; 1008 if (flags & LIST_CONTEXT) 1009 all_fmt |= STYLE_SINGLE; 1010 /* huh?? opt cannot be 'l' */ 1011 //if (LS_DISP_HR && opt == 'l') 1012 // all_fmt &= ~LS_DISP_HR; 1013 all_fmt |= flags; 1140 G.all_fmt &= ~TIME_MASK; 1141 1142 G.all_fmt |= flags; 1014 1143 } 1015 1144 } 1016 1145 1017 1146 #if ENABLE_FEATURE_LS_COLOR 1018 /* find color bit value - last position for short getopt*/1147 /* set G_show_color = 1/0 */ 1019 1148 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { 1020 1149 char *p = getenv("LS_COLORS"); 1021 1150 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1022 1151 if (!p || (p[0] && strcmp(p, "none") != 0)) 1023 show_color = 1;1152 G_show_color = 1; 1024 1153 } 1025 1154 if (opt & OPT_color) { 1026 1155 if (color_opt[0] == 'n') 1027 show_color = 0;1156 G_show_color = 0; 1028 1157 else switch (index_in_substrings(color_str, color_opt)) { 1029 1158 case 3: … … 1034 1163 case 1: 1035 1164 case 2: 1036 show_color = 1;1165 G_show_color = 1; 1037 1166 } 1038 1167 } … … 1041 1170 1042 1171 /* sort out which command line options take precedence */ 1043 if (ENABLE_FEATURE_LS_RECURSIVE && ( all_fmt & DISP_NOLIST))1044 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */1172 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST)) 1173 G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ 1045 1174 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1046 if (all_fmt & TIME_CHANGE) 1047 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; 1048 if (all_fmt & TIME_ACCESS) 1049 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; 1050 } 1051 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */ 1052 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC); 1053 if (ENABLE_FEATURE_LS_USERNAME) 1054 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC)) 1055 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ 1056 1057 /* choose a display format */ 1058 if (!(all_fmt & STYLE_MASK)) 1059 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE); 1175 if (G.all_fmt & TIME_CHANGE) 1176 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME; 1177 if (G.all_fmt & TIME_ACCESS) 1178 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME; 1179 } 1180 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ 1181 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); 1182 1183 /* choose a display format if one was not already specified by an option */ 1184 if (!(G.all_fmt & STYLE_MASK)) 1185 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); 1060 1186 1061 1187 argv += optind; … … 1064 1190 1065 1191 if (argv[1]) 1066 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */1192 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ 1067 1193 1068 1194 /* stuff the command line file names into a dnode array */ … … 1070 1196 nfiles = 0; 1071 1197 do { 1072 /* NB: follow links on command line unless -l or -s */ 1073 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS))); 1198 cur = my_stat(*argv, *argv, 1199 /* follow links on command line unless -l, -s or -F: */ 1200 !((G.all_fmt & STYLE_MASK) == STYLE_LONG 1201 || (G.all_fmt & LIST_BLOCKS) 1202 || (option_mask32 & OPT_F) 1203 ) 1204 /* ... or if -H: */ 1205 || (option_mask32 & OPT_H) 1206 /* ... or if -L, but my_stat always follows links if -L */ 1207 ); 1074 1208 argv++; 1075 1209 if (!cur) 1076 1210 continue; 1077 cur->fname_allocated = 0;1078 cur-> next = dn;1211 /*cur->fname_allocated = 0; - already is */ 1212 cur->dn_next = dn; 1079 1213 dn = cur; 1080 1214 nfiles++; … … 1083 1217 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ 1084 1218 if (nfiles == 0) 1085 return exit_code;1219 return G.exit_code; 1086 1220 1087 1221 /* now that we know how many files there are … … 1091 1225 for (i = 0; /* i < nfiles - detected via !dn below */; i++) { 1092 1226 dnp[i] = dn; /* save pointer to node in array */ 1093 dn = dn-> next;1227 dn = dn->dn_next; 1094 1228 if (!dn) 1095 1229 break; 1096 1230 } 1097 1231 1098 if (all_fmt & DISP_NOLIST) { 1099 dnsort(dnp, nfiles); 1100 showfiles(dnp, nfiles); 1232 if (G.all_fmt & DISP_NOLIST) { 1233 sort_and_display_files(dnp, nfiles); 1101 1234 } else { 1102 1235 dnd = splitdnarray(dnp, SPLIT_DIR); … … 1105 1238 dnfiles = nfiles - dndirs; 1106 1239 if (dnfiles > 0) { 1107 dnsort(dnf, dnfiles); 1108 showfiles(dnf, dnfiles); 1240 sort_and_display_files(dnf, dnfiles); 1109 1241 if (ENABLE_FEATURE_CLEAN_UP) 1110 1242 free(dnf); … … 1112 1244 if (dndirs > 0) { 1113 1245 dnsort(dnd, dndirs); 1114 s howdirs(dnd, dnfiles == 0);1246 scan_and_display_dirs_recur(dnd, dnfiles == 0); 1115 1247 if (ENABLE_FEATURE_CLEAN_UP) 1116 1248 free(dnd); 1117 1249 } 1118 1250 } 1251 1119 1252 if (ENABLE_FEATURE_CLEAN_UP) 1120 1253 dfree(dnp); 1121 return exit_code;1122 } 1254 return G.exit_code; 1255 }
Note:
See TracChangeset
for help on using the changeset viewer.