Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/coreutils/ls.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/coreutils/ls.c
r1765 r2725 4 4 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com> 5 5 * 6 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.6 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 7 */ 8 8 9 /* 9 /* [date unknown. Perhaps before year 2000] 10 10 * To achieve a small memory footprint, this version of 'ls' doesn't do any 11 11 * file sorting, and only has the most essential command line switches … … 18 18 * 19 19 * KNOWN BUGS: 20 * 1. ls -l of a directory doesn't give "total <blocks>" header 21 * 2. ls of a symlink to a directory doesn't list directory contents 22 * 3. hidden files can make column width too large 20 * 1. hidden files can make column width too large 23 21 * 24 22 * NON-OPTIMAL BEHAVIOUR: … … 28 26 * PORTABILITY: 29 27 * 1. requires lstat (BSD) - how do you do it without? 28 * 29 * [2009-03] 30 * ls sorts listing now, and supports almost all options. 30 31 */ 31 32 #include <getopt.h>33 32 #include "libbb.h" 33 #include "unicode.h" 34 34 35 35 36 /* This is a NOEXEC applet. Be very careful! */ 36 37 37 38 39 #if ENABLE_FTPD 40 /* ftpd uses ls, and without timestamps Mozilla won't understand 41 * ftpd's LIST output. 42 */ 43 # undef CONFIG_FEATURE_LS_TIMESTAMPS 44 # undef ENABLE_FEATURE_LS_TIMESTAMPS 45 # undef IF_FEATURE_LS_TIMESTAMPS 46 # undef IF_NOT_FEATURE_LS_TIMESTAMPS 47 # define CONFIG_FEATURE_LS_TIMESTAMPS 1 48 # define ENABLE_FEATURE_LS_TIMESTAMPS 1 49 # define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__ 50 # define IF_NOT_FEATURE_LS_TIMESTAMPS(...) 51 #endif 52 53 38 54 enum { 39 40 55 TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ 41 56 COLUMN_GAP = 2, /* includes the file type char */ … … 57 72 LIST_CONTEXT = 1 << 6, 58 73 LIST_SIZE = 1 << 7, 59 LIST_DEV = 1 << 8, 74 //LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE 60 75 LIST_DATE_TIME = 1 << 9, 61 76 LIST_FULLTIME = 1 << 10, … … 105 120 SPLIT_FILE = 0, 106 121 SPLIT_SUBDIR = 2, 107 108 122 }; 109 123 110 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) 111 #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) 112 #define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) 113 #define COLOR(mode) ("\000\043\043\043\042\000\043\043"\ 114 "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)]) 115 #define ATTR(mode) ("\00\00\01\00\01\00\01\00"\ 116 "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)]) 117 118 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ 119 #if ENABLE_FEATURE_LS_COLOR 120 static smallint show_color; 121 /* long option entry used only for --color, which has no short option 122 * equivalent */ 123 static const char ls_color_opt[] ALIGN1 = 124 "color\0" Optional_argument "\xff" /* no short equivalent */ 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 */ 133 static const char ls_options[] ALIGN1 = 134 "Cadil1gnsxQAk" /* 13 opts, total 13 */ 135 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */ 136 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */ 137 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 */ 125 143 ; 126 #else 127 enum { show_color = 0 }; 128 #endif 144 enum { 145 //OPT_C = (1 << 0), 146 //OPT_a = (1 << 1), 147 //OPT_d = (1 << 2), 148 //OPT_i = (1 << 3), 149 //OPT_l = (1 << 4), 150 //OPT_1 = (1 << 5), 151 OPT_g = (1 << 6), 152 //OPT_n = (1 << 7), 153 //OPT_s = (1 << 8), 154 //OPT_x = (1 << 9), 155 OPT_Q = (1 << 10), 156 //OPT_A = (1 << 11), 157 //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, 168 }; 169 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, 175 }; 176 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_TIMESTAMPS 193 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 #endif 198 #if ENABLE_FEATURE_LS_SORTFILES 199 SORT_SIZE, /* S */ 200 SORT_EXT, /* X */ 201 SORT_REVERSE, /* r */ 202 SORT_VERSION, /* v */ 203 #endif 204 #if ENABLE_FEATURE_LS_FILETYPES 205 LIST_FILETYPE | LIST_EXEC, /* F */ 206 LIST_FILETYPE, /* p */ 207 #endif 208 #if ENABLE_FEATURE_LS_FOLLOWLINKS 209 FOLLOW_LINKS, /* L */ 210 #endif 211 #if ENABLE_FEATURE_LS_RECURSIVE 212 DISP_RECURSIVE, /* R */ 213 #endif 214 #if ENABLE_FEATURE_HUMAN_READABLE 215 LS_DISP_HR, /* h */ 216 #endif 217 #if ENABLE_SELINUX 218 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */ 219 #endif 220 #if ENABLE_SELINUX 221 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */ 222 #endif 223 (1U<<31) 224 /* options after Z are not processed through opt_flags: 225 * T, w - ignored 226 */ 227 }; 228 129 229 130 230 /* 131 231 * a directory entry and its stat info are stored here 132 232 */ 133 struct dnode { /* the basic node */ 134 const char *name; /* the dir entry name */ 135 const char *fullname; /* the dir entry name */ 136 int allocated; 233 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 */ 237 smallint fname_allocated; 137 238 struct stat dstat; /* the file stat info */ 138 USE_SELINUX(security_context_t sid;) 139 struct dnode *next; /* point at the next node */ 239 IF_SELINUX(security_context_t sid;) 140 240 }; 141 typedef struct dnode dnode_t; 142 143 static struct dnode **list_dir(const char *); 144 static struct dnode **dnalloc(int); 145 static int list_single(struct dnode *); 146 147 static unsigned all_fmt; 148 241 242 struct globals { 243 #if ENABLE_FEATURE_LS_COLOR 244 smallint show_color; 245 #endif 246 smallint exit_code; 247 unsigned all_fmt; 149 248 #if ENABLE_FEATURE_AUTOWIDTH 150 static unsigned tabstops = COLUMN_GAP; 151 static unsigned terminal_width = TERMINAL_WIDTH; 249 unsigned tabstops; // = COLUMN_GAP; 250 unsigned terminal_width; // = TERMINAL_WIDTH; 251 #endif 252 #if ENABLE_FEATURE_LS_TIMESTAMPS 253 /* Do time() just once. Saves one syscall per file for "ls -l" */ 254 time_t current_time_t; 255 #endif 256 } FIX_ALIASING; 257 #define G (*(struct globals*)&bb_common_bufsiz1) 258 #if ENABLE_FEATURE_LS_COLOR 259 # define show_color (G.show_color ) 260 #else 261 enum { show_color = 0 }; 262 #endif 263 #define exit_code (G.exit_code ) 264 #define all_fmt (G.all_fmt ) 265 #if ENABLE_FEATURE_AUTOWIDTH 266 # define tabstops (G.tabstops ) 267 # define terminal_width (G.terminal_width) 152 268 #else 153 269 enum { … … 156 272 }; 157 273 #endif 158 159 static int status = EXIT_SUCCESS; 274 #define current_time_t (G.current_time_t) 275 #define INIT_G() do { \ 276 /* we have to zero it out because of NOEXEC */ \ 277 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);) \ 281 } while (0) 282 160 283 161 284 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) … … 163 286 struct stat dstat; 164 287 struct dnode *cur; 165 USE_SELINUX(security_context_t sid = NULL;)288 IF_SELINUX(security_context_t sid = NULL;) 166 289 167 290 if ((all_fmt & FOLLOW_LINKS) || force_follow) { … … 172 295 #endif 173 296 if (stat(fullname, &dstat)) { 174 bb_ perror_msg("%s",fullname);175 status= EXIT_FAILURE;297 bb_simple_perror_msg(fullname); 298 exit_code = EXIT_FAILURE; 176 299 return 0; 177 300 } … … 183 306 #endif 184 307 if (lstat(fullname, &dstat)) { 185 bb_ perror_msg("%s",fullname);186 status= EXIT_FAILURE;308 bb_simple_perror_msg(fullname); 309 exit_code = EXIT_FAILURE; 187 310 return 0; 188 311 } 189 312 } 190 313 191 cur = xmalloc(sizeof( struct dnode));314 cur = xmalloc(sizeof(*cur)); 192 315 cur->fullname = fullname; 193 316 cur->name = name; 194 317 cur->dstat = dstat; 195 USE_SELINUX(cur->sid = sid;)318 IF_SELINUX(cur->sid = sid;) 196 319 return cur; 197 320 } 198 321 322 /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 323 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file 324 * 3/7:multiplexed char/block device) 325 * and we use 0 for unknown and 15 for executables (see below) */ 326 #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)]) 329 /* 036 black foreground 050 black background 330 037 red foreground 051 red background 331 040 green foreground 052 green background 332 041 brown foreground 053 brown background 333 042 blue foreground 054 blue background 334 043 magenta (purple) foreground 055 magenta background 335 044 cyan (light blue) foreground 056 cyan background 336 045 gray foreground 057 white background 337 */ 338 #define COLOR(mode) ( \ 339 /*un fi chr dir blk file link sock exe */ \ 340 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ 341 [TYPEINDEX(mode)]) 342 /* Select normal (0) [actually "reset all"] or bold (1) 343 * (other attributes are 2:dim 4:underline 5:blink 7:reverse, 344 * let's use 7 for "impossible" types, just for fun) 345 * Note: coreutils 6.9 uses inverted red for setuid binaries. 346 */ 347 #define ATTR(mode) ( \ 348 /*un fi chr dir blk file link sock exe */ \ 349 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ 350 [TYPEINDEX(mode)]) 351 199 352 #if ENABLE_FEATURE_LS_COLOR 353 /* mode of zero is interpreted as "unknown" (stat failed) */ 200 354 static char fgcolor(mode_t mode) 201 355 { 202 /* Check wheter the file is existing (if so, color it red!) */203 if (errno == ENOENT)204 return '\037';205 356 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) 206 357 return COLOR(0xF000); /* File is executable ... */ 207 358 return COLOR(mode); 208 359 } 209 210 static char bgcolor(mode_t mode) 360 static char bold(mode_t mode) 211 361 { 212 362 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) … … 231 381 #endif 232 382 233 #define countdirs(A, B) count_dirs((A), (B), 1) 234 #define countsubdirs(A, B) count_dirs((A), (B), 0) 235 static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs) 236 { 237 int i, dirs; 383 static unsigned count_dirs(struct dnode **dn, int which) 384 { 385 unsigned dirs, all; 238 386 239 387 if (!dn) 240 388 return 0; 241 dirs = 0; 242 for (i = 0; i < nfiles; i++) { 389 390 dirs = all = 0; 391 for (; *dn; dn++) { 243 392 const char *name; 244 if (!S_ISDIR(dn[i]->dstat.st_mode)) 393 394 all++; 395 if (!S_ISDIR((*dn)->dstat.st_mode)) 245 396 continue; 246 name = dn[i]->name; 247 if (notsubdirs 248 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) 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])) 249 401 ) { 250 402 dirs++; 251 403 } 252 404 } 253 return dirs; 254 } 255 256 static int countfiles(struct dnode **dnp) 257 { 258 int nfiles; 259 struct dnode *cur; 260 261 if (dnp == NULL) 262 return 0; 263 nfiles = 0; 264 for (cur = dnp[0]; cur->next; cur = cur->next) 265 nfiles++; 266 nfiles++; 267 return nfiles; 405 return which != SPLIT_FILE ? dirs : all - dirs; 268 406 } 269 407 270 408 /* get memory to hold an array of pointers */ 271 static struct dnode **dnalloc( intnum)409 static struct dnode **dnalloc(unsigned num) 272 410 { 273 411 if (num < 1) 274 412 return NULL; 275 413 414 num++; /* so that we have terminating NULL */ 276 415 return xzalloc(num * sizeof(struct dnode *)); 277 416 } 278 417 279 418 #if ENABLE_FEATURE_LS_RECURSIVE 280 static void dfree(struct dnode **dnp , int nfiles)281 { 282 inti;419 static void dfree(struct dnode **dnp) 420 { 421 unsigned i; 283 422 284 423 if (dnp == NULL) 285 424 return; 286 425 287 for (i = 0; i < nfiles; i++) {426 for (i = 0; dnp[i]; i++) { 288 427 struct dnode *cur = dnp[i]; 289 if (cur-> allocated)290 free((char*)cur->fullname); /* free the filename */291 free(cur); /* free the dnode */292 } 293 free(dnp); /* free the array holding the dnode pointers */428 if (cur->fname_allocated) 429 free((char*)cur->fullname); 430 free(cur); 431 } 432 free(dnp); 294 433 } 295 434 #else … … 297 436 #endif 298 437 299 static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) 300 { 301 int dncnt, i, d; 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; 302 442 struct dnode **dnp; 303 443 304 if (dn == NULL || nfiles < 1)444 if (dn == NULL) 305 445 return NULL; 306 446 307 /* count how many dirs and regular files there are */ 308 if (which == SPLIT_SUBDIR) 309 dncnt = countsubdirs(dn, nfiles); 310 else { 311 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */ 312 if (which == SPLIT_FILE) 313 dncnt = nfiles - dncnt; /* looking for files */ 314 } 447 /* count how many dirs or files there are */ 448 dncnt = count_dirs(dn, which); 315 449 316 450 /* allocate a file array and a dir array */ … … 318 452 319 453 /* copy the entrys into the file or dir array */ 320 for (d = i = 0; i < nfiles; i++) {321 if (S_ISDIR( dn[i]->dstat.st_mode)) {454 for (d = 0; *dn; dn++) { 455 if (S_ISDIR((*dn)->dstat.st_mode)) { 322 456 const char *name; 457 323 458 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) 324 459 continue; 325 name = dn[i]->name;460 name = (*dn)->name; 326 461 if ((which & SPLIT_DIR) 327 462 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) 328 463 ) { 329 dnp[d++] = dn[i];464 dnp[d++] = *dn; 330 465 } 331 466 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { 332 dnp[d++] = dn[i];467 dnp[d++] = *dn; 333 468 } 334 469 } … … 342 477 struct dnode *d2 = *(struct dnode **)b; 343 478 unsigned sort_opts = all_fmt & SORT_MASK; 344 int dif;479 off_t dif; 345 480 346 481 dif = 0; /* assume SORT_NAME */ … … 348 483 // instead of branch forest 349 484 if (sort_opts == SORT_SIZE) { 350 dif = ( int) (d2->dstat.st_size - d1->dstat.st_size);485 dif = (d2->dstat.st_size - d1->dstat.st_size); 351 486 } else if (sort_opts == SORT_ATIME) { 352 dif = ( int) (d2->dstat.st_atime - d1->dstat.st_atime);487 dif = (d2->dstat.st_atime - d1->dstat.st_atime); 353 488 } else if (sort_opts == SORT_CTIME) { 354 dif = ( int) (d2->dstat.st_ctime - d1->dstat.st_ctime);489 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime); 355 490 } else if (sort_opts == SORT_MTIME) { 356 dif = ( int) (d2->dstat.st_mtime - d1->dstat.st_mtime);491 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime); 357 492 } else if (sort_opts == SORT_DIR) { 358 493 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode); … … 360 495 /* } else if (sort_opts == SORT_EXT) { */ 361 496 } 362 363 497 if (dif == 0) { 364 /* sort by name - may be a tie_breaker for time or size cmp */ 365 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name); 366 else dif = strcmp(d1->name, d2->name); 367 } 368 369 if (all_fmt & SORT_REVERSE) { 370 dif = -dif; 371 } 372 return dif; 498 /* sort by name, or tie_breaker for other sorts */ 499 if (ENABLE_LOCALE_SUPPORT) 500 dif = strcoll(d1->name, d2->name); 501 else 502 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; 373 515 } 374 516 … … 382 524 383 525 384 static void showfiles(struct dnode **dn, int nfiles) 385 { 386 int i, ncols, nrows, row, nc; 387 int column = 0; 388 int nexttab = 0; 389 int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */ 390 391 if (dn == NULL || nfiles < 1) 392 return; 393 394 if (all_fmt & STYLE_LONG) { 526 static unsigned calc_name_len(const char *name) 527 { 528 unsigned len; 529 uni_stat_t uni_stat; 530 531 // TODO: quote tab as \t, etc, if -Q 532 name = printable_string(&uni_stat, name); 533 534 if (!(option_mask32 & OPT_Q)) { 535 return uni_stat.unicode_width; 536 } 537 538 len = 2 + uni_stat.unicode_width; 539 while (*name) { 540 if (*name == '"' || *name == '\\') { 541 len++; 542 } 543 name++; 544 } 545 return len; 546 } 547 548 549 /* Return the number of used columns. 550 * Note that only STYLE_COLUMNS uses return value. 551 * STYLE_SINGLE and STYLE_LONG don't care. 552 * coreutils 7.2 also supports: 553 * ls -b (--escape) = octal escapes (although it doesn't look like working) 554 * ls -N (--literal) = not escape at all 555 */ 556 static unsigned print_name(const char *name) 557 { 558 unsigned len; 559 uni_stat_t uni_stat; 560 561 // TODO: quote tab as \t, etc, if -Q 562 name = printable_string(&uni_stat, name); 563 564 if (!(option_mask32 & OPT_Q)) { 565 fputs(name, stdout); 566 return uni_stat.unicode_width; 567 } 568 569 len = 2 + uni_stat.unicode_width; 570 putchar('"'); 571 while (*name) { 572 if (*name == '"' || *name == '\\') { 573 putchar('\\'); 574 len++; 575 } 576 putchar(*name); 577 name++; 578 } 579 putchar('"'); 580 return len; 581 } 582 583 /* Return the number of used columns. 584 * Note that only STYLE_COLUMNS uses return value, 585 * STYLE_SINGLE and STYLE_LONG don't care. 586 */ 587 static NOINLINE unsigned list_single(const struct dnode *dn) 588 { 589 unsigned column = 0; 590 char *lpath = lpath; /* for compiler */ 591 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 592 struct stat info; 593 char append; 594 #endif 595 596 /* Never happens: 597 if (dn->fullname == NULL) 598 return 0; 599 */ 600 601 #if ENABLE_FEATURE_LS_FILETYPES 602 append = append_char(dn->dstat.st_mode); 603 #endif 604 605 /* Do readlink early, so that if it fails, error message 606 * does not appear *inside* the "ls -l" line */ 607 if (all_fmt & LIST_SYMLINK) 608 if (S_ISLNK(dn->dstat.st_mode)) 609 lpath = xmalloc_readlink_or_warn(dn->fullname); 610 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); 619 #if ENABLE_FEATURE_LS_USERNAME 620 if (all_fmt & LIST_ID_NAME) { 621 if (option_mask32 & OPT_g) { 622 column += printf("%-8.8s ", 623 get_cached_groupname(dn->dstat.st_gid)); 624 } else { 625 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)) { 641 column += printf("%4u, %3u ", 642 (int) major(dn->dstat.st_rdev), 643 (int) minor(dn->dstat.st_rdev)); 644 } else { 645 if (all_fmt & LS_DISP_HR) { 646 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", 647 /* print st_size, show one fractional, use suffixes */ 648 make_human_readable_str(dn->dstat.st_size, 1, 0) 649 ); 650 } else { 651 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); 652 } 653 } 654 } 655 #if ENABLE_FEATURE_LS_TIMESTAMPS 656 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { 657 char *filetime; 658 time_t ttime = dn->dstat.st_mtime; 659 if (all_fmt & TIME_ACCESS) 660 ttime = dn->dstat.st_atime; 661 if (all_fmt & TIME_CHANGE) 662 ttime = dn->dstat.st_ctime; 663 filetime = ctime(&ttime); 664 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ 665 if (all_fmt & LIST_FULLTIME) 666 column += printf("%.24s ", filetime); 667 else { /* LIST_DATE_TIME */ 668 /* current_time_t ~== time(NULL) */ 669 time_t age = current_time_t - ttime; 670 printf("%.6s ", filetime + 4); /* "Jun 30" */ 671 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { 672 /* hh:mm if less than 6 months old */ 673 printf("%.5s ", filetime + 11); 674 } else { /* year. buggy if year > 9999 ;) */ 675 printf(" %.4s ", filetime + 20); 676 } 677 column += 13; 678 } 679 } 680 #endif 681 #if ENABLE_SELINUX 682 if (all_fmt & LIST_CONTEXT) { 683 column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); 684 freecon(dn->sid); 685 } 686 #endif 687 if (all_fmt & LIST_FILENAME) { 688 #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) { 698 printf("\033[0m"); 699 } 700 } 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_COLOR 705 #if ENABLE_FEATURE_LS_COLOR 706 info.st_mode = 0; /* for fgcolor() */ 707 #endif 708 if (stat(dn->fullname, &info) == 0) { 709 append = append_char(info.st_mode); 710 } 711 #endif 712 #if ENABLE_FEATURE_LS_COLOR 713 if (show_color) { 714 printf("\033[%u;%um", bold(info.st_mode), 715 fgcolor(info.st_mode)); 716 } 717 #endif 718 column += print_name(lpath) + 4; 719 if (show_color) { 720 printf("\033[0m"); 721 } 722 free(lpath); 723 } 724 } 725 #if ENABLE_FEATURE_LS_FILETYPES 726 if (all_fmt & LIST_FILETYPE) { 727 if (append) { 728 putchar(append); 729 column++; 730 } 731 } 732 #endif 733 734 return column; 735 } 736 737 static void showfiles(struct dnode **dn, unsigned nfiles) 738 { 739 unsigned i, ncols, nrows, row, nc; 740 unsigned column = 0; 741 unsigned nexttab = 0; 742 unsigned column_width = 0; /* used only by STYLE_COLUMNS */ 743 744 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ 395 745 ncols = 1; 396 746 } else { 397 747 /* find the longest file name, use that as the column width */ 398 for (i = 0; i < nfiles; i++) {399 int len = strlen(dn[i]->name);748 for (i = 0; dn[i]; i++) { 749 int len = calc_name_len(dn[i]->name); 400 750 if (column_width < len) 401 751 column_width = len; 402 752 } 403 753 column_width += tabstops + 404 USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )405 406 754 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) 755 ((all_fmt & LIST_INO) ? 8 : 0) + 756 ((all_fmt & LIST_BLOCKS) ? 5 : 0); 407 757 ncols = (int) (terminal_width / column_width); 408 758 } … … 420 770 for (nc = 0; nc < ncols; nc++) { 421 771 /* reach into the array based on the column and row */ 422 i = (nc * nrows) + row; /* assume display by column */423 772 if (all_fmt & DISP_ROWS) 424 773 i = (row * ncols) + nc; /* display across row */ 774 else 775 i = (nc * nrows) + row; /* display by column */ 425 776 if (i < nfiles) { 426 777 if (column > 0) { … … 439 790 440 791 441 static void showdirs(struct dnode **dn, int ndirs, int first) 442 { 443 int i, nfiles; 792 #if ENABLE_DESKTOP 793 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html 794 * If any of the -l, -n, -s options is specified, each list 795 * of files within the directory shall be preceded by a 796 * status line indicating the number of file system blocks 797 * occupied by files in the directory in 512-byte units if 798 * the -k option is not specified, or 1024-byte units if the 799 * -k option is specified, rounded up to the next integral 800 * number of units. 801 */ 802 /* by Jorgen Overgaard (jorgen AT antistaten.se) */ 803 static off_t calculate_blocks(struct dnode **dn) 804 { 805 uoff_t blocks = 1; 806 if (dn) { 807 while (*dn) { 808 /* st_blocks is in 512 byte blocks */ 809 blocks += (*dn)->dstat.st_blocks; 810 dn++; 811 } 812 } 813 814 /* Even though standard says use 512 byte blocks, coreutils use 1k */ 815 /* Actually, we round up by calculating (blocks + 1) / 2, 816 * "+ 1" was done when we initialized blocks to 1 */ 817 return blocks >> 1; 818 } 819 #endif 820 821 822 static struct dnode **list_dir(const char *, unsigned *); 823 824 static void showdirs(struct dnode **dn, int first) 825 { 826 unsigned nfiles; 827 unsigned dndirs; 444 828 struct dnode **subdnp; 445 int dndirs;446 829 struct dnode **dnd; 447 830 448 if (dn == NULL || ndirs < 1) 831 /* Never happens: 832 if (dn == NULL || ndirs < 1) { 449 833 return; 450 451 for (i = 0; i < ndirs; i++) { 834 } 835 */ 836 837 for (; *dn; dn++) { 452 838 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { 453 839 if (!first) 454 puts("");840 bb_putchar('\n'); 455 841 first = 0; 456 printf("%s:\n", dn[i]->fullname); 457 } 458 subdnp = list_dir(dn[i]->fullname); 459 nfiles = countfiles(subdnp); 842 printf("%s:\n", (*dn)->fullname); 843 } 844 subdnp = list_dir((*dn)->fullname, &nfiles); 845 #if ENABLE_DESKTOP 846 if ((all_fmt & STYLE_MASK) == STYLE_LONG) 847 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); 848 #endif 460 849 if (nfiles > 0) { 461 850 /* list all files at this level */ 462 851 dnsort(subdnp, nfiles); 463 852 showfiles(subdnp, nfiles); 464 if (ENABLE_FEATURE_LS_RECURSIVE ) {465 if (all_fmt & DISP_RECURSIVE) {466 /* recursive- list the sub-dirs */467 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);468 dndirs = countsubdirs(subdnp, nfiles);469 if (dndirs > 0) {470 dnsort(dnd, dndirs);471 showdirs(dnd, dndirs, 0);472 /* free the array of dnode pointers to the dirs */473 free(dnd);474 }853 if (ENABLE_FEATURE_LS_RECURSIVE 854 && (all_fmt & DISP_RECURSIVE) 855 ) { 856 /* recursive - list the sub-dirs */ 857 dnd = splitdnarray(subdnp, SPLIT_SUBDIR); 858 dndirs = count_dirs(subdnp, SPLIT_SUBDIR); 859 if (dndirs > 0) { 860 dnsort(dnd, dndirs); 861 showdirs(dnd, 0); 862 /* free the array of dnode pointers to the dirs */ 863 free(dnd); 475 864 } 476 /* free the dnodes and the fullname mem */477 dfree(subdnp, nfiles);478 865 } 479 } 480 } 481 } 482 483 484 static struct dnode **list_dir(const char *path) 866 /* free the dnodes and the fullname mem */ 867 dfree(subdnp); 868 } 869 } 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) 485 875 { 486 876 struct dnode *dn, *cur, **dnp; 487 877 struct dirent *entry; 488 878 DIR *dir; 489 int i, nfiles; 490 879 unsigned i, nfiles; 880 881 /* Never happens: 491 882 if (path == NULL) 492 883 return NULL; 493 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 } 494 892 dn = NULL; 495 893 nfiles = 0; 496 dir = warn_opendir(path);497 if (dir == NULL) {498 status = EXIT_FAILURE;499 return NULL; /* could not open the dir */500 }501 894 while ((entry = readdir(dir)) != NULL) { 502 895 char *fullname; … … 518 911 continue; 519 912 } 520 cur-> allocated = 1;913 cur->fname_allocated = 1; 521 914 cur->next = dn; 522 915 dn = cur; … … 525 918 closedir(dir); 526 919 920 if (dn == NULL) 921 return NULL; 922 527 923 /* now that we know how many files there are 528 924 * allocate memory for an array to hold dnode pointers 529 925 */ 530 if (dn == NULL) 531 return NULL; 926 *nfiles_p = nfiles; 532 927 dnp = dnalloc(nfiles); 533 for (i = 0, cur = dn; i < nfiles; i++) { 534 dnp[i] = cur; /* save pointer to node in array */ 535 cur = cur->next; 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; 536 933 } 537 934 … … 540 937 541 938 542 #if ENABLE_FEATURE_LS_TIMESTAMPS 543 /* Do time() just once. Saves one syscall per file for "ls -l" */ 544 /* Initialized in main() */ 545 static time_t current_time_t; 546 #endif 547 548 static int list_single(struct dnode *dn) 549 { 550 int i, column = 0; 551 552 #if ENABLE_FEATURE_LS_TIMESTAMPS 553 char *filetime; 554 time_t ttime, age; 555 #endif 556 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 557 struct stat info; 558 char append; 559 #endif 560 561 if (dn->fullname == NULL) 562 return 0; 563 564 #if ENABLE_FEATURE_LS_TIMESTAMPS 565 ttime = dn->dstat.st_mtime; /* the default time */ 566 if (all_fmt & TIME_ACCESS) 567 ttime = dn->dstat.st_atime; 568 if (all_fmt & TIME_CHANGE) 569 ttime = dn->dstat.st_ctime; 570 filetime = ctime(&ttime); 571 #endif 572 #if ENABLE_FEATURE_LS_FILETYPES 573 append = append_char(dn->dstat.st_mode); 574 #endif 575 576 for (i = 0; i <= 31; i++) { 577 switch (all_fmt & (1 << i)) { 578 case LIST_INO: 579 column += printf("%7ld ", (long) dn->dstat.st_ino); 580 break; 581 case LIST_BLOCKS: 582 column += printf("%4"OFF_FMT"d ", (off_t) dn->dstat.st_blocks >> 1); 583 break; 584 case LIST_MODEBITS: 585 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); 586 break; 587 case LIST_NLINKS: 588 column += printf("%4ld ", (long) dn->dstat.st_nlink); 589 break; 590 case LIST_ID_NAME: 591 #if ENABLE_FEATURE_LS_USERNAME 592 printf("%-8.8s %-8.8s", 593 get_cached_username(dn->dstat.st_uid), 594 get_cached_groupname(dn->dstat.st_gid)); 595 column += 17; 596 break; 597 #endif 598 case LIST_ID_NUMERIC: 599 column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); 600 break; 601 case LIST_SIZE: 602 case LIST_DEV: 603 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { 604 column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev), 605 (int) minor(dn->dstat.st_rdev)); 606 } else { 607 if (all_fmt & LS_DISP_HR) { 608 column += printf("%9s ", 609 make_human_readable_str(dn->dstat.st_size, 1, 0)); 610 } else { 611 column += printf("%9"OFF_FMT"d ", (off_t) dn->dstat.st_size); 612 } 613 } 614 break; 615 #if ENABLE_FEATURE_LS_TIMESTAMPS 616 case LIST_FULLTIME: 617 printf("%24.24s ", filetime); 618 column += 25; 619 break; 620 case LIST_DATE_TIME: 621 if ((all_fmt & LIST_FULLTIME) == 0) { 622 /* current_time_t ~== time(NULL) */ 623 age = current_time_t - ttime; 624 printf("%6.6s ", filetime + 4); 625 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { 626 /* hh:mm if less than 6 months old */ 627 printf("%5.5s ", filetime + 11); 628 } else { 629 printf(" %4.4s ", filetime + 20); 630 } 631 column += 13; 632 } 633 break; 634 #endif 635 #if ENABLE_SELINUX 636 case LIST_CONTEXT: 637 { 638 char context[80]; 639 int len = 0; 640 641 if (dn->sid) { 642 /* I assume sid initilized with NULL */ 643 len = strlen(dn->sid) + 1; 644 safe_strncpy(context, dn->sid, len); 645 freecon(dn->sid); 646 } else { 647 safe_strncpy(context, "unknown", 8); 648 } 649 printf("%-32s ", context); 650 column += MAX(33, len); 651 } 652 break; 653 #endif 654 case LIST_FILENAME: 655 errno = 0; 656 #if ENABLE_FEATURE_LS_COLOR 657 if (show_color && !lstat(dn->fullname, &info)) { 658 printf("\033[%d;%dm", bgcolor(info.st_mode), 659 fgcolor(info.st_mode)); 660 } 661 #endif 662 column += printf("%s", dn->name); 663 if (show_color) { 664 printf("\033[0m"); 665 } 666 break; 667 case LIST_SYMLINK: 668 if (S_ISLNK(dn->dstat.st_mode)) { 669 char *lpath = xmalloc_readlink_or_warn(dn->fullname); 670 if (!lpath) break; 671 printf(" -> "); 672 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 673 if (!stat(dn->fullname, &info)) { 674 append = append_char(info.st_mode); 675 } 676 #endif 677 #if ENABLE_FEATURE_LS_COLOR 678 if (show_color) { 679 errno = 0; 680 printf("\033[%d;%dm", bgcolor(info.st_mode), 681 fgcolor(info.st_mode)); 682 } 683 #endif 684 column += printf("%s", lpath) + 4; 685 if (show_color) { 686 printf("\033[0m"); 687 } 688 free(lpath); 689 } 690 break; 691 #if ENABLE_FEATURE_LS_FILETYPES 692 case LIST_FILETYPE: 693 if (append) { 694 putchar(append); 695 column++; 696 } 697 break; 698 #endif 699 } 700 } 701 702 return column; 703 } 704 705 /* "[-]Cadil1", POSIX mandated options, busybox always supports */ 706 /* "[-]gnsx", POSIX non-mandated options, busybox always supports */ 707 /* "[-]Ak" GNU options, busybox always supports */ 708 /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */ 709 /* "[-]p", POSIX non-mandated options, busybox optionally supports */ 710 /* "[-]SXvThw", GNU options, busybox optionally supports */ 711 /* "[-]K", SELinux mandated options, busybox optionally supports */ 712 /* "[-]e", I think we made this one up */ 713 static const char ls_options[] ALIGN1 = 714 "Cadil1gnsxAk" 715 USE_FEATURE_LS_TIMESTAMPS("cetu") 716 USE_FEATURE_LS_SORTFILES("SXrv") 717 USE_FEATURE_LS_FILETYPES("Fp") 718 USE_FEATURE_LS_FOLLOWLINKS("L") 719 USE_FEATURE_LS_RECURSIVE("R") 720 USE_FEATURE_HUMAN_READABLE("h") 721 USE_SELINUX("K") 722 USE_FEATURE_AUTOWIDTH("T:w:") 723 USE_SELINUX("Z"); 724 725 enum { 726 LIST_MASK_TRIGGER = 0, 727 STYLE_MASK_TRIGGER = STYLE_MASK, 728 DISP_MASK_TRIGGER = DISP_ROWS, 729 SORT_MASK_TRIGGER = SORT_MASK, 730 }; 731 732 static const unsigned opt_flags[] = { 733 LIST_SHORT | STYLE_COLUMNS, /* C */ 734 DISP_HIDDEN | DISP_DOT, /* a */ 735 DISP_NOLIST, /* d */ 736 LIST_INO, /* i */ 737 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */ 738 LIST_SHORT | STYLE_SINGLE, /* 1 */ 739 0, /* g - ingored */ 740 LIST_ID_NUMERIC, /* n */ 741 LIST_BLOCKS, /* s */ 742 DISP_ROWS, /* x */ 743 DISP_HIDDEN, /* A */ 744 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */ 745 #if ENABLE_FEATURE_LS_TIMESTAMPS 746 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */ 747 LIST_FULLTIME, /* e */ 748 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */ 749 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */ 750 #endif 751 #if ENABLE_FEATURE_LS_SORTFILES 752 SORT_SIZE, /* S */ 753 SORT_EXT, /* X */ 754 SORT_REVERSE, /* r */ 755 SORT_VERSION, /* v */ 756 #endif 757 #if ENABLE_FEATURE_LS_FILETYPES 758 LIST_FILETYPE | LIST_EXEC, /* F */ 759 LIST_FILETYPE, /* p */ 760 #endif 761 #if ENABLE_FEATURE_LS_FOLLOWLINKS 762 FOLLOW_LINKS, /* L */ 763 #endif 764 #if ENABLE_FEATURE_LS_RECURSIVE 765 DISP_RECURSIVE, /* R */ 766 #endif 767 #if ENABLE_FEATURE_HUMAN_READABLE 768 LS_DISP_HR, /* h */ 769 #endif 770 #if ENABLE_SELINUX 771 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */ 772 #endif 773 #if ENABLE_FEATURE_AUTOWIDTH 774 0, 0, /* T, w - ignored */ 775 #endif 776 #if ENABLE_SELINUX 777 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */ 778 #endif 779 (1U<<31) 780 }; 781 782 783 /* THIS IS A "SAFE" APPLET, main() MAY BE CALLED INTERNALLY FROM SHELL */ 784 /* BE CAREFUL! */ 785 786 int ls_main(int argc, char **argv); 787 int ls_main(int argc, char **argv) 939 int ls_main(int argc UNUSED_PARAM, char **argv) 788 940 { 789 941 struct dnode **dnd; … … 793 945 struct dnode *cur; 794 946 unsigned opt; 795 int nfiles = 0; 796 int dnfiles; 797 int dndirs; 798 int oi; 799 int ac; 800 int i; 801 char **av; 802 USE_FEATURE_AUTOWIDTH(char *tabstops_str = NULL;) 803 USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;) 804 USE_FEATURE_LS_COLOR(char *color_opt;) 805 806 #if ENABLE_FEATURE_LS_TIMESTAMPS 807 time(¤t_time_t); 808 #endif 947 unsigned nfiles; 948 unsigned dnfiles; 949 unsigned dndirs; 950 unsigned i; 951 #if ENABLE_FEATURE_LS_COLOR 952 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ 953 /* coreutils 6.10: 954 * # ls --color=BOGUS 955 * ls: invalid argument 'BOGUS' for '--color' 956 * Valid arguments are: 957 * 'always', 'yes', 'force' 958 * 'never', 'no', 'none' 959 * 'auto', 'tty', 'if-tty' 960 * (and substrings: "--color=alwa" work too) 961 */ 962 static const char ls_longopts[] ALIGN1 = 963 "color\0" Optional_argument "\xff"; /* no short equivalent */ 964 static const char color_str[] ALIGN1 = 965 "always\0""yes\0""force\0" 966 "auto\0""tty\0""if-tty\0"; 967 /* need to initialize since --color has _an optional_ argument */ 968 const char *color_opt = color_str; /* "always" */ 969 #endif 970 971 INIT_G(); 972 973 init_unicode(); 809 974 810 975 all_fmt = LIST_SHORT | … … 812 977 813 978 #if ENABLE_FEATURE_AUTOWIDTH 814 /* Obtain the terminal width */979 /* obtain the terminal width */ 815 980 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL); 816 /* Go one less... */981 /* go one less... */ 817 982 terminal_width--; 818 983 #endif 819 984 820 985 /* process options */ 821 USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)986 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;) 822 987 #if ENABLE_FEATURE_AUTOWIDTH 823 opt = getopt32(argv, ls_options, &tabstops_str, &terminal_width_str 824 USE_FEATURE_LS_COLOR(, &color_opt)); 825 if (tabstops_str) 826 tabstops = xatou(tabstops_str); 827 if (terminal_width_str) 828 terminal_width = xatou(terminal_width_str); 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)); 829 991 #else 830 opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));992 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt)); 831 993 #endif 832 994 for (i = 0; opt_flags[i] != (1U<<31); i++) { … … 858 1020 char *p = getenv("LS_COLORS"); 859 1021 /* LS_COLORS is unset, or (not empty && not "none") ? */ 860 if (!p || (p[0] && strcmp(p, "none") ))1022 if (!p || (p[0] && strcmp(p, "none") != 0)) 861 1023 show_color = 1; 862 1024 } 863 if (opt & (1 << i)) { /* next flag after short options */ 864 if (!color_opt || !strcmp("always", color_opt)) 865 show_color = 1; 866 else if (color_opt && !strcmp("never", color_opt)) 1025 if (opt & OPT_color) { 1026 if (color_opt[0] == 'n') 867 1027 show_color = 0; 868 else if (color_opt && !strcmp("auto", color_opt) && isatty(STDOUT_FILENO)) 869 show_color = 1; 1028 else switch (index_in_substrings(color_str, color_opt)) { 1029 case 3: 1030 case 4: 1031 case 5: 1032 if (isatty(STDOUT_FILENO)) { 1033 case 0: 1034 case 1: 1035 case 2: 1036 show_color = 1; 1037 } 1038 } 870 1039 } 871 1040 #endif … … 890 1059 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE); 891 1060 892 /* 893 * when there are no cmd line args we have to supply a default "." arg. 894 * we will create a second argv array, "av" that will hold either 895 * our created "." arg, or the real cmd line args. The av array 896 * just holds the pointers- we don't move the date the pointers 897 * point to. 898 */ 899 ac = argc - optind; /* how many cmd line args are left */ 900 if (ac < 1) { 901 static const char *const dotdir[] = { "." }; 902 903 av = (char **) dotdir; 904 ac = 1; 905 } else { 906 av = argv + optind; 907 } 908 909 /* now, everything is in the av array */ 910 if (ac > 1) 911 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ 1061 argv += optind; 1062 if (!argv[0]) 1063 *--argv = (char*)"."; 1064 1065 if (argv[1]) 1066 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ 912 1067 913 1068 /* stuff the command line file names into a dnode array */ 914 1069 dn = NULL; 915 for (oi = 0; oi < ac; oi++) { 916 /* ls w/o -l follows links on command line */ 917 cur = my_stat(av[oi], av[oi], !(all_fmt & STYLE_LONG)); 1070 nfiles = 0; 1071 do { 1072 /* NB: follow links on command line unless -l or -s */ 1073 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS))); 1074 argv++; 918 1075 if (!cur) 919 1076 continue; 920 cur-> allocated = 0;1077 cur->fname_allocated = 0; 921 1078 cur->next = dn; 922 1079 dn = cur; 923 1080 nfiles++; 924 } 1081 } while (*argv); 1082 1083 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ 1084 if (nfiles == 0) 1085 return exit_code; 925 1086 926 1087 /* now that we know how many files there are … … 928 1089 */ 929 1090 dnp = dnalloc(nfiles); 930 for (i = 0, cur = dn; i < nfiles; i++) { 931 dnp[i] = cur; /* save pointer to node in array */ 932 cur = cur->next; 1091 for (i = 0; /* i < nfiles - detected via !dn below */; i++) { 1092 dnp[i] = dn; /* save pointer to node in array */ 1093 dn = dn->next; 1094 if (!dn) 1095 break; 933 1096 } 934 1097 935 1098 if (all_fmt & DISP_NOLIST) { 936 1099 dnsort(dnp, nfiles); 937 if (nfiles > 0) 938 showfiles(dnp, nfiles); 1100 showfiles(dnp, nfiles); 939 1101 } else { 940 dnd = splitdnarray(dnp, nfiles,SPLIT_DIR);941 dnf = splitdnarray(dnp, nfiles,SPLIT_FILE);942 dndirs = count dirs(dnp, nfiles);1102 dnd = splitdnarray(dnp, SPLIT_DIR); 1103 dnf = splitdnarray(dnp, SPLIT_FILE); 1104 dndirs = count_dirs(dnp, SPLIT_DIR); 943 1105 dnfiles = nfiles - dndirs; 944 1106 if (dnfiles > 0) { … … 950 1112 if (dndirs > 0) { 951 1113 dnsort(dnd, dndirs); 952 showdirs(dnd, dn dirs, dnfiles == 0);1114 showdirs(dnd, dnfiles == 0); 953 1115 if (ENABLE_FEATURE_CLEAN_UP) 954 1116 free(dnd); … … 956 1118 } 957 1119 if (ENABLE_FEATURE_CLEAN_UP) 958 dfree(dnp , nfiles);959 return status;960 } 1120 dfree(dnp); 1121 return exit_code; 1122 }
Note:
See TracChangeset
for help on using the changeset viewer.