pkgsrc - initial commit
[pkgsrc.git] / archivers / libarchive / files / examples / untar.c
1 /*
2  * This file is in the public domain.
3  * Use it as you wish.
4  */
5
6 /*
7  * This is a compact tar extraction program whose primary goal is
8  * small size.  Statically linked, it can be under 64k, depending on
9  * how cleanly factored your system libraries are.  Note that this
10  * uses the standard libarchive, without any special recompilation.
11  * The only functional concession is that this program uses the
12  * uid/gid from the archive instead of doing uname/gname lookups.
13  * (Call archive_write_disk_set_standard_lookup() to enable
14  * uname/gname lookups, but be aware that this can add 500k or more to
15  * a static executable, depending on the system libraries.)
16  *
17  * To build:
18  * gcc -static -Wall -o untar untar.c -larchive
19  * strip untar
20  *
21  * For fun, statically compile the following simple hello.c program
22  * and compare the size.  (On my system, the result is 89k, untar is
23  * 69k.)
24  *
25  * #include <stdio.h>
26  * int main(int argc, char **argv) {
27  *    printf("hello, world\n");
28  *    return(0);
29  * }
30  */
31
32 #include <sys/types.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/stat.h>
36
37 #include <archive.h>
38 #include <archive_entry.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 static void     errmsg(const char *);
46 static void     extract(const char *filename, int do_extract, int flags);
47 static int      copy_data(struct archive *, struct archive *);
48 static void     msg(const char *);
49 static void     usage(void);
50
51 static int verbose = 0;
52
53 int
54 main(int argc, const char **argv)
55 {
56         const char *filename = NULL;
57         int compress, flags, mode, opt;
58
59         (void)argc;
60         mode = 'x';
61         verbose = 0;
62         compress = '\0';
63         flags = ARCHIVE_EXTRACT_TIME;
64
65         /* Among other sins, getopt(3) pulls in printf(3). */
66         while (*++argv != NULL && **argv == '-') {
67                 const char *p = *argv + 1;
68
69                 while ((opt = *p++) != '\0') {
70                         switch (opt) {
71                         case 'f':
72                                 if (*p != '\0')
73                                         filename = p;
74                                 else
75                                         filename = *++argv;
76                                 p += strlen(p);
77                                 break;
78                         case 'p':
79                                 flags |= ARCHIVE_EXTRACT_PERM;
80                                 flags |= ARCHIVE_EXTRACT_ACL;
81                                 flags |= ARCHIVE_EXTRACT_FFLAGS;
82                                 break;
83                         case 't':
84                                 mode = opt;
85                                 break;
86                         case 'v':
87                                 verbose++;
88                                 break;
89                         case 'x':
90                                 mode = opt;
91                                 break;
92                         default:
93                                 usage();
94                         }
95                 }
96         }
97
98         switch (mode) {
99         case 't':
100                 extract(filename, 0, flags);
101                 break;
102         case 'x':
103                 extract(filename, 1, flags);
104                 break;
105         }
106
107         return (0);
108 }
109
110
111 static void
112 extract(const char *filename, int do_extract, int flags)
113 {
114         struct archive *a;
115         struct archive *ext;
116         struct archive_entry *entry;
117         int r;
118
119         a = archive_read_new();
120         ext = archive_write_disk_new();
121         archive_write_disk_set_options(ext, flags);
122         /*
123          * Note: archive_write_disk_set_standard_lookup() is useful
124          * here, but it requires library routines that can add 500k or
125          * more to a static executable.
126          */
127         archive_read_support_format_tar(a);
128         /*
129          * On my system, enabling other archive formats adds 20k-30k
130          * each.  Enabling gzip decompression adds about 20k.
131          * Enabling bzip2 is more expensive because the libbz2 library
132          * isn't very well factored.
133          */
134         if (filename != NULL && strcmp(filename, "-") == 0)
135                 filename = NULL;
136         if ((r = archive_read_open_file(a, filename, 10240))) {
137                 errmsg(archive_error_string(a));
138                 errmsg("\n");
139                 exit(r);
140         }
141         for (;;) {
142                 r = archive_read_next_header(a, &entry);
143                 if (r == ARCHIVE_EOF)
144                         break;
145                 if (r != ARCHIVE_OK) {
146                         errmsg(archive_error_string(a));
147                         errmsg("\n");
148                         exit(1);
149                 }
150                 if (verbose && do_extract)
151                         msg("x ");
152                 if (verbose || !do_extract)
153                         msg(archive_entry_pathname(entry));
154                 if (do_extract) {
155                         r = archive_write_header(ext, entry);
156                         if (r != ARCHIVE_OK)
157                                 errmsg(archive_error_string(a));
158                         else
159                                 copy_data(a, ext);
160                 }
161                 if (verbose || !do_extract)
162                         msg("\n");
163         }
164         archive_read_close(a);
165         archive_read_finish(a);
166         exit(0);
167 }
168
169 static int
170 copy_data(struct archive *ar, struct archive *aw)
171 {
172         int r;
173         const void *buff;
174         size_t size;
175         off_t offset;
176
177         for (;;) {
178                 r = archive_read_data_block(ar, &buff, &size, &offset);
179                 if (r == ARCHIVE_EOF) {
180                         errmsg(archive_error_string(ar));
181                         return (ARCHIVE_OK);
182                 }
183                 if (r != ARCHIVE_OK)
184                         return (r);
185                 r = archive_write_data_block(aw, buff, size, offset);
186                 if (r != ARCHIVE_OK) {
187                         errmsg(archive_error_string(ar));
188                         return (r);
189                 }
190         }
191 }
192
193 static void
194 msg(const char *m)
195 {
196         write(1, m, strlen(m));
197 }
198
199 static void
200 errmsg(const char *m)
201 {
202         write(2, m, strlen(m));
203 }
204
205 static void
206 usage(void)
207 {
208         const char *m = "Usage: untar [-tvx] [-f file] [file]\n";
209         errmsg(m);
210         exit(1);
211 }