]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Merge r1396 from libarchive/trunk: significant rework of examples/untar.c,
authorTim Kientzle <kientzle@gmail.com>
Sat, 29 Aug 2009 04:08:13 +0000 (00:08 -0400)
committerTim Kientzle <kientzle@gmail.com>
Sat, 29 Aug 2009 04:08:13 +0000 (00:08 -0400)
the minimal libarchive-based untar.c program.  (Not to be confused with
contrib/untar.c which is an even more minimal untar program that doesn't
use libarchive.  I have got to rename one of them someday....)

SVN-Revision: 1397

examples/untar.c

index 88f6dc26de0137d52e7eb64d31b0bc891309d35a..d6870de45657e686f7d59caf31d4ab6c7cf506dc 100644 (file)
@@ -4,29 +4,52 @@
  */
 
 /*
- * This is a compact tar extraction program whose primary goal is
- * small size.  Statically linked, it can be under 64k, depending 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 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.)
+ * This is a compact tar extraction program using libarchive whose
+ * primary goal is small executable size.  Statically linked, it can
+ * 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.  (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, 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>
@@ -44,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;
 
@@ -133,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)
@@ -154,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");
@@ -176,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)
 {
@@ -202,6 +237,22 @@ errmsg(const char *m)
        write(2, m, strlen(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)
 {