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