aboutsummaryrefslogtreecommitdiff
path: root/coreutils
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
committerRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
commit0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2 (patch)
tree6709ddd6071a9c238ba69233540bbcfe560c6a44 /coreutils
parent67758035a4fe040c6ac69b39d61bcd6bddd7b827 (diff)
parent56a3b82e9692a25ef9c9269e88feac0d579ce8e8 (diff)
downloadbusybox-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.c853
-rw-r--r--coreutils/od.c7
-rw-r--r--coreutils/od_bloaty.c443
-rw-r--r--coreutils/tail.c36
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 @@
118enum { 118enum {
119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ 119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
120 120
121SPLIT_DIR = 1,
122SPLIT_FILE = 0, 121SPLIT_FILE = 0,
122SPLIT_DIR = 1,
123SPLIT_SUBDIR = 2, 123SPLIT_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,
137LIST_DATE_TIME = 1 << 8, 137LIST_DATE_TIME = 1 << 8,
138LIST_FULLTIME = 1 << 9, 138LIST_FULLTIME = 1 << 9,
139LIST_SYMLINK = 1 << 10, 139LIST_SYMLINK = 1 << 10,
140LIST_FILETYPE = 1 << 11, 140LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */
141LIST_EXEC = 1 << 12, 141LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
142LIST_MASK = (LIST_EXEC << 1) - 1, 142LIST_MASK = (LIST_CLASSIFY << 1) - 1,
143 143
144/* what files will be displayed */ 144/* what files will be displayed */
145DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ 145DISP_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 */
309struct dnode { 309struct 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
318struct globals { 352struct 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
336enum { 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
343enum {
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(&current_time_t);) \ 377 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
353} while (0) 378} while (0)
354 379
355 380
356static 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
441static char append_char(mode_t mode) 431static 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
455static 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 */
481static 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
491static 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) */
511static 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
546static 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
589static 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
598static unsigned calc_name_len(const char *name) 445static 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 */
659static NOINLINE unsigned list_single(const struct dnode *dn) 505static 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
818static void showfiles(struct dnode **dn, unsigned nfiles) 661static 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 720static 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) */
887static 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
907static struct dnode **list_dir(const char *, unsigned *); 778static unsigned count_dirs(struct dnode **dn, int which)
908
909static 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 806static 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
816static 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); 836static 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
875static 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
918static void dnsort(struct dnode **dn, int size)
919{
920 qsort(dn, size, sizeof(*dn), sortcmp);
921}
922
923static 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) */
955static struct dnode **list_dir(const char *path, unsigned *nfiles_p) 934static 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) */
1004static 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
1022static 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
1020int ls_main(int argc UNUSED_PARAM, char **argv) 1065int 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
22Based on od.c from coreutils-5.2.1
23Top bloat sources:
24 21
2500000073 t parse_old_offset 22/* #include "libbb.h" - done in od.c */
260000007b t get_lcm 23#define assert(a) ((void)0)
2700000090 r long_options
2800000092 t print_named_ascii
29000000bf t print_ascii
3000000168 t write_block
3100000366 t decode_format_string
3200000a71 T od_main
33
34Tested for compat with coreutils 6.3
35using this script. Minor differences fixed.
36 24
37#!/bin/sh
38echo STD
39time /path/to/coreutils/od \
40...params... \
41>std
42echo Exit code $?
43echo BBOX
44time ./busybox od \
45...params... \
46>bbox
47echo Exit code $?
48diff -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
38enum {
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
59typedef long double longdouble_t; 75typedef long double longdouble_t;
60typedef unsigned long long ulonglong_t; 76typedef 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
168static smallint flag_dump_strings; 184static smallint exit_code;
169/* Non-zero if an old-style 'pseudo-address' was specified. */
170static smallint flag_pseudo_start;
171static 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. */
175static smallint verbose;
176static smallint ioerror;
177 185
178static size_t string_min; 186static 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. */
181static size_t n_specs; 189static size_t n_specs;
@@ -186,7 +194,11 @@ static struct tspec *spec;
186static void (*format_address)(off_t, char); 194static 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
189static off_t pseudo_offset; 198static off_t pseudo_offset;
199#else
200enum { 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)
223static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { 235static 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
968static int
969parse_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
1086static void
1087read_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)
1119static void 1068static void
1120dump_strings(off_t address, off_t end_offset) 1069dump_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
1140static int
1141parse_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
1182int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1173int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1183int od_main(int argc, char **argv) 1174int 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
61struct globals { 61struct 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}