[RFC] tail.c size reduction
Denys Vlasenko
vda.linux at googlemail.com
Mon Sep 3 19:13:16 UTC 2007
On Sunday 02 September 2007 20:20, Tito wrote:
> Hi to all,
> this is a drop in replacement for tail.c.
> It is a result of a few sleepless nights of hacking
> that started by looking at and trying
> to understand mjn3's code.
> At the end I rewrote tail following a different
> italian creative and instinctive way.
> bloat-o-meter is not that bad:
>
> * scripts/bloat-o-meter busybox_old busybox_unstripped
> * function old new delta
> * xmalloc_fgetc - 40 +40
> * .rodata 122102 122134 +32
> * header_fmt 13 - -13
> * eat_num 37 - -37
> * tail_read 126 - -126
> * tail_main 1109 784 -325
> * ------------------------------------------------------------------------------
> * (add/remove: 1/3 grow/shrink: 1/1 up/down: 72/-501) Total: -429 bytes
>
> The functionality should be the same as the original, at least
> that is what my little testing says, but as the size reduction
> is that big I fear that i've overlooked something very stupid somewhere.
> That's why i'm posting it to the list for auditing and inspection by
> better coders than me that will see my obvious errors.
do {
/* With no FILE or When FILE is -, read standard input. */
if (argc == 0 || LONE_DASH(*argv)) {
opt |= ~0x1; /* unset FOLLOW */
llist_add_to_end(&flist, stdin);
llist_add_to_end(&nlist, (char *)bb_msg_standard_input);
continue;
}
file = fopen_or_warn(*argv, "r");
if (!file) {
status = EXIT_FAILURE;
continue;
}
/* Create lists of FILE* pointers and file names to reuse for -f FOLLOW */
llist_add_to_end(&flist, file);
llist_add_to_end(&nlist, *argv);
} while (*++argv);
If you run tail with no arguments, you go into first if and then "contunue".
Then you do ++argv ... and happily continue into environment vector which
is typically immediately follows argv[] in memory.
cat. deals with it like this:
static const char *const argv_dash[] = { "-", NULL };
if (!*argv)
argv = (char**) &argv_dash;
do {
if (LONE_DASH(*argv)) ...
Size: I think this
do {
/* With no FILE or When FILE is -, read standard input. */
if (argc == 0 || LONE_DASH(*argv)) {
Will be smaller in this form:
do {
char *arg = *argv;
/* With no FILE or When FILE is -, read standard input. */
if (!arg || LONE_DASH(arg)) {
arg is better than *argv since gcc can have doubts that *argv doesn't
change across function calls. Help it.
Why "!arg" instead of "argc == 0":
You need arg for LONE_DASH(), thus gcc pulls it into register, thus
!arg is as easy to evaluate as argc == 0, but does not require gcc
to allocate another register for argc.
But ultimate test in bloatcheck, of course.
while (flist) {
count = 0;
/* if we are reading stdin print the header before reading as read can block */
if ((FILE*)flist->data == stdin)
tail_xprint_header(fmt + first_file, nlist->data);
Like arg above, temporary FILE *curfile = (FILE*)flist->data;
may be smaller, and will be more readable for sure.
while ((line = (((COUNT_BYTES) ? xmalloc_fgetc : xmalloc_fgets)((FILE*)flist->data)))) {
malloc for each byte? :(
/* Show if there was an error while reading */
if (ferror((FILE*)flist->data)) {
bb_perror_msg("%s", nlist->data);
status = EXIT_FAILURE;
}
/* Create a list of the lines or chars */
llist_add_to_end(&slist, line);
/* If we exceed our -n or -c value remove one element from the head of the list */
if (++count > n)
free(llist_pop(&slist));
what will happen if count wraps around?
"malloc for each byte" and count problems can be solved
together: always store lines, not chars, and deal with
COUNT_BYTES case by printing only a part of first line of output,
so that total output size is correct.
}
/* Print the header if needed */
if ((FILE*)flist->data != stdin
&& ((VERBOSE && argc > 0) || (VERBOSE && slist && argc < 0))
)
tail_xprint_header(fmt + first_file, nlist->data);
((VERBOSE && argc > 0) || (VERBOSE && slist && argc < 0)) == VERBOSE && ((argc > 0) || (slist && argc < 0))
/* Zero first file flag */
first_file = 0;
/* Print what we have read */
while ((line = llist_pop(&slist))) {
tail_xprint_header("%s", line);
free(line);
}
/* Was the file truncated ? */
if (!slist && lseek(fileno((FILE*)flist->data), 0, SEEK_END)
< lseek(fileno((FILE*)flist->data), 0, SEEK_CUR)
)
bb_error_msg("file truncated");
Hard to read, and racy (which llseek is done first?).
if (!FOLLOW) {
/* Close the current file */
fclose_if_not_stdin(llist_pop(&flist));
/* Move the head of the list to the next file name */
llist_pop(&nlist);
} else {
/* FOLLOW */
/* Sleep for approximately S seconds (default 1) between iterations */
USE_FEATURE_FANCY_TAIL(sleep(xatou(str_s));)
/* Move FILE* pointer from the top to the end fo the list */
llist_add_to_end(&flist, llist_pop(&flist));
/* Move filename from the top to the end fo the list */
llist_add_to_end(&nlist, llist_pop(&nlist));
/* Decrement argc as this is our indicator of the first complete iteration of the list */
argc--;
}
}
--
vda
More information about the busybox
mailing list