diff options
author | Ron Yorston <rmy@pobox.com> | 2012-03-22 15:21:20 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2012-03-22 15:21:20 +0000 |
commit | 0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2 (patch) | |
tree | 6709ddd6071a9c238ba69233540bbcfe560c6a44 /coreutils | |
parent | 67758035a4fe040c6ac69b39d61bcd6bddd7b827 (diff) | |
parent | 56a3b82e9692a25ef9c9269e88feac0d579ce8e8 (diff) | |
download | busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.gz busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.bz2 busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.zip |
Merge commit '56a3b82e9692a25ef9c9269e88feac0d579ce8e8' into merge
Conflicts:
coreutils/ls.c
include/platform.h
libbb/bb_basename.c
Diffstat (limited to 'coreutils')
-rw-r--r-- | coreutils/ls.c | 853 | ||||
-rw-r--r-- | coreutils/od.c | 7 | ||||
-rw-r--r-- | coreutils/od_bloaty.c | 443 | ||||
-rw-r--r-- | coreutils/tail.c | 36 |
4 files changed, 673 insertions, 666 deletions
diff --git a/coreutils/ls.c b/coreutils/ls.c index 09b9f101a..c967fd462 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c | |||
@@ -39,7 +39,7 @@ | |||
39 | //usage: IF_FEATURE_LS_SORTFILES("rSXv") | 39 | //usage: IF_FEATURE_LS_SORTFILES("rSXv") |
40 | //usage: IF_FEATURE_LS_TIMESTAMPS("ctu") | 40 | //usage: IF_FEATURE_LS_TIMESTAMPS("ctu") |
41 | //usage: IF_SELINUX("kKZ") "]" | 41 | //usage: IF_SELINUX("kKZ") "]" |
42 | //usage: IF_FEATURE_AUTOWIDTH(" -w WIDTH") " [FILE]..." | 42 | //usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..." |
43 | //usage:#define ls_full_usage "\n\n" | 43 | //usage:#define ls_full_usage "\n\n" |
44 | //usage: "List directory contents\n" | 44 | //usage: "List directory contents\n" |
45 | //usage: "\nOptions:" | 45 | //usage: "\nOptions:" |
@@ -51,14 +51,14 @@ | |||
51 | //usage: "\n -d List directory entries instead of contents" | 51 | //usage: "\n -d List directory entries instead of contents" |
52 | //usage: IF_FEATURE_LS_FOLLOWLINKS( | 52 | //usage: IF_FEATURE_LS_FOLLOWLINKS( |
53 | //usage: "\n -L Follow symlinks" | 53 | //usage: "\n -L Follow symlinks" |
54 | //usage: "\n -H Follow symlinks on command line only" | 54 | //usage: "\n -H Follow symlinks on command line" |
55 | //usage: ) | 55 | //usage: ) |
56 | //usage: IF_FEATURE_LS_RECURSIVE( | 56 | //usage: IF_FEATURE_LS_RECURSIVE( |
57 | //usage: "\n -R Recurse" | 57 | //usage: "\n -R Recurse" |
58 | //usage: ) | 58 | //usage: ) |
59 | //usage: IF_FEATURE_LS_FILETYPES( | 59 | //usage: IF_FEATURE_LS_FILETYPES( |
60 | //usage: "\n -p Append / to dir entries" | ||
60 | //usage: "\n -F Append indicator (one of */=@|) to entries" | 61 | //usage: "\n -F Append indicator (one of */=@|) to entries" |
61 | //usage: "\n -p Append indicator (one of /=@|) to entries" | ||
62 | //usage: ) | 62 | //usage: ) |
63 | //usage: "\n -l Long listing format" | 63 | //usage: "\n -l Long listing format" |
64 | //usage: "\n -i List inode numbers" | 64 | //usage: "\n -i List inode numbers" |
@@ -118,11 +118,11 @@ | |||
118 | enum { | 118 | enum { |
119 | TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ | 119 | TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ |
120 | 120 | ||
121 | SPLIT_DIR = 1, | ||
122 | SPLIT_FILE = 0, | 121 | SPLIT_FILE = 0, |
122 | SPLIT_DIR = 1, | ||
123 | SPLIT_SUBDIR = 2, | 123 | SPLIT_SUBDIR = 2, |
124 | 124 | ||
125 | /* Bits in all_fmt: */ | 125 | /* Bits in G.all_fmt: */ |
126 | 126 | ||
127 | /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ | 127 | /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ |
128 | /* what file information will be listed */ | 128 | /* what file information will be listed */ |
@@ -137,9 +137,9 @@ LIST_SIZE = 1 << 7, | |||
137 | LIST_DATE_TIME = 1 << 8, | 137 | LIST_DATE_TIME = 1 << 8, |
138 | LIST_FULLTIME = 1 << 9, | 138 | LIST_FULLTIME = 1 << 9, |
139 | LIST_SYMLINK = 1 << 10, | 139 | LIST_SYMLINK = 1 << 10, |
140 | LIST_FILETYPE = 1 << 11, | 140 | LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */ |
141 | LIST_EXEC = 1 << 12, | 141 | LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */ |
142 | LIST_MASK = (LIST_EXEC << 1) - 1, | 142 | LIST_MASK = (LIST_CLASSIFY << 1) - 1, |
143 | 143 | ||
144 | /* what files will be displayed */ | 144 | /* what files will be displayed */ |
145 | DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ | 145 | DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ |
@@ -288,7 +288,7 @@ static const uint32_t opt_flags[] = { | |||
288 | SORT_VERSION, /* v */ | 288 | SORT_VERSION, /* v */ |
289 | #endif | 289 | #endif |
290 | #if ENABLE_FEATURE_LS_FILETYPES | 290 | #if ENABLE_FEATURE_LS_FILETYPES |
291 | LIST_FILETYPE | LIST_EXEC, /* F */ | 291 | LIST_FILETYPE | LIST_CLASSIFY, /* F */ |
292 | LIST_FILETYPE, /* p */ | 292 | LIST_FILETYPE, /* p */ |
293 | #endif | 293 | #endif |
294 | #if ENABLE_FEATURE_LS_RECURSIVE | 294 | #if ENABLE_FEATURE_LS_RECURSIVE |
@@ -304,25 +304,65 @@ static const uint32_t opt_flags[] = { | |||
304 | 304 | ||
305 | 305 | ||
306 | /* | 306 | /* |
307 | * a directory entry and its stat info are stored here | 307 | * a directory entry and its stat info |
308 | */ | 308 | */ |
309 | struct dnode { | 309 | struct dnode { |
310 | const char *name; /* the dir entry name */ | 310 | const char *name; /* usually basename, but think "ls -l dir/file" */ |
311 | const char *fullname; /* the dir entry name */ | 311 | const char *fullname; /* full name (usable for stat etc) */ |
312 | struct dnode *next; /* point at the next node */ | 312 | struct dnode *dn_next; /* for linked list */ |
313 | smallint fname_allocated; | ||
314 | struct stat dstat; /* the file stat info */ | ||
315 | IF_SELINUX(security_context_t sid;) | 313 | IF_SELINUX(security_context_t sid;) |
314 | smallint fname_allocated; | ||
315 | |||
316 | /* Used to avoid re-doing [l]stat at printout stage | ||
317 | * if we already collected needed data in scan stage: | ||
318 | */ | ||
319 | mode_t dn_mode_lstat; /* obtained with lstat, or 0 */ | ||
320 | mode_t dn_mode_stat; /* obtained with stat, or 0 */ | ||
321 | |||
322 | // struct stat dstat; | ||
323 | // struct stat is huge. We don't need it in full. | ||
324 | // At least we don't need st_dev and st_blksize, | ||
325 | // but there are invisible fields as well | ||
326 | // (such as nanosecond-resolution timespamps) | ||
327 | // and padding, which we also don't want to store. | ||
328 | // We also can pre-parse dev_t dn_rdev (in glibc, it's huge). | ||
329 | // On 32-bit uclibc: dnode size went from 112 to 84 bytes. | ||
330 | // | ||
331 | /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ | ||
332 | mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */ | ||
333 | off_t dn_size; | ||
334 | #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES | ||
335 | time_t dn_atime; | ||
336 | time_t dn_mtime; | ||
337 | time_t dn_ctime; | ||
338 | #endif | ||
339 | ino_t dn_ino; | ||
340 | #if !ENABLE_PLATFORM_MINGW32 | ||
341 | blkcnt_t dn_blocks; | ||
342 | #endif | ||
343 | nlink_t dn_nlink; | ||
344 | uid_t dn_uid; | ||
345 | gid_t dn_gid; | ||
346 | int dn_rdev_maj; | ||
347 | int dn_rdev_min; | ||
348 | // dev_t dn_dev; | ||
349 | // blksize_t dn_blksize; | ||
316 | }; | 350 | }; |
317 | 351 | ||
318 | struct globals { | 352 | struct globals { |
319 | #if ENABLE_FEATURE_LS_COLOR | 353 | #if ENABLE_FEATURE_LS_COLOR |
320 | smallint show_color; | 354 | smallint show_color; |
355 | # define G_show_color (G.show_color) | ||
356 | #else | ||
357 | # define G_show_color 0 | ||
321 | #endif | 358 | #endif |
322 | smallint exit_code; | 359 | smallint exit_code; |
323 | unsigned all_fmt; | 360 | unsigned all_fmt; |
324 | #if ENABLE_FEATURE_AUTOWIDTH | 361 | #if ENABLE_FEATURE_AUTOWIDTH |
325 | unsigned terminal_width; // = TERMINAL_WIDTH; | 362 | unsigned terminal_width; |
363 | # define G_terminal_width (G.terminal_width) | ||
364 | #else | ||
365 | # define G_terminal_width TERMINAL_WIDTH | ||
326 | #endif | 366 | #endif |
327 | #if ENABLE_FEATURE_LS_TIMESTAMPS | 367 | #if ENABLE_FEATURE_LS_TIMESTAMPS |
328 | /* Do time() just once. Saves one syscall per file for "ls -l" */ | 368 | /* Do time() just once. Saves one syscall per file for "ls -l" */ |
@@ -330,74 +370,24 @@ struct globals { | |||
330 | #endif | 370 | #endif |
331 | } FIX_ALIASING; | 371 | } FIX_ALIASING; |
332 | #define G (*(struct globals*)&bb_common_bufsiz1) | 372 | #define G (*(struct globals*)&bb_common_bufsiz1) |
333 | #if ENABLE_FEATURE_LS_COLOR | ||
334 | # define show_color (G.show_color ) | ||
335 | #else | ||
336 | enum { show_color = 0 }; | ||
337 | #endif | ||
338 | #define exit_code (G.exit_code ) | ||
339 | #define all_fmt (G.all_fmt ) | ||
340 | #if ENABLE_FEATURE_AUTOWIDTH | ||
341 | # define terminal_width (G.terminal_width) | ||
342 | #else | ||
343 | enum { | ||
344 | terminal_width = TERMINAL_WIDTH, | ||
345 | }; | ||
346 | #endif | ||
347 | #define current_time_t (G.current_time_t) | ||
348 | #define INIT_G() do { \ | 373 | #define INIT_G() do { \ |
349 | /* we have to zero it out because of NOEXEC */ \ | 374 | /* we have to zero it out because of NOEXEC */ \ |
350 | memset(&G, 0, sizeof(G)); \ | 375 | memset(&G, 0, sizeof(G)); \ |
351 | IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \ | 376 | IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \ |
352 | IF_FEATURE_LS_TIMESTAMPS(time(¤t_time_t);) \ | 377 | IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ |
353 | } while (0) | 378 | } while (0) |
354 | 379 | ||
355 | 380 | ||
356 | static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) | 381 | /*** Output code ***/ |
357 | { | ||
358 | struct stat dstat; | ||
359 | struct dnode *cur; | ||
360 | IF_SELINUX(security_context_t sid = NULL;) | ||
361 | 382 | ||
362 | if ((option_mask32 & OPT_L) || force_follow) { | ||
363 | #if ENABLE_SELINUX | ||
364 | if (is_selinux_enabled()) { | ||
365 | getfilecon(fullname, &sid); | ||
366 | } | ||
367 | #endif | ||
368 | if (stat(fullname, &dstat)) { | ||
369 | bb_simple_perror_msg(fullname); | ||
370 | exit_code = EXIT_FAILURE; | ||
371 | return 0; | ||
372 | } | ||
373 | } else { | ||
374 | #if ENABLE_SELINUX | ||
375 | if (is_selinux_enabled()) { | ||
376 | lgetfilecon(fullname, &sid); | ||
377 | } | ||
378 | #endif | ||
379 | if (lstat(fullname, &dstat)) { | ||
380 | bb_simple_perror_msg(fullname); | ||
381 | exit_code = EXIT_FAILURE; | ||
382 | return 0; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | cur = xmalloc(sizeof(*cur)); | ||
387 | cur->fullname = fullname; | ||
388 | cur->name = name; | ||
389 | cur->dstat = dstat; | ||
390 | IF_SELINUX(cur->sid = sid;) | ||
391 | return cur; | ||
392 | } | ||
393 | 383 | ||
394 | /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket | 384 | /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket |
395 | * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file | 385 | * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file |
396 | * 3/7:multiplexed char/block device) | 386 | * 3/7:multiplexed char/block device) |
397 | * and we use 0 for unknown and 15 for executables (see below) */ | 387 | * and we use 0 for unknown and 15 for executables (see below) */ |
398 | #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) | 388 | #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) |
399 | #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) | 389 | /* un fi chr - dir - blk - file - link - sock - - exe */ |
400 | #define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) | 390 | #define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)]) |
401 | /* 036 black foreground 050 black background | 391 | /* 036 black foreground 050 black background |
402 | 037 red foreground 051 red background | 392 | 037 red foreground 051 red background |
403 | 040 green foreground 052 green background | 393 | 040 green foreground 052 green background |
@@ -408,7 +398,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f | |||
408 | 045 gray foreground 057 white background | 398 | 045 gray foreground 057 white background |
409 | */ | 399 | */ |
410 | #define COLOR(mode) ( \ | 400 | #define COLOR(mode) ( \ |
411 | /*un fi chr dir blk file link sock exe */ \ | 401 | /*un fi chr - dir - blk - file - link - sock - - exe */ \ |
412 | "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ | 402 | "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ |
413 | [TYPEINDEX(mode)]) | 403 | [TYPEINDEX(mode)]) |
414 | /* Select normal (0) [actually "reset all"] or bold (1) | 404 | /* Select normal (0) [actually "reset all"] or bold (1) |
@@ -417,7 +407,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f | |||
417 | * Note: coreutils 6.9 uses inverted red for setuid binaries. | 407 | * Note: coreutils 6.9 uses inverted red for setuid binaries. |
418 | */ | 408 | */ |
419 | #define ATTR(mode) ( \ | 409 | #define ATTR(mode) ( \ |
420 | /*un fi chr dir blk file link sock exe */ \ | 410 | /*un fi chr - dir - blk - file- link- sock- - exe */ \ |
421 | "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ | 411 | "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ |
422 | [TYPEINDEX(mode)]) | 412 | [TYPEINDEX(mode)]) |
423 | 413 | ||
@@ -437,14 +427,14 @@ static char bold(mode_t mode) | |||
437 | } | 427 | } |
438 | #endif | 428 | #endif |
439 | 429 | ||
440 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | 430 | #if ENABLE_FEATURE_LS_FILETYPES |
441 | static char append_char(mode_t mode) | 431 | static char append_char(mode_t mode) |
442 | { | 432 | { |
443 | if (!(all_fmt & LIST_FILETYPE)) | 433 | if (!(G.all_fmt & LIST_FILETYPE)) |
444 | return '\0'; | 434 | return '\0'; |
445 | if (S_ISDIR(mode)) | 435 | if (S_ISDIR(mode)) |
446 | return '/'; | 436 | return '/'; |
447 | if (!(all_fmt & LIST_EXEC)) | 437 | if (!(G.all_fmt & LIST_CLASSIFY)) |
448 | return '\0'; | 438 | return '\0'; |
449 | if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) | 439 | if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) |
450 | return '*'; | 440 | return '*'; |
@@ -452,149 +442,6 @@ static char append_char(mode_t mode) | |||
452 | } | 442 | } |
453 | #endif | 443 | #endif |
454 | 444 | ||
455 | static unsigned count_dirs(struct dnode **dn, int which) | ||
456 | { | ||
457 | unsigned dirs, all; | ||
458 | |||
459 | if (!dn) | ||
460 | return 0; | ||
461 | |||
462 | dirs = all = 0; | ||
463 | for (; *dn; dn++) { | ||
464 | const char *name; | ||
465 | |||
466 | all++; | ||
467 | if (!S_ISDIR((*dn)->dstat.st_mode)) | ||
468 | continue; | ||
469 | name = (*dn)->name; | ||
470 | if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ | ||
471 | /* or if it's not . or .. */ | ||
472 | || name[0] != '.' || (name[1] && (name[1] != '.' || name[2])) | ||
473 | ) { | ||
474 | dirs++; | ||
475 | } | ||
476 | } | ||
477 | return which != SPLIT_FILE ? dirs : all - dirs; | ||
478 | } | ||
479 | |||
480 | /* get memory to hold an array of pointers */ | ||
481 | static struct dnode **dnalloc(unsigned num) | ||
482 | { | ||
483 | if (num < 1) | ||
484 | return NULL; | ||
485 | |||
486 | num++; /* so that we have terminating NULL */ | ||
487 | return xzalloc(num * sizeof(struct dnode *)); | ||
488 | } | ||
489 | |||
490 | #if ENABLE_FEATURE_LS_RECURSIVE | ||
491 | static void dfree(struct dnode **dnp) | ||
492 | { | ||
493 | unsigned i; | ||
494 | |||
495 | if (dnp == NULL) | ||
496 | return; | ||
497 | |||
498 | for (i = 0; dnp[i]; i++) { | ||
499 | struct dnode *cur = dnp[i]; | ||
500 | if (cur->fname_allocated) | ||
501 | free((char*)cur->fullname); | ||
502 | free(cur); | ||
503 | } | ||
504 | free(dnp); | ||
505 | } | ||
506 | #else | ||
507 | #define dfree(...) ((void)0) | ||
508 | #endif | ||
509 | |||
510 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ | ||
511 | static struct dnode **splitdnarray(struct dnode **dn, int which) | ||
512 | { | ||
513 | unsigned dncnt, d; | ||
514 | struct dnode **dnp; | ||
515 | |||
516 | if (dn == NULL) | ||
517 | return NULL; | ||
518 | |||
519 | /* count how many dirs or files there are */ | ||
520 | dncnt = count_dirs(dn, which); | ||
521 | |||
522 | /* allocate a file array and a dir array */ | ||
523 | dnp = dnalloc(dncnt); | ||
524 | |||
525 | /* copy the entrys into the file or dir array */ | ||
526 | for (d = 0; *dn; dn++) { | ||
527 | if (S_ISDIR((*dn)->dstat.st_mode)) { | ||
528 | const char *name; | ||
529 | |||
530 | if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) | ||
531 | continue; | ||
532 | name = (*dn)->name; | ||
533 | if ((which & SPLIT_DIR) | ||
534 | || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) | ||
535 | ) { | ||
536 | dnp[d++] = *dn; | ||
537 | } | ||
538 | } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { | ||
539 | dnp[d++] = *dn; | ||
540 | } | ||
541 | } | ||
542 | return dnp; | ||
543 | } | ||
544 | |||
545 | #if ENABLE_FEATURE_LS_SORTFILES | ||
546 | static int sortcmp(const void *a, const void *b) | ||
547 | { | ||
548 | struct dnode *d1 = *(struct dnode **)a; | ||
549 | struct dnode *d2 = *(struct dnode **)b; | ||
550 | unsigned sort_opts = all_fmt & SORT_MASK; | ||
551 | off_t dif; | ||
552 | |||
553 | dif = 0; /* assume SORT_NAME */ | ||
554 | // TODO: use pre-initialized function pointer | ||
555 | // instead of branch forest | ||
556 | if (sort_opts == SORT_SIZE) { | ||
557 | dif = (d2->dstat.st_size - d1->dstat.st_size); | ||
558 | } else if (sort_opts == SORT_ATIME) { | ||
559 | dif = (d2->dstat.st_atime - d1->dstat.st_atime); | ||
560 | } else if (sort_opts == SORT_CTIME) { | ||
561 | dif = (d2->dstat.st_ctime - d1->dstat.st_ctime); | ||
562 | } else if (sort_opts == SORT_MTIME) { | ||
563 | dif = (d2->dstat.st_mtime - d1->dstat.st_mtime); | ||
564 | } else if (sort_opts == SORT_DIR) { | ||
565 | dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode); | ||
566 | /* } else if (sort_opts == SORT_VERSION) { */ | ||
567 | /* } else if (sort_opts == SORT_EXT) { */ | ||
568 | } | ||
569 | if (dif == 0) { | ||
570 | /* sort by name, or tie_breaker for other sorts */ | ||
571 | if (ENABLE_LOCALE_SUPPORT) | ||
572 | dif = strcoll(d1->name, d2->name); | ||
573 | else | ||
574 | dif = strcmp(d1->name, d2->name); | ||
575 | } | ||
576 | |||
577 | /* Make dif fit into an int */ | ||
578 | if (sizeof(dif) > sizeof(int)) { | ||
579 | enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; | ||
580 | /* shift leaving only "int" worth of bits */ | ||
581 | if (dif != 0) { | ||
582 | dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); | ||
583 | } | ||
584 | } | ||
585 | |||
586 | return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; | ||
587 | } | ||
588 | |||
589 | static void dnsort(struct dnode **dn, int size) | ||
590 | { | ||
591 | qsort(dn, size, sizeof(*dn), sortcmp); | ||
592 | } | ||
593 | #else | ||
594 | #define dnsort(dn, size) ((void)0) | ||
595 | #endif | ||
596 | |||
597 | |||
598 | static unsigned calc_name_len(const char *name) | 445 | static unsigned calc_name_len(const char *name) |
599 | { | 446 | { |
600 | unsigned len; | 447 | unsigned len; |
@@ -617,7 +464,6 @@ static unsigned calc_name_len(const char *name) | |||
617 | return len; | 464 | return len; |
618 | } | 465 | } |
619 | 466 | ||
620 | |||
621 | /* Return the number of used columns. | 467 | /* Return the number of used columns. |
622 | * Note that only STYLE_COLUMNAR uses return value. | 468 | * Note that only STYLE_COLUMNAR uses return value. |
623 | * STYLE_SINGLE and STYLE_LONG don't care. | 469 | * STYLE_SINGLE and STYLE_LONG don't care. |
@@ -656,98 +502,94 @@ static unsigned print_name(const char *name) | |||
656 | * Note that only STYLE_COLUMNAR uses return value, | 502 | * Note that only STYLE_COLUMNAR uses return value, |
657 | * STYLE_SINGLE and STYLE_LONG don't care. | 503 | * STYLE_SINGLE and STYLE_LONG don't care. |
658 | */ | 504 | */ |
659 | static NOINLINE unsigned list_single(const struct dnode *dn) | 505 | static NOINLINE unsigned display_single(const struct dnode *dn) |
660 | { | 506 | { |
661 | unsigned column = 0; | 507 | unsigned column = 0; |
662 | char *lpath = lpath; /* for compiler */ | 508 | char *lpath; |
663 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | 509 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR |
664 | struct stat info; | 510 | struct stat statbuf; |
665 | char append; | 511 | char append; |
666 | #endif | 512 | #endif |
667 | 513 | ||
668 | /* Never happens: | ||
669 | if (dn->fullname == NULL) | ||
670 | return 0; | ||
671 | */ | ||
672 | |||
673 | #if ENABLE_FEATURE_LS_FILETYPES | 514 | #if ENABLE_FEATURE_LS_FILETYPES |
674 | append = append_char(dn->dstat.st_mode); | 515 | append = append_char(dn->dn_mode); |
675 | #endif | 516 | #endif |
676 | 517 | ||
677 | /* Do readlink early, so that if it fails, error message | 518 | /* Do readlink early, so that if it fails, error message |
678 | * does not appear *inside* the "ls -l" line */ | 519 | * does not appear *inside* the "ls -l" line */ |
679 | if (all_fmt & LIST_SYMLINK) | 520 | lpath = NULL; |
680 | if (S_ISLNK(dn->dstat.st_mode)) | 521 | if (G.all_fmt & LIST_SYMLINK) |
522 | if (S_ISLNK(dn->dn_mode)) | ||
681 | lpath = xmalloc_readlink_or_warn(dn->fullname); | 523 | lpath = xmalloc_readlink_or_warn(dn->fullname); |
682 | 524 | ||
683 | if (all_fmt & LIST_INO) | 525 | if (G.all_fmt & LIST_INO) |
684 | column += printf("%7llu ", (long long) dn->dstat.st_ino); | 526 | column += printf("%7llu ", (long long) dn->dn_ino); |
685 | //TODO: -h should affect -s too: | 527 | //TODO: -h should affect -s too: |
686 | if (all_fmt & LIST_BLOCKS) | 528 | if (G.all_fmt & LIST_BLOCKS) |
687 | #if ENABLE_PLATFORM_MINGW32 | 529 | #if ENABLE_PLATFORM_MINGW32 |
688 | /* MinGW does not have st_blocks */ | 530 | /* MinGW does not have st_blocks */ |
689 | column += printf("%6"OFF_FMT"u ", (off_t)0); | 531 | column += printf("%6"OFF_FMT"u ", (off_t)0); |
690 | #else | 532 | #else |
691 | column += printf("%6"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1)); | 533 | column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); |
692 | #endif | 534 | #endif |
693 | if (all_fmt & LIST_MODEBITS) | 535 | if (G.all_fmt & LIST_MODEBITS) |
694 | column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); | 536 | column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode)); |
695 | if (all_fmt & LIST_NLINKS) | 537 | if (G.all_fmt & LIST_NLINKS) |
696 | column += printf("%4lu ", (long) dn->dstat.st_nlink); | 538 | column += printf("%4lu ", (long) dn->dn_nlink); |
697 | if (all_fmt & LIST_ID_NUMERIC) { | 539 | if (G.all_fmt & LIST_ID_NUMERIC) { |
698 | if (option_mask32 & OPT_g) | 540 | if (option_mask32 & OPT_g) |
699 | column += printf("%-8u ", (int) dn->dstat.st_gid); | 541 | column += printf("%-8u ", (int) dn->dn_gid); |
700 | else | 542 | else |
701 | column += printf("%-8u %-8u ", | 543 | column += printf("%-8u %-8u ", |
702 | (int) dn->dstat.st_uid, | 544 | (int) dn->dn_uid, |
703 | (int) dn->dstat.st_gid); | 545 | (int) dn->dn_gid); |
704 | } | 546 | } |
705 | #if ENABLE_FEATURE_LS_USERNAME | 547 | #if ENABLE_FEATURE_LS_USERNAME |
706 | else if (all_fmt & LIST_ID_NAME) { | 548 | else if (G.all_fmt & LIST_ID_NAME) { |
707 | if (option_mask32 & OPT_g) { | 549 | if (option_mask32 & OPT_g) { |
708 | column += printf("%-8.8s ", | 550 | column += printf("%-8.8s ", |
709 | get_cached_groupname(dn->dstat.st_gid)); | 551 | get_cached_groupname(dn->dn_gid)); |
710 | } else { | 552 | } else { |
711 | column += printf("%-8.8s %-8.8s ", | 553 | column += printf("%-8.8s %-8.8s ", |
712 | get_cached_username(dn->dstat.st_uid), | 554 | get_cached_username(dn->dn_uid), |
713 | get_cached_groupname(dn->dstat.st_gid)); | 555 | get_cached_groupname(dn->dn_gid)); |
714 | } | 556 | } |
715 | } | 557 | } |
716 | #endif | 558 | #endif |
717 | if (all_fmt & LIST_SIZE) { | 559 | if (G.all_fmt & LIST_SIZE) { |
718 | if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { | 560 | if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) { |
719 | column += printf("%4u, %3u ", | 561 | column += printf("%4u, %3u ", |
720 | (int) major(dn->dstat.st_rdev), | 562 | dn->dn_rdev_maj, |
721 | (int) minor(dn->dstat.st_rdev)); | 563 | dn->dn_rdev_min); |
722 | } else { | 564 | } else { |
723 | if (option_mask32 & OPT_h) { | 565 | if (option_mask32 & OPT_h) { |
724 | column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", | 566 | column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", |
725 | /* print st_size, show one fractional, use suffixes */ | 567 | /* print size, show one fractional, use suffixes */ |
726 | make_human_readable_str(dn->dstat.st_size, 1, 0) | 568 | make_human_readable_str(dn->dn_size, 1, 0) |
727 | ); | 569 | ); |
728 | } else { | 570 | } else { |
729 | column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); | 571 | column += printf("%9"OFF_FMT"u ", dn->dn_size); |
730 | } | 572 | } |
731 | } | 573 | } |
732 | } | 574 | } |
733 | #if ENABLE_FEATURE_LS_TIMESTAMPS | 575 | #if ENABLE_FEATURE_LS_TIMESTAMPS |
734 | if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { | 576 | if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { |
735 | char *filetime; | 577 | char *filetime; |
736 | time_t ttime = dn->dstat.st_mtime; | 578 | time_t ttime = dn->dn_mtime; |
737 | if (all_fmt & TIME_ACCESS) | 579 | if (G.all_fmt & TIME_ACCESS) |
738 | ttime = dn->dstat.st_atime; | 580 | ttime = dn->dn_atime; |
739 | if (all_fmt & TIME_CHANGE) | 581 | if (G.all_fmt & TIME_CHANGE) |
740 | ttime = dn->dstat.st_ctime; | 582 | ttime = dn->dn_ctime; |
741 | filetime = ctime(&ttime); | 583 | filetime = ctime(&ttime); |
742 | /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ | 584 | /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ |
743 | if (all_fmt & LIST_FULLTIME) { /* -e */ | 585 | if (G.all_fmt & LIST_FULLTIME) { /* -e */ |
744 | /* Note: coreutils 8.4 ls --full-time prints: | 586 | /* Note: coreutils 8.4 ls --full-time prints: |
745 | * 2009-07-13 17:49:27.000000000 +0200 | 587 | * 2009-07-13 17:49:27.000000000 +0200 |
746 | */ | 588 | */ |
747 | column += printf("%.24s ", filetime); | 589 | column += printf("%.24s ", filetime); |
748 | } else { /* LIST_DATE_TIME */ | 590 | } else { /* LIST_DATE_TIME */ |
749 | /* current_time_t ~== time(NULL) */ | 591 | /* G.current_time_t ~== time(NULL) */ |
750 | time_t age = current_time_t - ttime; | 592 | time_t age = G.current_time_t - ttime; |
751 | printf("%.6s ", filetime + 4); /* "Jun 30" */ | 593 | printf("%.6s ", filetime + 4); /* "Jun 30" */ |
752 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { | 594 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { |
753 | /* hh:mm if less than 6 months old */ | 595 | /* hh:mm if less than 6 months old */ |
@@ -760,51 +602,52 @@ static NOINLINE unsigned list_single(const struct dnode *dn) | |||
760 | } | 602 | } |
761 | #endif | 603 | #endif |
762 | #if ENABLE_SELINUX | 604 | #if ENABLE_SELINUX |
763 | if (all_fmt & LIST_CONTEXT) { | 605 | if (G.all_fmt & LIST_CONTEXT) { |
764 | column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); | 606 | column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); |
765 | freecon(dn->sid); | 607 | freecon(dn->sid); |
766 | } | 608 | } |
767 | #endif | 609 | #endif |
768 | 610 | ||
769 | #if ENABLE_FEATURE_LS_COLOR | 611 | #if ENABLE_FEATURE_LS_COLOR |
770 | if (show_color) { | 612 | if (G_show_color) { |
771 | info.st_mode = 0; /* for fgcolor() */ | 613 | mode_t mode = dn->dn_mode_lstat; |
772 | lstat(dn->fullname, &info); | 614 | if (!mode) |
773 | printf("\033[%u;%um", bold(info.st_mode), | 615 | if (lstat(dn->fullname, &statbuf) == 0) |
774 | fgcolor(info.st_mode)); | 616 | mode = statbuf.st_mode; |
617 | printf("\033[%u;%um", bold(mode), fgcolor(mode)); | ||
775 | } | 618 | } |
776 | #endif | 619 | #endif |
777 | column += print_name(dn->name); | 620 | column += print_name(dn->name); |
778 | if (show_color) { | 621 | if (G_show_color) { |
779 | printf("\033[0m"); | 622 | printf("\033[0m"); |
780 | } | 623 | } |
781 | 624 | ||
782 | if (all_fmt & LIST_SYMLINK) { | 625 | if (lpath) { |
783 | if (S_ISLNK(dn->dstat.st_mode) && lpath) { | 626 | printf(" -> "); |
784 | printf(" -> "); | ||
785 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | 627 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR |
786 | #if ENABLE_FEATURE_LS_COLOR | 628 | if ((G.all_fmt & LIST_FILETYPE) || G_show_color) { |
787 | info.st_mode = 0; /* for fgcolor() */ | 629 | mode_t mode = dn->dn_mode_stat; |
788 | #endif | 630 | if (!mode) |
789 | if (stat(dn->fullname, &info) == 0) { | 631 | if (stat(dn->fullname, &statbuf) == 0) |
790 | append = append_char(info.st_mode); | 632 | mode = statbuf.st_mode; |
791 | } | 633 | # if ENABLE_FEATURE_LS_FILETYPES |
792 | #endif | 634 | append = append_char(mode); |
793 | #if ENABLE_FEATURE_LS_COLOR | 635 | # endif |
794 | if (show_color) { | 636 | # if ENABLE_FEATURE_LS_COLOR |
795 | printf("\033[%u;%um", bold(info.st_mode), | 637 | if (G_show_color) { |
796 | fgcolor(info.st_mode)); | 638 | printf("\033[%u;%um", bold(mode), fgcolor(mode)); |
797 | } | 639 | } |
640 | # endif | ||
641 | } | ||
798 | #endif | 642 | #endif |
799 | column += print_name(lpath) + 4; | 643 | column += print_name(lpath) + 4; |
800 | if (show_color) { | 644 | free(lpath); |
801 | printf("\033[0m"); | 645 | if (G_show_color) { |
802 | } | 646 | printf("\033[0m"); |
803 | free(lpath); | ||
804 | } | 647 | } |
805 | } | 648 | } |
806 | #if ENABLE_FEATURE_LS_FILETYPES | 649 | #if ENABLE_FEATURE_LS_FILETYPES |
807 | if (all_fmt & LIST_FILETYPE) { | 650 | if (G.all_fmt & LIST_FILETYPE) { |
808 | if (append) { | 651 | if (append) { |
809 | putchar(append); | 652 | putchar(append); |
810 | column++; | 653 | column++; |
@@ -815,14 +658,14 @@ static NOINLINE unsigned list_single(const struct dnode *dn) | |||
815 | return column; | 658 | return column; |
816 | } | 659 | } |
817 | 660 | ||
818 | static void showfiles(struct dnode **dn, unsigned nfiles) | 661 | static void display_files(struct dnode **dn, unsigned nfiles) |
819 | { | 662 | { |
820 | unsigned i, ncols, nrows, row, nc; | 663 | unsigned i, ncols, nrows, row, nc; |
821 | unsigned column; | 664 | unsigned column; |
822 | unsigned nexttab; | 665 | unsigned nexttab; |
823 | unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ | 666 | unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ |
824 | 667 | ||
825 | if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ | 668 | if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ |
826 | ncols = 1; | 669 | ncols = 1; |
827 | } else { | 670 | } else { |
828 | /* find the longest file name, use that as the column width */ | 671 | /* find the longest file name, use that as the column width */ |
@@ -832,10 +675,10 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
832 | column_width = len; | 675 | column_width = len; |
833 | } | 676 | } |
834 | column_width += 1 + | 677 | column_width += 1 + |
835 | IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) | 678 | IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + ) |
836 | ((all_fmt & LIST_INO) ? 8 : 0) + | 679 | ((G.all_fmt & LIST_INO) ? 8 : 0) + |
837 | ((all_fmt & LIST_BLOCKS) ? 5 : 0); | 680 | ((G.all_fmt & LIST_BLOCKS) ? 5 : 0); |
838 | ncols = (int) (terminal_width / column_width); | 681 | ncols = (unsigned)G_terminal_width / column_width; |
839 | } | 682 | } |
840 | 683 | ||
841 | if (ncols > 1) { | 684 | if (ncols > 1) { |
@@ -852,7 +695,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
852 | for (row = 0; row < nrows; row++) { | 695 | for (row = 0; row < nrows; row++) { |
853 | for (nc = 0; nc < ncols; nc++) { | 696 | for (nc = 0; nc < ncols; nc++) { |
854 | /* reach into the array based on the column and row */ | 697 | /* reach into the array based on the column and row */ |
855 | if (all_fmt & DISP_ROWS) | 698 | if (G.all_fmt & DISP_ROWS) |
856 | i = (row * ncols) + nc; /* display across row */ | 699 | i = (row * ncols) + nc; /* display across row */ |
857 | else | 700 | else |
858 | i = (nc * nrows) + row; /* display by column */ | 701 | i = (nc * nrows) + row; /* display by column */ |
@@ -863,7 +706,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
863 | column += nexttab + 1; | 706 | column += nexttab + 1; |
864 | } | 707 | } |
865 | nexttab = column + column_width; | 708 | nexttab = column + column_width; |
866 | column += list_single(dn[i]); | 709 | column += display_single(dn[i]); |
867 | } | 710 | } |
868 | } | 711 | } |
869 | putchar('\n'); | 712 | putchar('\n'); |
@@ -872,102 +715,233 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
872 | } | 715 | } |
873 | 716 | ||
874 | 717 | ||
875 | #if !ENABLE_PLATFORM_MINGW32 | 718 | /*** Dir scanning code ***/ |
876 | #if ENABLE_DESKTOP | 719 | |
877 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html | 720 | static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) |
878 | * If any of the -l, -n, -s options is specified, each list | ||
879 | * of files within the directory shall be preceded by a | ||
880 | * status line indicating the number of file system blocks | ||
881 | * occupied by files in the directory in 512-byte units if | ||
882 | * the -k option is not specified, or 1024-byte units if the | ||
883 | * -k option is specified, rounded up to the next integral | ||
884 | * number of units. | ||
885 | */ | ||
886 | /* by Jorgen Overgaard (jorgen AT antistaten.se) */ | ||
887 | static off_t calculate_blocks(struct dnode **dn) | ||
888 | { | 721 | { |
889 | uoff_t blocks = 1; | 722 | struct stat statbuf; |
890 | if (dn) { | 723 | struct dnode *cur; |
891 | while (*dn) { | 724 | |
892 | /* st_blocks is in 512 byte blocks */ | 725 | cur = xzalloc(sizeof(*cur)); |
893 | blocks += (*dn)->dstat.st_blocks; | 726 | cur->fullname = fullname; |
894 | dn++; | 727 | cur->name = name; |
728 | |||
729 | if ((option_mask32 & OPT_L) || force_follow) { | ||
730 | #if ENABLE_SELINUX | ||
731 | if (is_selinux_enabled()) { | ||
732 | getfilecon(fullname, &cur->sid); | ||
733 | } | ||
734 | #endif | ||
735 | if (stat(fullname, &statbuf)) { | ||
736 | bb_simple_perror_msg(fullname); | ||
737 | G.exit_code = EXIT_FAILURE; | ||
738 | free(cur); | ||
739 | return NULL; | ||
740 | } | ||
741 | cur->dn_mode_stat = statbuf.st_mode; | ||
742 | } else { | ||
743 | #if ENABLE_SELINUX | ||
744 | if (is_selinux_enabled()) { | ||
745 | lgetfilecon(fullname, &cur->sid); | ||
746 | } | ||
747 | #endif | ||
748 | if (lstat(fullname, &statbuf)) { | ||
749 | bb_simple_perror_msg(fullname); | ||
750 | G.exit_code = EXIT_FAILURE; | ||
751 | free(cur); | ||
752 | return NULL; | ||
895 | } | 753 | } |
754 | cur->dn_mode_lstat = statbuf.st_mode; | ||
896 | } | 755 | } |
897 | 756 | ||
898 | /* Even though standard says use 512 byte blocks, coreutils use 1k */ | 757 | /* cur->dstat = statbuf: */ |
899 | /* Actually, we round up by calculating (blocks + 1) / 2, | 758 | cur->dn_mode = statbuf.st_mode ; |
900 | * "+ 1" was done when we initialized blocks to 1 */ | 759 | cur->dn_size = statbuf.st_size ; |
901 | return blocks >> 1; | 760 | #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES |
902 | } | 761 | cur->dn_atime = statbuf.st_atime ; |
762 | cur->dn_mtime = statbuf.st_mtime ; | ||
763 | cur->dn_ctime = statbuf.st_ctime ; | ||
903 | #endif | 764 | #endif |
765 | cur->dn_ino = statbuf.st_ino ; | ||
766 | #if !ENABLE_PLATFORM_MINGW32 | ||
767 | cur->dn_blocks = statbuf.st_blocks; | ||
904 | #endif | 768 | #endif |
769 | cur->dn_nlink = statbuf.st_nlink ; | ||
770 | cur->dn_uid = statbuf.st_uid ; | ||
771 | cur->dn_gid = statbuf.st_gid ; | ||
772 | cur->dn_rdev_maj = major(statbuf.st_rdev); | ||
773 | cur->dn_rdev_min = minor(statbuf.st_rdev); | ||
905 | 774 | ||
775 | return cur; | ||
776 | } | ||
906 | 777 | ||
907 | static struct dnode **list_dir(const char *, unsigned *); | 778 | static unsigned count_dirs(struct dnode **dn, int which) |
908 | |||
909 | static void showdirs(struct dnode **dn, int first) | ||
910 | { | 779 | { |
911 | unsigned nfiles; | 780 | unsigned dirs, all; |
912 | unsigned dndirs; | ||
913 | struct dnode **subdnp; | ||
914 | struct dnode **dnd; | ||
915 | 781 | ||
782 | if (!dn) | ||
783 | return 0; | ||
784 | |||
785 | dirs = all = 0; | ||
916 | for (; *dn; dn++) { | 786 | for (; *dn; dn++) { |
917 | if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { | 787 | const char *name; |
918 | if (!first) | 788 | |
919 | bb_putchar('\n'); | 789 | all++; |
920 | first = 0; | 790 | if (!S_ISDIR((*dn)->dn_mode)) |
921 | printf("%s:\n", (*dn)->fullname); | 791 | continue; |
792 | |||
793 | name = (*dn)->name; | ||
794 | if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ | ||
795 | /* or if it's not . or .. */ | ||
796 | || name[0] != '.' | ||
797 | || (name[1] && (name[1] != '.' || name[2])) | ||
798 | ) { | ||
799 | dirs++; | ||
922 | } | 800 | } |
923 | subdnp = list_dir((*dn)->fullname, &nfiles); | 801 | } |
924 | #if !ENABLE_PLATFORM_MINGW32 | 802 | return which != SPLIT_FILE ? dirs : all - dirs; |
925 | #if ENABLE_DESKTOP | 803 | } |
926 | if ((all_fmt & STYLE_MASK) == STYLE_LONG) | 804 | |
927 | printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); | 805 | /* get memory to hold an array of pointers */ |
928 | #endif | 806 | static struct dnode **dnalloc(unsigned num) |
807 | { | ||
808 | if (num < 1) | ||
809 | return NULL; | ||
810 | |||
811 | num++; /* so that we have terminating NULL */ | ||
812 | return xzalloc(num * sizeof(struct dnode *)); | ||
813 | } | ||
814 | |||
815 | #if ENABLE_FEATURE_LS_RECURSIVE | ||
816 | static void dfree(struct dnode **dnp) | ||
817 | { | ||
818 | unsigned i; | ||
819 | |||
820 | if (dnp == NULL) | ||
821 | return; | ||
822 | |||
823 | for (i = 0; dnp[i]; i++) { | ||
824 | struct dnode *cur = dnp[i]; | ||
825 | if (cur->fname_allocated) | ||
826 | free((char*)cur->fullname); | ||
827 | free(cur); | ||
828 | } | ||
829 | free(dnp); | ||
830 | } | ||
831 | #else | ||
832 | #define dfree(...) ((void)0) | ||
929 | #endif | 833 | #endif |
930 | if (nfiles > 0) { | 834 | |
931 | /* list all files at this level */ | 835 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ |
932 | dnsort(subdnp, nfiles); | 836 | static struct dnode **splitdnarray(struct dnode **dn, int which) |
933 | showfiles(subdnp, nfiles); | 837 | { |
934 | if (ENABLE_FEATURE_LS_RECURSIVE | 838 | unsigned dncnt, d; |
935 | && (all_fmt & DISP_RECURSIVE) | 839 | struct dnode **dnp; |
840 | |||
841 | if (dn == NULL) | ||
842 | return NULL; | ||
843 | |||
844 | /* count how many dirs or files there are */ | ||
845 | dncnt = count_dirs(dn, which); | ||
846 | |||
847 | /* allocate a file array and a dir array */ | ||
848 | dnp = dnalloc(dncnt); | ||
849 | |||
850 | /* copy the entrys into the file or dir array */ | ||
851 | for (d = 0; *dn; dn++) { | ||
852 | if (S_ISDIR((*dn)->dn_mode)) { | ||
853 | const char *name; | ||
854 | |||
855 | if (which == SPLIT_FILE) | ||
856 | continue; | ||
857 | |||
858 | name = (*dn)->name; | ||
859 | if ((which & SPLIT_DIR) /* any dir... */ | ||
860 | /* ... or not . or .. */ | ||
861 | || name[0] != '.' | ||
862 | || (name[1] && (name[1] != '.' || name[2])) | ||
936 | ) { | 863 | ) { |
937 | /* recursive - list the sub-dirs */ | 864 | dnp[d++] = *dn; |
938 | dnd = splitdnarray(subdnp, SPLIT_SUBDIR); | ||
939 | dndirs = count_dirs(subdnp, SPLIT_SUBDIR); | ||
940 | if (dndirs > 0) { | ||
941 | dnsort(dnd, dndirs); | ||
942 | showdirs(dnd, 0); | ||
943 | /* free the array of dnode pointers to the dirs */ | ||
944 | free(dnd); | ||
945 | } | ||
946 | } | 865 | } |
947 | /* free the dnodes and the fullname mem */ | 866 | } else |
948 | dfree(subdnp); | 867 | if (which == SPLIT_FILE) { |
868 | dnp[d++] = *dn; | ||
949 | } | 869 | } |
950 | } | 870 | } |
871 | return dnp; | ||
951 | } | 872 | } |
952 | 873 | ||
874 | #if ENABLE_FEATURE_LS_SORTFILES | ||
875 | static int sortcmp(const void *a, const void *b) | ||
876 | { | ||
877 | struct dnode *d1 = *(struct dnode **)a; | ||
878 | struct dnode *d2 = *(struct dnode **)b; | ||
879 | unsigned sort_opts = G.all_fmt & SORT_MASK; | ||
880 | off_t dif; | ||
881 | |||
882 | dif = 0; /* assume SORT_NAME */ | ||
883 | // TODO: use pre-initialized function pointer | ||
884 | // instead of branch forest | ||
885 | if (sort_opts == SORT_SIZE) { | ||
886 | dif = (d2->dn_size - d1->dn_size); | ||
887 | } else if (sort_opts == SORT_ATIME) { | ||
888 | dif = (d2->dn_atime - d1->dn_atime); | ||
889 | } else if (sort_opts == SORT_CTIME) { | ||
890 | dif = (d2->dn_ctime - d1->dn_ctime); | ||
891 | } else if (sort_opts == SORT_MTIME) { | ||
892 | dif = (d2->dn_mtime - d1->dn_mtime); | ||
893 | } else if (sort_opts == SORT_DIR) { | ||
894 | dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); | ||
895 | /* } else if (sort_opts == SORT_VERSION) { */ | ||
896 | /* } else if (sort_opts == SORT_EXT) { */ | ||
897 | } | ||
898 | if (dif == 0) { | ||
899 | /* sort by name, or tie_breaker for other sorts */ | ||
900 | if (ENABLE_LOCALE_SUPPORT) | ||
901 | dif = strcoll(d1->name, d2->name); | ||
902 | else | ||
903 | dif = strcmp(d1->name, d2->name); | ||
904 | } | ||
905 | |||
906 | /* Make dif fit into an int */ | ||
907 | if (sizeof(dif) > sizeof(int)) { | ||
908 | enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; | ||
909 | /* shift leaving only "int" worth of bits */ | ||
910 | if (dif != 0) { | ||
911 | dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); | ||
912 | } | ||
913 | } | ||
914 | |||
915 | return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; | ||
916 | } | ||
917 | |||
918 | static void dnsort(struct dnode **dn, int size) | ||
919 | { | ||
920 | qsort(dn, size, sizeof(*dn), sortcmp); | ||
921 | } | ||
922 | |||
923 | static void sort_and_display_files(struct dnode **dn, unsigned nfiles) | ||
924 | { | ||
925 | dnsort(dn, nfiles); | ||
926 | display_files(dn, nfiles); | ||
927 | } | ||
928 | #else | ||
929 | # define dnsort(dn, size) ((void)0) | ||
930 | # define sort_and_display_files(dn, nfiles) display_files(dn, nfiles) | ||
931 | #endif | ||
953 | 932 | ||
954 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ | 933 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ |
955 | static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | 934 | static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) |
956 | { | 935 | { |
957 | struct dnode *dn, *cur, **dnp; | 936 | struct dnode *dn, *cur, **dnp; |
958 | struct dirent *entry; | 937 | struct dirent *entry; |
959 | DIR *dir; | 938 | DIR *dir; |
960 | unsigned i, nfiles; | 939 | unsigned i, nfiles; |
961 | 940 | ||
962 | /* Never happens: | ||
963 | if (path == NULL) | ||
964 | return NULL; | ||
965 | */ | ||
966 | |||
967 | *nfiles_p = 0; | 941 | *nfiles_p = 0; |
968 | dir = warn_opendir(path); | 942 | dir = warn_opendir(path); |
969 | if (dir == NULL) { | 943 | if (dir == NULL) { |
970 | exit_code = EXIT_FAILURE; | 944 | G.exit_code = EXIT_FAILURE; |
971 | return NULL; /* could not open the dir */ | 945 | return NULL; /* could not open the dir */ |
972 | } | 946 | } |
973 | dn = NULL; | 947 | dn = NULL; |
@@ -978,11 +952,11 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
978 | /* are we going to list the file- it may be . or .. or a hidden file */ | 952 | /* are we going to list the file- it may be . or .. or a hidden file */ |
979 | if (entry->d_name[0] == '.') { | 953 | if (entry->d_name[0] == '.') { |
980 | if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) | 954 | if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) |
981 | && !(all_fmt & DISP_DOT) | 955 | && !(G.all_fmt & DISP_DOT) |
982 | ) { | 956 | ) { |
983 | continue; | 957 | continue; |
984 | } | 958 | } |
985 | if (!(all_fmt & DISP_HIDDEN)) | 959 | if (!(G.all_fmt & DISP_HIDDEN)) |
986 | continue; | 960 | continue; |
987 | } | 961 | } |
988 | fullname = concat_path_file(path, entry->d_name); | 962 | fullname = concat_path_file(path, entry->d_name); |
@@ -992,7 +966,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
992 | continue; | 966 | continue; |
993 | } | 967 | } |
994 | cur->fname_allocated = 1; | 968 | cur->fname_allocated = 1; |
995 | cur->next = dn; | 969 | cur->dn_next = dn; |
996 | dn = cur; | 970 | dn = cur; |
997 | nfiles++; | 971 | nfiles++; |
998 | } | 972 | } |
@@ -1008,7 +982,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
1008 | dnp = dnalloc(nfiles); | 982 | dnp = dnalloc(nfiles); |
1009 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { | 983 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { |
1010 | dnp[i] = dn; /* save pointer to node in array */ | 984 | dnp[i] = dn; /* save pointer to node in array */ |
1011 | dn = dn->next; | 985 | dn = dn->dn_next; |
1012 | if (!dn) | 986 | if (!dn) |
1013 | break; | 987 | break; |
1014 | } | 988 | } |
@@ -1016,6 +990,77 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
1016 | return dnp; | 990 | return dnp; |
1017 | } | 991 | } |
1018 | 992 | ||
993 | #if ENABLE_DESKTOP && !ENABLE_PLATFORM_MINGW32 | ||
994 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html | ||
995 | * If any of the -l, -n, -s options is specified, each list | ||
996 | * of files within the directory shall be preceded by a | ||
997 | * status line indicating the number of file system blocks | ||
998 | * occupied by files in the directory in 512-byte units if | ||
999 | * the -k option is not specified, or 1024-byte units if the | ||
1000 | * -k option is specified, rounded up to the next integral | ||
1001 | * number of units. | ||
1002 | */ | ||
1003 | /* by Jorgen Overgaard (jorgen AT antistaten.se) */ | ||
1004 | static off_t calculate_blocks(struct dnode **dn) | ||
1005 | { | ||
1006 | uoff_t blocks = 1; | ||
1007 | if (dn) { | ||
1008 | while (*dn) { | ||
1009 | /* st_blocks is in 512 byte blocks */ | ||
1010 | blocks += (*dn)->dn_blocks; | ||
1011 | dn++; | ||
1012 | } | ||
1013 | } | ||
1014 | |||
1015 | /* Even though standard says use 512 byte blocks, coreutils use 1k */ | ||
1016 | /* Actually, we round up by calculating (blocks + 1) / 2, | ||
1017 | * "+ 1" was done when we initialized blocks to 1 */ | ||
1018 | return blocks >> 1; | ||
1019 | } | ||
1020 | #endif | ||
1021 | |||
1022 | static void scan_and_display_dirs_recur(struct dnode **dn, int first) | ||
1023 | { | ||
1024 | unsigned nfiles; | ||
1025 | struct dnode **subdnp; | ||
1026 | |||
1027 | for (; *dn; dn++) { | ||
1028 | if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { | ||
1029 | if (!first) | ||
1030 | bb_putchar('\n'); | ||
1031 | first = 0; | ||
1032 | printf("%s:\n", (*dn)->fullname); | ||
1033 | } | ||
1034 | subdnp = scan_one_dir((*dn)->fullname, &nfiles); | ||
1035 | #if ENABLE_DESKTOP && !ENABLE_PLATFORM_MINGW32 | ||
1036 | if ((G.all_fmt & STYLE_MASK) == STYLE_LONG) | ||
1037 | printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); | ||
1038 | #endif | ||
1039 | if (nfiles > 0) { | ||
1040 | /* list all files at this level */ | ||
1041 | sort_and_display_files(subdnp, nfiles); | ||
1042 | |||
1043 | if (ENABLE_FEATURE_LS_RECURSIVE | ||
1044 | && (G.all_fmt & DISP_RECURSIVE) | ||
1045 | ) { | ||
1046 | struct dnode **dnd; | ||
1047 | unsigned dndirs; | ||
1048 | /* recursive - list the sub-dirs */ | ||
1049 | dnd = splitdnarray(subdnp, SPLIT_SUBDIR); | ||
1050 | dndirs = count_dirs(subdnp, SPLIT_SUBDIR); | ||
1051 | if (dndirs > 0) { | ||
1052 | dnsort(dnd, dndirs); | ||
1053 | scan_and_display_dirs_recur(dnd, 0); | ||
1054 | /* free the array of dnode pointers to the dirs */ | ||
1055 | free(dnd); | ||
1056 | } | ||
1057 | } | ||
1058 | /* free the dnodes and the fullname mem */ | ||
1059 | dfree(subdnp); | ||
1060 | } | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1019 | 1064 | ||
1020 | int ls_main(int argc UNUSED_PARAM, char **argv) | 1065 | int ls_main(int argc UNUSED_PARAM, char **argv) |
1021 | { | 1066 | { |
@@ -1054,13 +1099,13 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1054 | init_unicode(); | 1099 | init_unicode(); |
1055 | 1100 | ||
1056 | if (ENABLE_FEATURE_LS_SORTFILES) | 1101 | if (ENABLE_FEATURE_LS_SORTFILES) |
1057 | all_fmt = SORT_NAME; | 1102 | G.all_fmt = SORT_NAME; |
1058 | 1103 | ||
1059 | #if ENABLE_FEATURE_AUTOWIDTH | 1104 | #if ENABLE_FEATURE_AUTOWIDTH |
1060 | /* obtain the terminal width */ | 1105 | /* obtain the terminal width */ |
1061 | get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL); | 1106 | get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL); |
1062 | /* go one less... */ | 1107 | /* go one less... */ |
1063 | terminal_width--; | 1108 | G_terminal_width--; |
1064 | #endif | 1109 | #endif |
1065 | 1110 | ||
1066 | /* process options */ | 1111 | /* process options */ |
@@ -1081,7 +1126,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1081 | /* -w NUM: */ | 1126 | /* -w NUM: */ |
1082 | IF_FEATURE_AUTOWIDTH(":w+"); | 1127 | IF_FEATURE_AUTOWIDTH(":w+"); |
1083 | opt = getopt32(argv, ls_options | 1128 | opt = getopt32(argv, ls_options |
1084 | IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width) | 1129 | IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width) |
1085 | IF_FEATURE_LS_COLOR(, &color_opt) | 1130 | IF_FEATURE_LS_COLOR(, &color_opt) |
1086 | ); | 1131 | ); |
1087 | for (i = 0; opt_flags[i] != (1U << 31); i++) { | 1132 | for (i = 0; opt_flags[i] != (1U << 31); i++) { |
@@ -1089,27 +1134,27 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1089 | uint32_t flags = opt_flags[i]; | 1134 | uint32_t flags = opt_flags[i]; |
1090 | 1135 | ||
1091 | if (flags & STYLE_MASK) | 1136 | if (flags & STYLE_MASK) |
1092 | all_fmt &= ~STYLE_MASK; | 1137 | G.all_fmt &= ~STYLE_MASK; |
1093 | if (flags & SORT_MASK) | 1138 | if (flags & SORT_MASK) |
1094 | all_fmt &= ~SORT_MASK; | 1139 | G.all_fmt &= ~SORT_MASK; |
1095 | if (flags & TIME_MASK) | 1140 | if (flags & TIME_MASK) |
1096 | all_fmt &= ~TIME_MASK; | 1141 | G.all_fmt &= ~TIME_MASK; |
1097 | 1142 | ||
1098 | all_fmt |= flags; | 1143 | G.all_fmt |= flags; |
1099 | } | 1144 | } |
1100 | } | 1145 | } |
1101 | 1146 | ||
1102 | #if ENABLE_FEATURE_LS_COLOR | 1147 | #if ENABLE_FEATURE_LS_COLOR |
1103 | /* set show_color = 1/0 */ | 1148 | /* set G_show_color = 1/0 */ |
1104 | if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { | 1149 | if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { |
1105 | char *p = getenv("LS_COLORS"); | 1150 | char *p = getenv("LS_COLORS"); |
1106 | /* LS_COLORS is unset, or (not empty && not "none") ? */ | 1151 | /* LS_COLORS is unset, or (not empty && not "none") ? */ |
1107 | if (!p || (p[0] && strcmp(p, "none") != 0)) | 1152 | if (!p || (p[0] && strcmp(p, "none") != 0)) |
1108 | show_color = 1; | 1153 | G_show_color = 1; |
1109 | } | 1154 | } |
1110 | if (opt & OPT_color) { | 1155 | if (opt & OPT_color) { |
1111 | if (color_opt[0] == 'n') | 1156 | if (color_opt[0] == 'n') |
1112 | show_color = 0; | 1157 | G_show_color = 0; |
1113 | else switch (index_in_substrings(color_str, color_opt)) { | 1158 | else switch (index_in_substrings(color_str, color_opt)) { |
1114 | case 3: | 1159 | case 3: |
1115 | case 4: | 1160 | case 4: |
@@ -1118,34 +1163,34 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1118 | case 0: | 1163 | case 0: |
1119 | case 1: | 1164 | case 1: |
1120 | case 2: | 1165 | case 2: |
1121 | show_color = 1; | 1166 | G_show_color = 1; |
1122 | } | 1167 | } |
1123 | } | 1168 | } |
1124 | } | 1169 | } |
1125 | #endif | 1170 | #endif |
1126 | 1171 | ||
1127 | /* sort out which command line options take precedence */ | 1172 | /* sort out which command line options take precedence */ |
1128 | if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST)) | 1173 | if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST)) |
1129 | all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ | 1174 | G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ |
1130 | if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { | 1175 | if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { |
1131 | if (all_fmt & TIME_CHANGE) | 1176 | if (G.all_fmt & TIME_CHANGE) |
1132 | all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; | 1177 | G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME; |
1133 | if (all_fmt & TIME_ACCESS) | 1178 | if (G.all_fmt & TIME_ACCESS) |
1134 | all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; | 1179 | G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME; |
1135 | } | 1180 | } |
1136 | if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ | 1181 | if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ |
1137 | all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); | 1182 | G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); |
1138 | 1183 | ||
1139 | /* choose a display format if one was not already specified by an option */ | 1184 | /* choose a display format if one was not already specified by an option */ |
1140 | if (!(all_fmt & STYLE_MASK)) | 1185 | if (!(G.all_fmt & STYLE_MASK)) |
1141 | all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); | 1186 | G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); |
1142 | 1187 | ||
1143 | argv += optind; | 1188 | argv += optind; |
1144 | if (!argv[0]) | 1189 | if (!argv[0]) |
1145 | *--argv = (char*)"."; | 1190 | *--argv = (char*)"."; |
1146 | 1191 | ||
1147 | if (argv[1]) | 1192 | if (argv[1]) |
1148 | all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ | 1193 | G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ |
1149 | 1194 | ||
1150 | /* stuff the command line file names into a dnode array */ | 1195 | /* stuff the command line file names into a dnode array */ |
1151 | dn = NULL; | 1196 | dn = NULL; |
@@ -1153,25 +1198,26 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1153 | do { | 1198 | do { |
1154 | cur = my_stat(*argv, *argv, | 1199 | cur = my_stat(*argv, *argv, |
1155 | /* follow links on command line unless -l, -s or -F: */ | 1200 | /* follow links on command line unless -l, -s or -F: */ |
1156 | !((all_fmt & STYLE_MASK) == STYLE_LONG | 1201 | !((G.all_fmt & STYLE_MASK) == STYLE_LONG |
1157 | || (all_fmt & LIST_BLOCKS) | 1202 | || (G.all_fmt & LIST_BLOCKS) |
1158 | || (option_mask32 & OPT_F) | 1203 | || (option_mask32 & OPT_F) |
1159 | ) | 1204 | ) |
1160 | /* ... or if -H: */ | 1205 | /* ... or if -H: */ |
1161 | || (option_mask32 & OPT_H) | 1206 | || (option_mask32 & OPT_H) |
1207 | /* ... or if -L, but my_stat always follows links if -L */ | ||
1162 | ); | 1208 | ); |
1163 | argv++; | 1209 | argv++; |
1164 | if (!cur) | 1210 | if (!cur) |
1165 | continue; | 1211 | continue; |
1166 | cur->fname_allocated = 0; | 1212 | /*cur->fname_allocated = 0; - already is */ |
1167 | cur->next = dn; | 1213 | cur->dn_next = dn; |
1168 | dn = cur; | 1214 | dn = cur; |
1169 | nfiles++; | 1215 | nfiles++; |
1170 | } while (*argv); | 1216 | } while (*argv); |
1171 | 1217 | ||
1172 | /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ | 1218 | /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ |
1173 | if (nfiles == 0) | 1219 | if (nfiles == 0) |
1174 | return exit_code; | 1220 | return G.exit_code; |
1175 | 1221 | ||
1176 | /* now that we know how many files there are | 1222 | /* now that we know how many files there are |
1177 | * allocate memory for an array to hold dnode pointers | 1223 | * allocate memory for an array to hold dnode pointers |
@@ -1179,33 +1225,32 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1179 | dnp = dnalloc(nfiles); | 1225 | dnp = dnalloc(nfiles); |
1180 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { | 1226 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { |
1181 | dnp[i] = dn; /* save pointer to node in array */ | 1227 | dnp[i] = dn; /* save pointer to node in array */ |
1182 | dn = dn->next; | 1228 | dn = dn->dn_next; |
1183 | if (!dn) | 1229 | if (!dn) |
1184 | break; | 1230 | break; |
1185 | } | 1231 | } |
1186 | 1232 | ||
1187 | if (all_fmt & DISP_NOLIST) { | 1233 | if (G.all_fmt & DISP_NOLIST) { |
1188 | dnsort(dnp, nfiles); | 1234 | sort_and_display_files(dnp, nfiles); |
1189 | showfiles(dnp, nfiles); | ||
1190 | } else { | 1235 | } else { |
1191 | dnd = splitdnarray(dnp, SPLIT_DIR); | 1236 | dnd = splitdnarray(dnp, SPLIT_DIR); |
1192 | dnf = splitdnarray(dnp, SPLIT_FILE); | 1237 | dnf = splitdnarray(dnp, SPLIT_FILE); |
1193 | dndirs = count_dirs(dnp, SPLIT_DIR); | 1238 | dndirs = count_dirs(dnp, SPLIT_DIR); |
1194 | dnfiles = nfiles - dndirs; | 1239 | dnfiles = nfiles - dndirs; |
1195 | if (dnfiles > 0) { | 1240 | if (dnfiles > 0) { |
1196 | dnsort(dnf, dnfiles); | 1241 | sort_and_display_files(dnf, dnfiles); |
1197 | showfiles(dnf, dnfiles); | ||
1198 | if (ENABLE_FEATURE_CLEAN_UP) | 1242 | if (ENABLE_FEATURE_CLEAN_UP) |
1199 | free(dnf); | 1243 | free(dnf); |
1200 | } | 1244 | } |
1201 | if (dndirs > 0) { | 1245 | if (dndirs > 0) { |
1202 | dnsort(dnd, dndirs); | 1246 | dnsort(dnd, dndirs); |
1203 | showdirs(dnd, dnfiles == 0); | 1247 | scan_and_display_dirs_recur(dnd, dnfiles == 0); |
1204 | if (ENABLE_FEATURE_CLEAN_UP) | 1248 | if (ENABLE_FEATURE_CLEAN_UP) |
1205 | free(dnd); | 1249 | free(dnd); |
1206 | } | 1250 | } |
1207 | } | 1251 | } |
1252 | |||
1208 | if (ENABLE_FEATURE_CLEAN_UP) | 1253 | if (ENABLE_FEATURE_CLEAN_UP) |
1209 | dfree(dnp); | 1254 | dfree(dnp); |
1210 | return exit_code; | 1255 | return G.exit_code; |
1211 | } | 1256 | } |
diff --git a/coreutils/od.c b/coreutils/od.c index 31ebde210..fb11fcfe3 100644 --- a/coreutils/od.c +++ b/coreutils/od.c | |||
@@ -11,11 +11,12 @@ | |||
11 | * Original copyright notice is retained at the end of this file. | 11 | * Original copyright notice is retained at the end of this file. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | //usage:#if !ENABLE_DESKTOP | ||
14 | //usage:#define od_trivial_usage | 15 | //usage:#define od_trivial_usage |
15 | //usage: "[-aBbcDdeFfHhIiLlOovXx] " IF_DESKTOP("[-t TYPE] ") "[FILE]" | 16 | //usage: "[-aBbcDdeFfHhIiLlOovXx] [FILE]" |
16 | //usage:#define od_full_usage "\n\n" | 17 | //usage:#define od_full_usage "\n\n" |
17 | //usage: "Write an unambiguous representation, octal bytes by default, of FILE\n" | 18 | //usage: "Print FILE (or stdin) unambiguously, as octal bytes by default" |
18 | //usage: "(or stdin) to stdout" | 19 | //usage:#endif |
19 | 20 | ||
20 | #include "libbb.h" | 21 | #include "libbb.h" |
21 | #if ENABLE_DESKTOP | 22 | #if ENABLE_DESKTOP |
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c index 4c6b64d5e..8013f483c 100644 --- a/coreutils/od_bloaty.c +++ b/coreutils/od_bloaty.c | |||
@@ -13,48 +13,64 @@ | |||
13 | 13 | ||
14 | You should have received a copy of the GNU General Public License | 14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software Foundation, | 15 | along with this program; if not, write to the Free Software Foundation, |
16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | 16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | 17 | */ | |
18 | /* Written by Jim Meyering. */ | 18 | /* Written by Jim Meyering. */ |
19 | /* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */ | ||
19 | 20 | ||
20 | /* Busyboxed by Denys Vlasenko | ||
21 | |||
22 | Based on od.c from coreutils-5.2.1 | ||
23 | Top bloat sources: | ||
24 | 21 | ||
25 | 00000073 t parse_old_offset | 22 | /* #include "libbb.h" - done in od.c */ |
26 | 0000007b t get_lcm | 23 | #define assert(a) ((void)0) |
27 | 00000090 r long_options | ||
28 | 00000092 t print_named_ascii | ||
29 | 000000bf t print_ascii | ||
30 | 00000168 t write_block | ||
31 | 00000366 t decode_format_string | ||
32 | 00000a71 T od_main | ||
33 | |||
34 | Tested for compat with coreutils 6.3 | ||
35 | using this script. Minor differences fixed. | ||
36 | 24 | ||
37 | #!/bin/sh | ||
38 | echo STD | ||
39 | time /path/to/coreutils/od \ | ||
40 | ...params... \ | ||
41 | >std | ||
42 | echo Exit code $? | ||
43 | echo BBOX | ||
44 | time ./busybox od \ | ||
45 | ...params... \ | ||
46 | >bbox | ||
47 | echo Exit code $? | ||
48 | diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; } | ||
49 | 25 | ||
50 | */ | 26 | //usage:#if ENABLE_DESKTOP |
27 | //usage:#define od_trivial_usage | ||
28 | //usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE...]" | ||
29 | // We don't support: | ||
30 | // ... [FILE] [[+]OFFSET[.][b]] | ||
31 | // Support is buggy for: | ||
32 | // od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]] | ||
33 | |||
34 | //usage:#define od_full_usage "\n\n" | ||
35 | //usage: "Print FILEs (or stdin) unambiguously, as octal bytes by default" | ||
36 | //usage:#endif | ||
37 | |||
38 | enum { | ||
39 | OPT_A = 1 << 0, | ||
40 | OPT_N = 1 << 1, | ||
41 | OPT_a = 1 << 2, | ||
42 | OPT_b = 1 << 3, | ||
43 | OPT_c = 1 << 4, | ||
44 | OPT_d = 1 << 5, | ||
45 | OPT_f = 1 << 6, | ||
46 | OPT_h = 1 << 7, | ||
47 | OPT_i = 1 << 8, | ||
48 | OPT_j = 1 << 9, | ||
49 | OPT_l = 1 << 10, | ||
50 | OPT_o = 1 << 11, | ||
51 | OPT_t = 1 << 12, | ||
52 | /* When zero and two or more consecutive blocks are equal, format | ||
53 | only the first block and output an asterisk alone on the following | ||
54 | line to indicate that identical blocks have been elided: */ | ||
55 | OPT_v = 1 << 13, | ||
56 | OPT_x = 1 << 14, | ||
57 | OPT_s = 1 << 15, | ||
58 | OPT_S = 1 << 16, | ||
59 | OPT_w = 1 << 17, | ||
60 | OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, | ||
61 | }; | ||
51 | 62 | ||
52 | #include "libbb.h" | 63 | #define OD_GETOPT32() getopt32(argv, \ |
64 | "A:N:abcdfhij:lot:vxsS:w::", \ | ||
65 | /* -w with optional param */ \ | ||
66 | /* -S was -s and also had optional parameter */ \ | ||
67 | /* but in coreutils 6.3 it was renamed and now has */ \ | ||
68 | /* _mandatory_ parameter */ \ | ||
69 | &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block) | ||
53 | 70 | ||
54 | #define assert(a) ((void)0) | ||
55 | 71 | ||
56 | /* Check for 0x7f is a coreutils 6.3 addition */ | 72 | /* Check for 0x7f is a coreutils 6.3 addition */ |
57 | #define ISPRINT(c) (((c)>=' ') && (c) != 0x7f) | 73 | #define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f) |
58 | 74 | ||
59 | typedef long double longdouble_t; | 75 | typedef long double longdouble_t; |
60 | typedef unsigned long long ulonglong_t; | 76 | typedef unsigned long long ulonglong_t; |
@@ -165,17 +181,9 @@ struct ERR_width_bytes_has_bad_size { | |||
165 | char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; | 181 | char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; |
166 | }; | 182 | }; |
167 | 183 | ||
168 | static smallint flag_dump_strings; | 184 | static smallint exit_code; |
169 | /* Non-zero if an old-style 'pseudo-address' was specified. */ | ||
170 | static smallint flag_pseudo_start; | ||
171 | static smallint limit_bytes_to_format; | ||
172 | /* When zero and two or more consecutive blocks are equal, format | ||
173 | only the first block and output an asterisk alone on the following | ||
174 | line to indicate that identical blocks have been elided. */ | ||
175 | static smallint verbose; | ||
176 | static smallint ioerror; | ||
177 | 185 | ||
178 | static size_t string_min; | 186 | static unsigned string_min; |
179 | 187 | ||
180 | /* An array of specs describing how to format each input block. */ | 188 | /* An array of specs describing how to format each input block. */ |
181 | static size_t n_specs; | 189 | static size_t n_specs; |
@@ -186,7 +194,11 @@ static struct tspec *spec; | |||
186 | static void (*format_address)(off_t, char); | 194 | static void (*format_address)(off_t, char); |
187 | /* The difference between the old-style pseudo starting address and | 195 | /* The difference between the old-style pseudo starting address and |
188 | the number of bytes to skip. */ | 196 | the number of bytes to skip. */ |
197 | #if ENABLE_LONG_OPTS | ||
189 | static off_t pseudo_offset; | 198 | static off_t pseudo_offset; |
199 | #else | ||
200 | enum { pseudo_offset = 0 }; | ||
201 | #endif | ||
190 | /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all | 202 | /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all |
191 | input is formatted. */ | 203 | input is formatted. */ |
192 | 204 | ||
@@ -221,7 +233,7 @@ static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 | |||
221 | 233 | ||
222 | #define MAX_FP_TYPE_SIZE sizeof(longdouble_t) | 234 | #define MAX_FP_TYPE_SIZE sizeof(longdouble_t) |
223 | static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { | 235 | static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { |
224 | /* gcc seems to allow repeated indexes. Last one stays */ | 236 | /* gcc seems to allow repeated indexes. Last one wins */ |
225 | [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, | 237 | [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, |
226 | [sizeof(double)] = FLOAT_DOUBLE, | 238 | [sizeof(double)] = FLOAT_DOUBLE, |
227 | [sizeof(float)] = FLOAT_SINGLE | 239 | [sizeof(float)] = FLOAT_SINGLE |
@@ -383,7 +395,7 @@ print_named_ascii(size_t n_bytes, const char *block, | |||
383 | }; | 395 | }; |
384 | // buf[N] pos: 01234 56789 | 396 | // buf[N] pos: 01234 56789 |
385 | char buf[12] = " x\0 0xx\0"; | 397 | char buf[12] = " x\0 0xx\0"; |
386 | // actually " x\0 xxx\0", but I want to share the string with below. | 398 | // actually " x\0 xxx\0", but want to share string with print_ascii. |
387 | // [12] because we take three 32bit stack slots anyway, and | 399 | // [12] because we take three 32bit stack slots anyway, and |
388 | // gcc is too dumb to initialize with constant stores, | 400 | // gcc is too dumb to initialize with constant stores, |
389 | // it copies initializer from rodata. Oh well. | 401 | // it copies initializer from rodata. Oh well. |
@@ -479,10 +491,10 @@ open_next_file(void) | |||
479 | if (in_stream) { | 491 | if (in_stream) { |
480 | break; | 492 | break; |
481 | } | 493 | } |
482 | ioerror = 1; | 494 | exit_code = 1; |
483 | } | 495 | } |
484 | 496 | ||
485 | if (limit_bytes_to_format && !flag_dump_strings) | 497 | if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N) |
486 | setbuf(in_stream, NULL); | 498 | setbuf(in_stream, NULL); |
487 | } | 499 | } |
488 | 500 | ||
@@ -502,15 +514,14 @@ check_and_close(void) | |||
502 | ? bb_msg_standard_input | 514 | ? bb_msg_standard_input |
503 | : file_list[-1] | 515 | : file_list[-1] |
504 | ); | 516 | ); |
505 | ioerror = 1; | 517 | exit_code = 1; |
506 | } | 518 | } |
507 | fclose_if_not_stdin(in_stream); | 519 | fclose_if_not_stdin(in_stream); |
508 | in_stream = NULL; | 520 | in_stream = NULL; |
509 | } | 521 | } |
510 | 522 | ||
511 | if (ferror(stdout)) { | 523 | if (ferror(stdout)) { |
512 | bb_error_msg(bb_msg_write_error); | 524 | bb_error_msg_and_die(bb_msg_write_error); |
513 | ioerror = 1; | ||
514 | } | 525 | } |
515 | } | 526 | } |
516 | 527 | ||
@@ -542,7 +553,6 @@ decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) | |||
542 | unsigned field_width = 0; | 553 | unsigned field_width = 0; |
543 | int pos; | 554 | int pos; |
544 | 555 | ||
545 | |||
546 | switch (*s) { | 556 | switch (*s) { |
547 | case 'd': | 557 | case 'd': |
548 | case 'o': | 558 | case 'o': |
@@ -788,7 +798,7 @@ skip(off_t n_skip) | |||
788 | /* take "check & close / open_next" route */ | 798 | /* take "check & close / open_next" route */ |
789 | } else { | 799 | } else { |
790 | if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) | 800 | if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) |
791 | ioerror = 1; | 801 | exit_code = 1; |
792 | return; | 802 | return; |
793 | } | 803 | } |
794 | } else { | 804 | } else { |
@@ -889,7 +899,8 @@ write_block(off_t current_offset, size_t n_bytes, | |||
889 | static char prev_pair_equal = 0; | 899 | static char prev_pair_equal = 0; |
890 | size_t i; | 900 | size_t i; |
891 | 901 | ||
892 | if (!verbose && !first | 902 | if (!(option_mask32 & OPT_v) |
903 | && !first | ||
893 | && n_bytes == bytes_per_block | 904 | && n_bytes == bytes_per_block |
894 | && memcmp(prev_block, curr_block, bytes_per_block) == 0 | 905 | && memcmp(prev_block, curr_block, bytes_per_block) == 0 |
895 | ) { | 906 | ) { |
@@ -911,9 +922,9 @@ write_block(off_t current_offset, size_t n_bytes, | |||
911 | (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); | 922 | (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); |
912 | if (spec[i].hexl_mode_trailer) { | 923 | if (spec[i].hexl_mode_trailer) { |
913 | /* space-pad out to full line width, then dump the trailer */ | 924 | /* space-pad out to full line width, then dump the trailer */ |
914 | int datum_width = width_bytes[spec[i].size]; | 925 | unsigned datum_width = width_bytes[spec[i].size]; |
915 | int blank_fields = (bytes_per_block - n_bytes) / datum_width; | 926 | unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width; |
916 | int field_width = spec[i].field_width + 1; | 927 | unsigned field_width = spec[i].field_width + 1; |
917 | printf("%*s", blank_fields * field_width, ""); | 928 | printf("%*s", blank_fields * field_width, ""); |
918 | dump_hexl_mode_trailer(n_bytes, curr_block); | 929 | dump_hexl_mode_trailer(n_bytes, curr_block); |
919 | } | 930 | } |
@@ -961,42 +972,6 @@ get_lcm(void) | |||
961 | return l_c_m; | 972 | return l_c_m; |
962 | } | 973 | } |
963 | 974 | ||
964 | #if ENABLE_LONG_OPTS | ||
965 | /* If S is a valid traditional offset specification with an optional | ||
966 | leading '+' return nonzero and set *OFFSET to the offset it denotes. */ | ||
967 | |||
968 | static int | ||
969 | parse_old_offset(const char *s, off_t *offset) | ||
970 | { | ||
971 | static const struct suffix_mult Bb[] = { | ||
972 | { "B", 1024 }, | ||
973 | { "b", 512 }, | ||
974 | { "", 0 } | ||
975 | }; | ||
976 | char *p; | ||
977 | int radix; | ||
978 | |||
979 | /* Skip over any leading '+'. */ | ||
980 | if (s[0] == '+') ++s; | ||
981 | |||
982 | /* Determine the radix we'll use to interpret S. If there is a '.', | ||
983 | * it's decimal, otherwise, if the string begins with '0X'or '0x', | ||
984 | * it's hexadecimal, else octal. */ | ||
985 | p = strchr(s, '.'); | ||
986 | radix = 8; | ||
987 | if (p) { | ||
988 | p[0] = '\0'; /* cheating */ | ||
989 | radix = 10; | ||
990 | } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) | ||
991 | radix = 16; | ||
992 | |||
993 | *offset = xstrtooff_sfx(s, radix, Bb); | ||
994 | if (p) p[0] = '.'; | ||
995 | |||
996 | return (*offset >= 0); | ||
997 | } | ||
998 | #endif | ||
999 | |||
1000 | /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the | 975 | /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the |
1001 | formatted block to standard output, and repeat until the specified | 976 | formatted block to standard output, and repeat until the specified |
1002 | maximum number of bytes has been read or until all input has been | 977 | maximum number of bytes has been read or until all input has been |
@@ -1014,27 +989,25 @@ dump(off_t current_offset, off_t end_offset) | |||
1014 | int idx; | 989 | int idx; |
1015 | size_t n_bytes_read; | 990 | size_t n_bytes_read; |
1016 | 991 | ||
1017 | block[0] = xmalloc(2*bytes_per_block); | 992 | block[0] = xmalloc(2 * bytes_per_block); |
1018 | block[1] = block[0] + bytes_per_block; | 993 | block[1] = block[0] + bytes_per_block; |
1019 | 994 | ||
1020 | idx = 0; | 995 | idx = 0; |
1021 | if (limit_bytes_to_format) { | 996 | if (option_mask32 & OPT_N) { |
1022 | while (1) { | 997 | while (1) { |
1023 | size_t n_needed; | 998 | size_t n_needed; |
1024 | if (current_offset >= end_offset) { | 999 | if (current_offset >= end_offset) { |
1025 | n_bytes_read = 0; | 1000 | n_bytes_read = 0; |
1026 | break; | 1001 | break; |
1027 | } | 1002 | } |
1028 | n_needed = MIN(end_offset - current_offset, | 1003 | n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block); |
1029 | (off_t) bytes_per_block); | ||
1030 | read_block(n_needed, block[idx], &n_bytes_read); | 1004 | read_block(n_needed, block[idx], &n_bytes_read); |
1031 | if (n_bytes_read < bytes_per_block) | 1005 | if (n_bytes_read < bytes_per_block) |
1032 | break; | 1006 | break; |
1033 | assert(n_bytes_read == bytes_per_block); | 1007 | assert(n_bytes_read == bytes_per_block); |
1034 | write_block(current_offset, n_bytes_read, | 1008 | write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); |
1035 | block[!idx], block[idx]); | ||
1036 | current_offset += n_bytes_read; | 1009 | current_offset += n_bytes_read; |
1037 | idx = !idx; | 1010 | idx ^= 1; |
1038 | } | 1011 | } |
1039 | } else { | 1012 | } else { |
1040 | while (1) { | 1013 | while (1) { |
@@ -1042,10 +1015,9 @@ dump(off_t current_offset, off_t end_offset) | |||
1042 | if (n_bytes_read < bytes_per_block) | 1015 | if (n_bytes_read < bytes_per_block) |
1043 | break; | 1016 | break; |
1044 | assert(n_bytes_read == bytes_per_block); | 1017 | assert(n_bytes_read == bytes_per_block); |
1045 | write_block(current_offset, n_bytes_read, | 1018 | write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); |
1046 | block[!idx], block[idx]); | ||
1047 | current_offset += n_bytes_read; | 1019 | current_offset += n_bytes_read; |
1048 | idx = !idx; | 1020 | idx ^= 1; |
1049 | } | 1021 | } |
1050 | } | 1022 | } |
1051 | 1023 | ||
@@ -1061,41 +1033,18 @@ dump(off_t current_offset, off_t end_offset) | |||
1061 | 1033 | ||
1062 | memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); | 1034 | memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); |
1063 | write_block(current_offset, bytes_to_write, | 1035 | write_block(current_offset, bytes_to_write, |
1064 | block[!idx], block[idx]); | 1036 | block[idx ^ 1], block[idx]); |
1065 | current_offset += n_bytes_read; | 1037 | current_offset += n_bytes_read; |
1066 | } | 1038 | } |
1067 | 1039 | ||
1068 | format_address(current_offset, '\n'); | 1040 | format_address(current_offset, '\n'); |
1069 | 1041 | ||
1070 | if (limit_bytes_to_format && current_offset >= end_offset) | 1042 | if ((option_mask32 & OPT_N) && current_offset >= end_offset) |
1071 | check_and_close(); | 1043 | check_and_close(); |
1072 | 1044 | ||
1073 | free(block[0]); | 1045 | free(block[0]); |
1074 | } | 1046 | } |
1075 | 1047 | ||
1076 | /* Read a single byte into *C from the concatenation of the input files | ||
1077 | named in the global array FILE_LIST. On the first call to this | ||
1078 | function, the global variable IN_STREAM is expected to be an open | ||
1079 | stream associated with the input file INPUT_FILENAME. If IN_STREAM | ||
1080 | is at end-of-file, close it and update the global variables IN_STREAM | ||
1081 | and INPUT_FILENAME so they correspond to the next file in the list. | ||
1082 | Then try to read a byte from the newly opened file. Repeat if | ||
1083 | necessary until EOF is reached for the last file in FILE_LIST, then | ||
1084 | set *C to EOF and return. Subsequent calls do likewise. */ | ||
1085 | |||
1086 | static void | ||
1087 | read_char(int *c) | ||
1088 | { | ||
1089 | while (in_stream) { /* !EOF */ | ||
1090 | *c = fgetc(in_stream); | ||
1091 | if (*c != EOF) | ||
1092 | return; | ||
1093 | check_and_close(); | ||
1094 | open_next_file(); | ||
1095 | } | ||
1096 | *c = EOF; | ||
1097 | } | ||
1098 | |||
1099 | /* Read N bytes into BLOCK from the concatenation of the input files | 1048 | /* Read N bytes into BLOCK from the concatenation of the input files |
1100 | named in the global array FILE_LIST. On the first call to this | 1049 | named in the global array FILE_LIST. On the first call to this |
1101 | function, the global variable IN_STREAM is expected to be an open | 1050 | function, the global variable IN_STREAM is expected to be an open |
@@ -1119,8 +1068,8 @@ read_char(int *c) | |||
1119 | static void | 1068 | static void |
1120 | dump_strings(off_t address, off_t end_offset) | 1069 | dump_strings(off_t address, off_t end_offset) |
1121 | { | 1070 | { |
1122 | size_t bufsize = MAX(100, string_min); | 1071 | unsigned bufsize = MAX(100, string_min); |
1123 | char *buf = xmalloc(bufsize); | 1072 | unsigned char *buf = xmalloc(bufsize); |
1124 | 1073 | ||
1125 | while (1) { | 1074 | while (1) { |
1126 | size_t i; | 1075 | size_t i; |
@@ -1128,19 +1077,25 @@ dump_strings(off_t address, off_t end_offset) | |||
1128 | 1077 | ||
1129 | /* See if the next 'string_min' chars are all printing chars. */ | 1078 | /* See if the next 'string_min' chars are all printing chars. */ |
1130 | tryline: | 1079 | tryline: |
1131 | if (limit_bytes_to_format && (end_offset - string_min <= address)) | 1080 | if ((option_mask32 & OPT_N) && (end_offset - string_min <= address)) |
1132 | break; | 1081 | break; |
1133 | i = 0; | 1082 | i = 0; |
1134 | while (!limit_bytes_to_format || address < end_offset) { | 1083 | while (!(option_mask32 & OPT_N) || address < end_offset) { |
1135 | if (i == bufsize) { | 1084 | if (i == bufsize) { |
1136 | bufsize += bufsize/8; | 1085 | bufsize += bufsize/8; |
1137 | buf = xrealloc(buf, bufsize); | 1086 | buf = xrealloc(buf, bufsize); |
1138 | } | 1087 | } |
1139 | read_char(&c); | 1088 | |
1140 | if (c < 0) { /* EOF */ | 1089 | while (in_stream) { /* !EOF */ |
1141 | free(buf); | 1090 | c = fgetc(in_stream); |
1142 | return; | 1091 | if (c != EOF) |
1092 | goto got_char; | ||
1093 | check_and_close(); | ||
1094 | open_next_file(); | ||
1143 | } | 1095 | } |
1096 | /* EOF */ | ||
1097 | goto ret; | ||
1098 | got_char: | ||
1144 | address++; | 1099 | address++; |
1145 | if (!c) | 1100 | if (!c) |
1146 | break; | 1101 | break; |
@@ -1152,8 +1107,7 @@ dump_strings(off_t address, off_t end_offset) | |||
1152 | if (i < string_min) /* Too short! */ | 1107 | if (i < string_min) /* Too short! */ |
1153 | goto tryline; | 1108 | goto tryline; |
1154 | 1109 | ||
1155 | /* If we get here, the string is all printable and NUL-terminated, | 1110 | /* If we get here, the string is all printable and NUL-terminated */ |
1156 | * so print it. It is all in 'buf' and 'i' is its length. */ | ||
1157 | buf[i] = 0; | 1111 | buf[i] = 0; |
1158 | format_address(address - i - 1, ' '); | 1112 | format_address(address - i - 1, ' '); |
1159 | 1113 | ||
@@ -1174,13 +1128,50 @@ dump_strings(off_t address, off_t end_offset) | |||
1174 | 1128 | ||
1175 | /* We reach this point only if we search through | 1129 | /* We reach this point only if we search through |
1176 | (max_bytes_to_format - string_min) bytes before reaching EOF. */ | 1130 | (max_bytes_to_format - string_min) bytes before reaching EOF. */ |
1131 | check_and_close(); | ||
1132 | ret: | ||
1177 | free(buf); | 1133 | free(buf); |
1134 | } | ||
1178 | 1135 | ||
1179 | check_and_close(); | 1136 | #if ENABLE_LONG_OPTS |
1137 | /* If S is a valid traditional offset specification with an optional | ||
1138 | leading '+' return nonzero and set *OFFSET to the offset it denotes. */ | ||
1139 | |||
1140 | static int | ||
1141 | parse_old_offset(const char *s, off_t *offset) | ||
1142 | { | ||
1143 | static const struct suffix_mult Bb[] = { | ||
1144 | { "B", 1024 }, | ||
1145 | { "b", 512 }, | ||
1146 | { "", 0 } | ||
1147 | }; | ||
1148 | char *p; | ||
1149 | int radix; | ||
1150 | |||
1151 | /* Skip over any leading '+'. */ | ||
1152 | if (s[0] == '+') ++s; | ||
1153 | if (!isdigit(s[0])) return 0; /* not a number */ | ||
1154 | |||
1155 | /* Determine the radix we'll use to interpret S. If there is a '.', | ||
1156 | * it's decimal, otherwise, if the string begins with '0X'or '0x', | ||
1157 | * it's hexadecimal, else octal. */ | ||
1158 | p = strchr(s, '.'); | ||
1159 | radix = 8; | ||
1160 | if (p) { | ||
1161 | p[0] = '\0'; /* cheating */ | ||
1162 | radix = 10; | ||
1163 | } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) | ||
1164 | radix = 16; | ||
1165 | |||
1166 | *offset = xstrtooff_sfx(s, radix, Bb); | ||
1167 | if (p) p[0] = '.'; | ||
1168 | |||
1169 | return (*offset >= 0); | ||
1180 | } | 1170 | } |
1171 | #endif | ||
1181 | 1172 | ||
1182 | int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1173 | int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1183 | int od_main(int argc, char **argv) | 1174 | int od_main(int argc UNUSED_PARAM, char **argv) |
1184 | { | 1175 | { |
1185 | static const struct suffix_mult bkm[] = { | 1176 | static const struct suffix_mult bkm[] = { |
1186 | { "b", 512 }, | 1177 | { "b", 512 }, |
@@ -1188,27 +1179,6 @@ int od_main(int argc, char **argv) | |||
1188 | { "m", 1024*1024 }, | 1179 | { "m", 1024*1024 }, |
1189 | { "", 0 } | 1180 | { "", 0 } |
1190 | }; | 1181 | }; |
1191 | enum { | ||
1192 | OPT_A = 1 << 0, | ||
1193 | OPT_N = 1 << 1, | ||
1194 | OPT_a = 1 << 2, | ||
1195 | OPT_b = 1 << 3, | ||
1196 | OPT_c = 1 << 4, | ||
1197 | OPT_d = 1 << 5, | ||
1198 | OPT_f = 1 << 6, | ||
1199 | OPT_h = 1 << 7, | ||
1200 | OPT_i = 1 << 8, | ||
1201 | OPT_j = 1 << 9, | ||
1202 | OPT_l = 1 << 10, | ||
1203 | OPT_o = 1 << 11, | ||
1204 | OPT_t = 1 << 12, | ||
1205 | OPT_v = 1 << 13, | ||
1206 | OPT_x = 1 << 14, | ||
1207 | OPT_s = 1 << 15, | ||
1208 | OPT_S = 1 << 16, | ||
1209 | OPT_w = 1 << 17, | ||
1210 | OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, | ||
1211 | }; | ||
1212 | #if ENABLE_LONG_OPTS | 1182 | #if ENABLE_LONG_OPTS |
1213 | static const char od_longopts[] ALIGN1 = | 1183 | static const char od_longopts[] ALIGN1 = |
1214 | "skip-bytes\0" Required_argument "j" | 1184 | "skip-bytes\0" Required_argument "j" |
@@ -1216,18 +1186,18 @@ int od_main(int argc, char **argv) | |||
1216 | "read-bytes\0" Required_argument "N" | 1186 | "read-bytes\0" Required_argument "N" |
1217 | "format\0" Required_argument "t" | 1187 | "format\0" Required_argument "t" |
1218 | "output-duplicates\0" No_argument "v" | 1188 | "output-duplicates\0" No_argument "v" |
1189 | /* Yes, it's true: -S NUM, but --strings[=NUM]! | ||
1190 | * that is, NUM is mandatory for -S but optional for --strings! | ||
1191 | */ | ||
1219 | "strings\0" Optional_argument "S" | 1192 | "strings\0" Optional_argument "S" |
1220 | "width\0" Optional_argument "w" | 1193 | "width\0" Optional_argument "w" |
1221 | "traditional\0" No_argument "\xff" | 1194 | "traditional\0" No_argument "\xff" |
1222 | ; | 1195 | ; |
1223 | #endif | 1196 | #endif |
1224 | char *str_A, *str_N, *str_j, *str_S; | 1197 | const char *str_A, *str_N, *str_j, *str_S = "3"; |
1225 | llist_t *lst_t = NULL; | 1198 | llist_t *lst_t = NULL; |
1226 | unsigned opt; | 1199 | unsigned opt; |
1227 | int l_c_m; | 1200 | int l_c_m; |
1228 | /* The old-style 'pseudo starting address' to be printed in parentheses | ||
1229 | after any true address. */ | ||
1230 | off_t pseudo_start = pseudo_start; // for gcc | ||
1231 | /* The number of input bytes to skip before formatting and writing. */ | 1201 | /* The number of input bytes to skip before formatting and writing. */ |
1232 | off_t n_bytes_to_skip = 0; | 1202 | off_t n_bytes_to_skip = 0; |
1233 | /* The offset of the first byte after the last byte to be formatted. */ | 1203 | /* The offset of the first byte after the last byte to be formatted. */ |
@@ -1239,20 +1209,13 @@ int od_main(int argc, char **argv) | |||
1239 | format_address = format_address_std; | 1209 | format_address = format_address_std; |
1240 | address_base_char = 'o'; | 1210 | address_base_char = 'o'; |
1241 | address_pad_len_char = '7'; | 1211 | address_pad_len_char = '7'; |
1242 | /* flag_dump_strings = 0; - already is */ | ||
1243 | 1212 | ||
1244 | /* Parse command line */ | 1213 | /* Parse command line */ |
1245 | opt_complementary = "w+:t::"; /* -w N, -t is a list */ | 1214 | opt_complementary = "w+:t::"; /* -w N, -t is a list */ |
1246 | #if ENABLE_LONG_OPTS | 1215 | #if ENABLE_LONG_OPTS |
1247 | applet_long_options = od_longopts; | 1216 | applet_long_options = od_longopts; |
1248 | #endif | 1217 | #endif |
1249 | opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:" | 1218 | opt = OD_GETOPT32(); |
1250 | "w::", // -w with optional param | ||
1251 | // -S was -s and also had optional parameter | ||
1252 | // but in coreutils 6.3 it was renamed and now has | ||
1253 | // _mandatory_ parameter | ||
1254 | &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block); | ||
1255 | argc -= optind; | ||
1256 | argv += optind; | 1219 | argv += optind; |
1257 | if (opt & OPT_A) { | 1220 | if (opt & OPT_A) { |
1258 | static const char doxn[] ALIGN1 = "doxn"; | 1221 | static const char doxn[] ALIGN1 = "doxn"; |
@@ -1274,7 +1237,6 @@ int od_main(int argc, char **argv) | |||
1274 | address_pad_len_char = doxn_address_pad_len_char[pos]; | 1237 | address_pad_len_char = doxn_address_pad_len_char[pos]; |
1275 | } | 1238 | } |
1276 | if (opt & OPT_N) { | 1239 | if (opt & OPT_N) { |
1277 | limit_bytes_to_format = 1; | ||
1278 | max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); | 1240 | max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); |
1279 | } | 1241 | } |
1280 | if (opt & OPT_a) decode_format_string("a"); | 1242 | if (opt & OPT_a) decode_format_string("a"); |
@@ -1287,28 +1249,23 @@ int od_main(int argc, char **argv) | |||
1287 | if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); | 1249 | if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); |
1288 | if (opt & OPT_l) decode_format_string("d4"); | 1250 | if (opt & OPT_l) decode_format_string("d4"); |
1289 | if (opt & OPT_o) decode_format_string("o2"); | 1251 | if (opt & OPT_o) decode_format_string("o2"); |
1290 | //if (opt & OPT_t)... | ||
1291 | while (lst_t) { | 1252 | while (lst_t) { |
1292 | decode_format_string(llist_pop(&lst_t)); | 1253 | decode_format_string(llist_pop(&lst_t)); |
1293 | } | 1254 | } |
1294 | if (opt & OPT_v) verbose = 1; | ||
1295 | if (opt & OPT_x) decode_format_string("x2"); | 1255 | if (opt & OPT_x) decode_format_string("x2"); |
1296 | if (opt & OPT_s) decode_format_string("d2"); | 1256 | if (opt & OPT_s) decode_format_string("d2"); |
1297 | if (opt & OPT_S) { | 1257 | if (opt & OPT_S) { |
1298 | string_min = 3; | ||
1299 | string_min = xstrtou_sfx(str_S, 0, bkm); | 1258 | string_min = xstrtou_sfx(str_S, 0, bkm); |
1300 | flag_dump_strings = 1; | ||
1301 | } | 1259 | } |
1302 | //if (opt & OPT_w)... | ||
1303 | //if (opt & OPT_traditional)... | ||
1304 | 1260 | ||
1305 | if (flag_dump_strings && n_specs > 0) | 1261 | // Bloat: |
1306 | bb_error_msg_and_die("no type may be specified when dumping strings"); | 1262 | //if ((option_mask32 & OPT_S) && n_specs > 0) |
1263 | // bb_error_msg_and_die("no type may be specified when dumping strings"); | ||
1307 | 1264 | ||
1308 | /* If the --traditional option is used, there may be from | 1265 | /* If the --traditional option is used, there may be from |
1309 | * 0 to 3 remaining command line arguments; handle each case | 1266 | * 0 to 3 remaining command line arguments; handle each case |
1310 | * separately. | 1267 | * separately. |
1311 | * od [file] [[+]offset[.][b] [[+]label[.][b]]] | 1268 | * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]] |
1312 | * The offset and pseudo_start have the same syntax. | 1269 | * The offset and pseudo_start have the same syntax. |
1313 | * | 1270 | * |
1314 | * FIXME: POSIX 1003.1-2001 with XSI requires support for the | 1271 | * FIXME: POSIX 1003.1-2001 with XSI requires support for the |
@@ -1316,93 +1273,91 @@ int od_main(int argc, char **argv) | |||
1316 | 1273 | ||
1317 | #if ENABLE_LONG_OPTS | 1274 | #if ENABLE_LONG_OPTS |
1318 | if (opt & OPT_traditional) { | 1275 | if (opt & OPT_traditional) { |
1319 | off_t o1, o2; | 1276 | if (argv[0]) { |
1320 | 1277 | off_t pseudo_start = -1; | |
1321 | if (argc == 1) { | 1278 | off_t o1, o2; |
1322 | if (parse_old_offset(argv[0], &o1)) { | 1279 | |
1323 | n_bytes_to_skip = o1; | 1280 | if (!argv[1]) { /* one arg */ |
1324 | --argc; | 1281 | if (parse_old_offset(argv[0], &o1)) { |
1325 | ++argv; | 1282 | /* od --traditional OFFSET */ |
1326 | } | 1283 | n_bytes_to_skip = o1; |
1327 | } else if (argc == 2) { | 1284 | argv++; |
1328 | if (parse_old_offset(argv[0], &o1) | 1285 | } |
1329 | && parse_old_offset(argv[1], &o2) | 1286 | /* od --traditional FILE */ |
1330 | ) { | 1287 | } else if (!argv[2]) { /* two args */ |
1331 | n_bytes_to_skip = o1; | 1288 | if (parse_old_offset(argv[0], &o1) |
1332 | flag_pseudo_start = 1; | 1289 | && parse_old_offset(argv[1], &o2) |
1333 | pseudo_start = o2; | 1290 | ) { |
1334 | argv += 2; | 1291 | /* od --traditional OFFSET LABEL */ |
1335 | argc -= 2; | 1292 | n_bytes_to_skip = o1; |
1336 | } else if (parse_old_offset(argv[1], &o2)) { | 1293 | pseudo_start = o2; |
1337 | n_bytes_to_skip = o2; | 1294 | argv += 2; |
1338 | --argc; | 1295 | } else if (parse_old_offset(argv[1], &o2)) { |
1339 | argv[1] = argv[0]; | 1296 | /* od --traditional FILE OFFSET */ |
1340 | ++argv; | 1297 | n_bytes_to_skip = o2; |
1341 | } else { | 1298 | argv[1] = NULL; |
1342 | bb_error_msg_and_die("invalid second operand " | 1299 | } else { |
1343 | "in compatibility mode '%s'", argv[1]); | 1300 | bb_error_msg_and_die("invalid second argument '%s'", argv[1]); |
1344 | } | 1301 | } |
1345 | } else if (argc == 3) { | 1302 | } else if (!argv[3]) { /* three args */ |
1346 | if (parse_old_offset(argv[1], &o1) | 1303 | if (parse_old_offset(argv[1], &o1) |
1347 | && parse_old_offset(argv[2], &o2) | 1304 | && parse_old_offset(argv[2], &o2) |
1348 | ) { | 1305 | ) { |
1349 | n_bytes_to_skip = o1; | 1306 | /* od --traditional FILE OFFSET LABEL */ |
1350 | flag_pseudo_start = 1; | 1307 | n_bytes_to_skip = o1; |
1351 | pseudo_start = o2; | 1308 | pseudo_start = o2; |
1352 | argv[2] = argv[0]; | 1309 | argv[1] = NULL; |
1353 | argv += 2; | 1310 | } else { |
1354 | argc -= 2; | 1311 | bb_error_msg_and_die("the last two arguments must be offsets"); |
1355 | } else { | 1312 | } |
1356 | bb_error_msg_and_die("in compatibility mode " | 1313 | } else { /* >3 args */ |
1357 | "the last two arguments must be offsets"); | 1314 | bb_error_msg_and_die("too many arguments"); |
1358 | } | 1315 | } |
1359 | } else if (argc > 3) { | ||
1360 | bb_error_msg_and_die("compatibility mode supports " | ||
1361 | "at most three arguments"); | ||
1362 | } | ||
1363 | 1316 | ||
1364 | if (flag_pseudo_start) { | 1317 | if (pseudo_start >= 0) { |
1365 | if (format_address == format_address_none) { | 1318 | if (format_address == format_address_none) { |
1366 | address_base_char = 'o'; | 1319 | address_base_char = 'o'; |
1367 | address_pad_len_char = '7'; | 1320 | address_pad_len_char = '7'; |
1368 | format_address = format_address_paren; | 1321 | format_address = format_address_paren; |
1369 | } else | 1322 | } else { |
1370 | format_address = format_address_label; | 1323 | format_address = format_address_label; |
1324 | } | ||
1325 | pseudo_offset = pseudo_start - n_bytes_to_skip; | ||
1326 | } | ||
1371 | } | 1327 | } |
1328 | /* else: od --traditional (without args) */ | ||
1372 | } | 1329 | } |
1373 | #endif | 1330 | #endif |
1374 | 1331 | ||
1375 | if (limit_bytes_to_format) { | 1332 | if (option_mask32 & OPT_N) { |
1376 | end_offset = n_bytes_to_skip + max_bytes_to_format; | 1333 | end_offset = n_bytes_to_skip + max_bytes_to_format; |
1377 | if (end_offset < n_bytes_to_skip) | 1334 | if (end_offset < n_bytes_to_skip) |
1378 | bb_error_msg_and_die("skip-bytes + read-bytes is too large"); | 1335 | bb_error_msg_and_die("SKIP + SIZE is too large"); |
1379 | } | 1336 | } |
1380 | 1337 | ||
1381 | if (n_specs == 0) { | 1338 | if (n_specs == 0) { |
1382 | decode_format_string("o2"); | 1339 | decode_format_string("o2"); |
1383 | n_specs = 1; | 1340 | /*n_specs = 1; - done by decode_format_string */ |
1384 | } | 1341 | } |
1385 | 1342 | ||
1386 | /* If no files were listed on the command line, | 1343 | /* If no files were listed on the command line, |
1387 | set the global pointer FILE_LIST so that it | 1344 | set the global pointer FILE_LIST so that it |
1388 | references the null-terminated list of one name: "-". */ | 1345 | references the null-terminated list of one name: "-". */ |
1389 | file_list = bb_argv_dash; | 1346 | file_list = bb_argv_dash; |
1390 | if (argc > 0) { | 1347 | if (argv[0]) { |
1391 | /* Set the global pointer FILE_LIST so that it | 1348 | /* Set the global pointer FILE_LIST so that it |
1392 | references the first file-argument on the command-line. */ | 1349 | references the first file-argument on the command-line. */ |
1393 | file_list = (char const *const *) argv; | 1350 | file_list = (char const *const *) argv; |
1394 | } | 1351 | } |
1395 | 1352 | ||
1396 | /* open the first input file */ | 1353 | /* Open the first input file */ |
1397 | open_next_file(); | 1354 | open_next_file(); |
1398 | /* skip over any unwanted header bytes */ | 1355 | /* Skip over any unwanted header bytes */ |
1399 | skip(n_bytes_to_skip); | 1356 | skip(n_bytes_to_skip); |
1400 | if (!in_stream) | 1357 | if (!in_stream) |
1401 | return EXIT_FAILURE; | 1358 | return EXIT_FAILURE; |
1402 | 1359 | ||
1403 | pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0); | 1360 | /* Compute output block length */ |
1404 | |||
1405 | /* Compute output block length. */ | ||
1406 | l_c_m = get_lcm(); | 1361 | l_c_m = get_lcm(); |
1407 | 1362 | ||
1408 | if (opt & OPT_w) { /* -w: width */ | 1363 | if (opt & OPT_w) { /* -w: width */ |
@@ -1424,13 +1379,13 @@ int od_main(int argc, char **argv) | |||
1424 | } | 1379 | } |
1425 | #endif | 1380 | #endif |
1426 | 1381 | ||
1427 | if (flag_dump_strings) | 1382 | if (option_mask32 & OPT_S) |
1428 | dump_strings(n_bytes_to_skip, end_offset); | 1383 | dump_strings(n_bytes_to_skip, end_offset); |
1429 | else | 1384 | else |
1430 | dump(n_bytes_to_skip, end_offset); | 1385 | dump(n_bytes_to_skip, end_offset); |
1431 | 1386 | ||
1432 | if (fclose(stdin) == EOF) | 1387 | if (fclose(stdin)) |
1433 | bb_perror_msg_and_die(bb_msg_standard_input); | 1388 | bb_perror_msg_and_die(bb_msg_standard_input); |
1434 | 1389 | ||
1435 | return ioerror; | 1390 | return exit_code; |
1436 | } | 1391 | } |
diff --git a/coreutils/tail.c b/coreutils/tail.c index eac982735..4b42ebc52 100644 --- a/coreutils/tail.c +++ b/coreutils/tail.c | |||
@@ -59,7 +59,8 @@ static const struct suffix_mult tail_suffixes[] = { | |||
59 | }; | 59 | }; |
60 | 60 | ||
61 | struct globals { | 61 | struct globals { |
62 | bool status; | 62 | bool from_top; |
63 | bool exitcode; | ||
63 | } FIX_ALIASING; | 64 | } FIX_ALIASING; |
64 | #define G (*(struct globals*)&bb_common_bufsiz1) | 65 | #define G (*(struct globals*)&bb_common_bufsiz1) |
65 | 66 | ||
@@ -85,7 +86,7 @@ static ssize_t tail_read(int fd, char *buf, size_t count) | |||
85 | r = full_read(fd, buf, count); | 86 | r = full_read(fd, buf, count); |
86 | if (r < 0) { | 87 | if (r < 0) { |
87 | bb_perror_msg(bb_msg_read_error); | 88 | bb_perror_msg(bb_msg_read_error); |
88 | G.status = EXIT_FAILURE; | 89 | G.exitcode = EXIT_FAILURE; |
89 | } | 90 | } |
90 | 91 | ||
91 | return r; | 92 | return r; |
@@ -99,7 +100,7 @@ static unsigned eat_num(const char *p) | |||
99 | p++; | 100 | p++; |
100 | else if (*p == '+') { | 101 | else if (*p == '+') { |
101 | p++; | 102 | p++; |
102 | G.status = 1; /* mark that we saw "+" */ | 103 | G.from_top = 1; |
103 | } | 104 | } |
104 | return xatou_sfx(p, tail_suffixes); | 105 | return xatou_sfx(p, tail_suffixes); |
105 | } | 106 | } |
@@ -109,7 +110,6 @@ int tail_main(int argc, char **argv) | |||
109 | { | 110 | { |
110 | unsigned count = 10; | 111 | unsigned count = 10; |
111 | unsigned sleep_period = 1; | 112 | unsigned sleep_period = 1; |
112 | bool from_top; | ||
113 | const char *str_c, *str_n; | 113 | const char *str_c, *str_n; |
114 | 114 | ||
115 | char *tailbuf; | 115 | char *tailbuf; |
@@ -152,8 +152,6 @@ int tail_main(int argc, char **argv) | |||
152 | #endif | 152 | #endif |
153 | argc -= optind; | 153 | argc -= optind; |
154 | argv += optind; | 154 | argv += optind; |
155 | from_top = G.status; /* 1 if there was "-c +N" or "-n +N" */ | ||
156 | G.status = EXIT_SUCCESS; | ||
157 | 155 | ||
158 | /* open all the files */ | 156 | /* open all the files */ |
159 | fds = xmalloc(sizeof(fds[0]) * (argc + 1)); | 157 | fds = xmalloc(sizeof(fds[0]) * (argc + 1)); |
@@ -171,7 +169,7 @@ int tail_main(int argc, char **argv) | |||
171 | do { | 169 | do { |
172 | int fd = open_or_warn_stdin(argv[i]); | 170 | int fd = open_or_warn_stdin(argv[i]); |
173 | if (fd < 0 && !FOLLOW_RETRY) { | 171 | if (fd < 0 && !FOLLOW_RETRY) { |
174 | G.status = EXIT_FAILURE; | 172 | G.exitcode = EXIT_FAILURE; |
175 | continue; | 173 | continue; |
176 | } | 174 | } |
177 | fds[nfiles] = fd; | 175 | fds[nfiles] = fd; |
@@ -183,15 +181,19 @@ int tail_main(int argc, char **argv) | |||
183 | 181 | ||
184 | /* prepare the buffer */ | 182 | /* prepare the buffer */ |
185 | tailbufsize = BUFSIZ; | 183 | tailbufsize = BUFSIZ; |
186 | if (!from_top && COUNT_BYTES) { | 184 | if (!G.from_top && COUNT_BYTES) { |
187 | if (tailbufsize < count + BUFSIZ) { | 185 | if (tailbufsize < count + BUFSIZ) { |
188 | tailbufsize = count + BUFSIZ; | 186 | tailbufsize = count + BUFSIZ; |
189 | } | 187 | } |
190 | } | 188 | } |
191 | tailbuf = xmalloc(tailbufsize); | 189 | /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block. |
190 | * (In fact, it doesn't need ANY memory). So delay allocation. | ||
191 | */ | ||
192 | tailbuf = NULL; | ||
192 | 193 | ||
193 | /* tail the files */ | 194 | /* tail the files */ |
194 | fmt = header_fmt_str + 1; /* skip header leading newline on first output */ | 195 | |
196 | fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */ | ||
195 | i = 0; | 197 | i = 0; |
196 | do { | 198 | do { |
197 | char *buf; | 199 | char *buf; |
@@ -209,7 +211,7 @@ int tail_main(int argc, char **argv) | |||
209 | fmt = header_fmt_str; | 211 | fmt = header_fmt_str; |
210 | } | 212 | } |
211 | 213 | ||
212 | if (!from_top) { | 214 | if (!G.from_top) { |
213 | off_t current = lseek(fd, 0, SEEK_END); | 215 | off_t current = lseek(fd, 0, SEEK_END); |
214 | if (current > 0) { | 216 | if (current > 0) { |
215 | unsigned off; | 217 | unsigned off; |
@@ -242,6 +244,9 @@ int tail_main(int argc, char **argv) | |||
242 | } | 244 | } |
243 | } | 245 | } |
244 | 246 | ||
247 | if (!tailbuf) | ||
248 | tailbuf = xmalloc(tailbufsize); | ||
249 | |||
245 | buf = tailbuf; | 250 | buf = tailbuf; |
246 | taillen = 0; | 251 | taillen = 0; |
247 | /* "We saw 1st line/byte". | 252 | /* "We saw 1st line/byte". |
@@ -249,7 +254,7 @@ int tail_main(int argc, char **argv) | |||
249 | seen = 1; | 254 | seen = 1; |
250 | newlines_seen = 0; | 255 | newlines_seen = 0; |
251 | while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) { | 256 | while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) { |
252 | if (from_top) { | 257 | if (G.from_top) { |
253 | int nwrite = nread; | 258 | int nwrite = nread; |
254 | if (seen < count) { | 259 | if (seen < count) { |
255 | /* We need to skip a few more bytes/lines */ | 260 | /* We need to skip a few more bytes/lines */ |
@@ -313,7 +318,7 @@ int tail_main(int argc, char **argv) | |||
313 | buf = tailbuf + taillen; | 318 | buf = tailbuf + taillen; |
314 | } | 319 | } |
315 | } /* while (tail_read() > 0) */ | 320 | } /* while (tail_read() > 0) */ |
316 | if (!from_top) { | 321 | if (!G.from_top) { |
317 | xwrite(STDOUT_FILENO, tailbuf, taillen); | 322 | xwrite(STDOUT_FILENO, tailbuf, taillen); |
318 | } | 323 | } |
319 | } while (++i < nfiles); | 324 | } while (++i < nfiles); |
@@ -368,10 +373,11 @@ int tail_main(int argc, char **argv) | |||
368 | xwrite(STDOUT_FILENO, tailbuf, nread); | 373 | xwrite(STDOUT_FILENO, tailbuf, nread); |
369 | } | 374 | } |
370 | } while (++i < nfiles); | 375 | } while (++i < nfiles); |
371 | } | 376 | } /* while (1) */ |
377 | |||
372 | if (ENABLE_FEATURE_CLEAN_UP) { | 378 | if (ENABLE_FEATURE_CLEAN_UP) { |
373 | free(fds); | 379 | free(fds); |
374 | free(tailbuf); | 380 | free(tailbuf); |
375 | } | 381 | } |
376 | return G.status; | 382 | return G.exitcode; |
377 | } | 383 | } |