diff options
Diffstat (limited to 'examples/untar.c')
-rw-r--r-- | examples/untar.c | 98 |
1 files changed, 74 insertions, 24 deletions
diff --git a/examples/untar.c b/examples/untar.c index 468c1079..d6870de4 100644 --- a/examples/untar.c +++ b/examples/untar.c @@ -6,28 +6,50 @@ /* * This is a compact tar extraction program using libarchive whose * primary goal is small executable size. Statically linked, it can - * be under 64k, depending on how cleanly factored your system - * libraries are. Note that this uses the standard libarchive, + * be very small, depending in large part on how cleanly factored your + * system libraries are. Note that this uses the standard libarchive, * without any special recompilation. The only functional concession * is that this program uses the uid/gid from the archive instead of - * doing uname/gname lookups. (Call + * doing uname/gname lookups. (Add a call to * archive_write_disk_set_standard_lookup() to enable uname/gname * lookups, but be aware that this can add 500k or more to a static - * executable, depending on the system libraries.) + * executable, depending on the system libraries, since user/group + * lookups frequently pull in password, YP/LDAP, networking, and DNS + * resolver libraries.) * * To build: - * gcc -static -Wall -o untar untar.c -larchive - * strip untar + * $ gcc -static -Wall -o untar untar.c -larchive + * $ strip untar + * + * NOTE: On some systems, you may need to add additional flags + * to ensure that untar.c is compiled the same way as libarchive + * was compiled. In particular, Linux users will probably + * have to add -D_FILE_OFFSET_BITS=64 to the command line above. * * For fun, statically compile the following simple hello.c program - * and compare the size. (On my system, the result is 89k, untar is - * 69k.) + * using the same flags as for untar and compare the size: * * #include <stdio.h> * int main(int argc, char **argv) { * printf("hello, world\n"); * return(0); * } + * + * You may be even more surprised by the compiled size of true.c listed here: + * + * int main(int argc, char **argv) { + * return (0); + * } + * + * On a slightly customized FreeBSD 5 system that I used around + * 2005, hello above compiled to 89k compared to untar of 69k. So at + * that time, libarchive's tar reader and extract-to-disk routines + * compiled to less code than printf(). + * + * On my FreeBSD development system today (August, 2009): + * hello: 195024 bytes + * true: 194912 bytes + * untar: 259924 bytes */ #include <sys/types.h> @@ -45,9 +67,11 @@ __FBSDID("$FreeBSD$"); static void errmsg(const char *); static void extract(const char *filename, int do_extract, int flags); +static void fail(const char *, const char *, int); static int copy_data(struct archive *, struct archive *); static void msg(const char *); static void usage(void); +static void warn(const char *, const char *); static int verbose = 0; @@ -134,20 +158,16 @@ extract(const char *filename, int do_extract, int flags) */ if (filename != NULL && strcmp(filename, "-") == 0) filename = NULL; - if ((r = archive_read_open_file(a, filename, 10240))) { - errmsg(archive_error_string(a)); - errmsg("\n"); - exit(r); - } + if ((r = archive_read_open_file(a, filename, 10240))) + fail("archive_read_open_file()", + archive_error_string(a), r); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; - if (r != ARCHIVE_OK) { - errmsg(archive_error_string(a)); - errmsg("\n"); - exit(1); - } + if (r != ARCHIVE_OK) + fail("archive_read_next_header()", + archive_error_string(a), 1); if (verbose && do_extract) msg("x "); if (verbose || !do_extract) @@ -155,9 +175,16 @@ extract(const char *filename, int do_extract, int flags) if (do_extract) { r = archive_write_header(ext, entry); if (r != ARCHIVE_OK) - errmsg(archive_error_string(a)); - else + warn("archive_write_header()", + archive_error_string(a)); + else { copy_data(a, ext); + r = archive_write_finish_entry(ext); + if (r != ARCHIVE_OK) + fail("archive_write_finish_entry()", + archive_error_string(ext), 1); + } + } if (verbose || !do_extract) msg("\n"); @@ -177,20 +204,27 @@ copy_data(struct archive *ar, struct archive *aw) for (;;) { r = archive_read_data_block(ar, &buff, &size, &offset); - if (r == ARCHIVE_EOF) { - errmsg(archive_error_string(ar)); + if (r == ARCHIVE_EOF) return (ARCHIVE_OK); - } if (r != ARCHIVE_OK) return (r); r = archive_write_data_block(aw, buff, size, offset); if (r != ARCHIVE_OK) { - errmsg(archive_error_string(ar)); + warn("archive_write_data_block()", + archive_error_string(aw)); return (r); } } } +/* + * These reporting functions use low-level I/O; on some systems, this + * is a significant code reduction. Of course, on many server and + * desktop operating systems, malloc() and even crt rely on printf(), + * which in turn pulls in most of the rest of stdio, so this is not an + * optimization at all there. (If you're going to pay 100k or more + * for printf() anyway, you may as well use it!) + */ static void msg(const char *m) { @@ -204,6 +238,22 @@ errmsg(const char *m) } static void +warn(const char *f, const char *m) +{ + errmsg(f); + errmsg(" failed: "); + errmsg(m); + errmsg("\n"); +} + +static void +fail(const char *f, const char *m, int r) +{ + warn(f, m); + exit(r); +} + +static void usage(void) { const char *m = "Usage: untar [-tvx] [-f file] [file]\n"; |