]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libarchive/contrib/untar.c
Copy libarchive from vendor branch to contrib
[FreeBSD/FreeBSD.git] / contrib / libarchive / contrib / untar.c
1 /*
2  * "untar" is an extremely simple tar extractor:
3  *  * A single C source file, so it should be easy to compile
4  *    and run on any system with a C compiler.
5  *  * Extremely portable standard C.  The only non-ANSI function
6  *    used is mkdir().
7  *  * Reads basic ustar tar archives.
8  *  * Does not require libarchive or any other special library.
9  *
10  * To compile: cc -o untar untar.c
11  *
12  * Usage:  untar <archive>
13  *
14  * In particular, this program should be sufficient to extract the
15  * distribution for libarchive, allowing people to bootstrap
16  * libarchive on systems that do not already have a tar program.
17  *
18  * To unpack libarchive-x.y.z.tar.gz:
19  *    * gunzip libarchive-x.y.z.tar.gz
20  *    * untar libarchive-x.y.z.tar
21  *
22  * Written by Tim Kientzle, March 2009.
23  *
24  * Released into the public domain.
25  */
26
27 /* These are all highly standard and portable headers. */
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 /* This is for mkdir(); this may need to be changed for some platforms. */
33 #include <sys/stat.h>  /* For mkdir() */
34
35 /* Parse an octal number, ignoring leading and trailing nonsense. */
36 static int
37 parseoct(const char *p, size_t n)
38 {
39         int i = 0;
40
41         while (*p < '0' || *p > '7') {
42                 ++p;
43                 --n;
44         }
45         while (*p >= '0' && *p <= '7' && n > 0) {
46                 i *= 8;
47                 i += *p - '0';
48                 ++p;
49                 --n;
50         }
51         return (i);
52 }
53
54 /* Returns true if this is 512 zero bytes. */
55 static int
56 is_end_of_archive(const char *p)
57 {
58         int n;
59         for (n = 511; n >= 0; --n)
60                 if (p[n] != '\0')
61                         return (0);
62         return (1);
63 }
64
65 /* Create a directory, including parent directories as necessary. */
66 static void
67 create_dir(char *pathname, int mode)
68 {
69         char *p;
70         int r;
71
72         /* Strip trailing '/' */
73         if (pathname[strlen(pathname) - 1] == '/')
74                 pathname[strlen(pathname) - 1] = '\0';
75
76         /* Try creating the directory. */
77         r = mkdir(pathname, mode);
78
79         if (r != 0) {
80                 /* On failure, try creating parent directory. */
81                 p = strrchr(pathname, '/');
82                 if (p != NULL) {
83                         *p = '\0';
84                         create_dir(pathname, 0755);
85                         *p = '/';
86                         r = mkdir(pathname, mode);
87                 }
88         }
89         if (r != 0)
90                 fprintf(stderr, "Could not create directory %s\n", pathname);
91 }
92
93 /* Create a file, including parent directory as necessary. */
94 static FILE *
95 create_file(char *pathname, int mode)
96 {
97         FILE *f;
98         f = fopen(pathname, "w+");
99         if (f == NULL) {
100                 /* Try creating parent dir and then creating file. */
101                 char *p = strrchr(pathname, '/');
102                 if (p != NULL) {
103                         *p = '\0';
104                         create_dir(pathname, 0755);
105                         *p = '/';
106                         f = fopen(pathname, "w+");
107                 }
108         }
109         return (f);
110 }
111
112 /* Verify the tar checksum. */
113 static int
114 verify_checksum(const char *p)
115 {
116         int n, u = 0;
117         for (n = 0; n < 512; ++n) {
118                 if (n < 148 || n > 155)
119                         /* Standard tar checksum adds unsigned bytes. */
120                         u += ((unsigned char *)p)[n];
121                 else
122                         u += 0x20;
123
124         }
125         return (u == parseoct(p + 148, 8));
126 }
127
128 /* Extract a tar archive. */
129 static void
130 untar(FILE *a, const char *path)
131 {
132         char buff[512];
133         FILE *f = NULL;
134         size_t bytes_read;
135         int filesize;
136
137         printf("Extracting from %s\n", path);
138         for (;;) {
139                 bytes_read = fread(buff, 1, 512, a);
140                 if (bytes_read < 512) {
141                         fprintf(stderr,
142                             "Short read on %s: expected 512, got %d\n",
143                             path, bytes_read);
144                         return;
145                 }
146                 if (is_end_of_archive(buff)) {
147                         printf("End of %s\n", path);
148                         return;
149                 }
150                 if (!verify_checksum(buff)) {
151                         fprintf(stderr, "Checksum failure\n");
152                         return;
153                 }
154                 filesize = parseoct(buff + 124, 12);
155                 switch (buff[156]) {
156                 case '1':
157                         printf(" Ignoring hardlink %s\n", buff);
158                         break;
159                 case '2':
160                         printf(" Ignoring symlink %s\n", buff);
161                         break;
162                 case '3':
163                         printf(" Ignoring character device %s\n", buff);
164                                 break;
165                 case '4':
166                         printf(" Ignoring block device %s\n", buff);
167                         break;
168                 case '5':
169                         printf(" Extracting dir %s\n", buff);
170                         create_dir(buff, parseoct(buff + 100, 8));
171                         filesize = 0;
172                         break;
173                 case '6':
174                         printf(" Ignoring FIFO %s\n", buff);
175                         break;
176                 default:
177                         printf(" Extracting file %s\n", buff);
178                         f = create_file(buff, parseoct(buff + 100, 8));
179                         break;
180                 }
181                 while (filesize > 0) {
182                         bytes_read = fread(buff, 1, 512, a);
183                         if (bytes_read < 512) {
184                                 fprintf(stderr,
185                                     "Short read on %s: Expected 512, got %d\n",
186                                     path, bytes_read);
187                                 return;
188                         }
189                         if (filesize < 512)
190                                 bytes_read = filesize;
191                         if (f != NULL) {
192                                 if (fwrite(buff, 1, bytes_read, f)
193                                     != bytes_read)
194                                 {
195                                         fprintf(stderr, "Failed write\n");
196                                         fclose(f);
197                                         f = NULL;
198                                 }
199                         }
200                         filesize -= bytes_read;
201                 }
202                 if (f != NULL) {
203                         fclose(f);
204                         f = NULL;
205                 }
206         }
207 }
208
209 int
210 main(int argc, char **argv)
211 {
212         FILE *a;
213
214         ++argv; /* Skip program name */
215         for ( ;*argv != NULL; ++argv) {
216                 a = fopen(*argv, "r");
217                 if (a == NULL)
218                         fprintf(stderr, "Unable to open %s\n", *argv);
219                 else {
220                         untar(a, *argv);
221                         fclose(a);
222                 }
223         }
224         return (0);
225 }