]> git.ipfire.org Git - thirdparty/libarchive.git/blame - contrib/untar.c
Change CMAKE_BUILD_TYPE comparison to be case-insensitive (#2130)
[thirdparty/libarchive.git] / contrib / untar.c
CommitLineData
42d53127
TK
1/*
2 * This file is in the public domain. Use it as you see fit.
3 */
4
28d15c9a 5/*
4eb60e13
TK
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.
28d15c9a
TK
13 *
14 * To compile: cc -o untar untar.c
15 *
16 * Usage: untar <archive>
17 *
4eb60e13
TK
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 *
28d15c9a
TK
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
d67c6ea3 39#if defined(_WIN32) && !defined(__CYGWIN__)
bbc74679
TK
40#include <windows.h>
41#endif
a902fcd4 42
bbc74679 43#define BLOCKSIZE 512
7befaaf3 44
bbc74679 45/* System call to create a directory. */
28d15c9a 46static int
bbc74679
TK
47system_mkdir(char *pathname, int mode)
48{
49#if defined(_WIN32) && !defined(__CYGWIN__)
50 (void)mode; /* UNUSED */
51 return _mkdir(pathname);
52#else
53 return mkdir(pathname, mode);
54#endif
55}
56
57/* Parse an octal number, ignoring leading and trailing nonsense. */
58static unsigned long
28d15c9a
TK
59parseoct(const char *p, size_t n)
60{
bbc74679 61 unsigned long i = 0;
28d15c9a 62
025ba555 63 while ((*p < '0' || *p > '7') && n > 0) {
28d15c9a
TK
64 ++p;
65 --n;
66 }
67 while (*p >= '0' && *p <= '7' && n > 0) {
68 i *= 8;
69 i += *p - '0';
70 ++p;
71 --n;
72 }
73 return (i);
74}
75
76/* Returns true if this is 512 zero bytes. */
77static int
78is_end_of_archive(const char *p)
79{
80 int n;
bbc74679 81 for (n = 0; n < BLOCKSIZE; ++n)
28d15c9a
TK
82 if (p[n] != '\0')
83 return (0);
84 return (1);
85}
86
87/* Create a directory, including parent directories as necessary. */
88static void
89create_dir(char *pathname, int mode)
90{
91 char *p;
92 int r;
93
94 /* Strip trailing '/' */
95 if (pathname[strlen(pathname) - 1] == '/')
96 pathname[strlen(pathname) - 1] = '\0';
97
98 /* Try creating the directory. */
bbc74679 99 r = system_mkdir(pathname, mode);
28d15c9a
TK
100 if (r != 0) {
101 /* On failure, try creating parent directory. */
102 p = strrchr(pathname, '/');
103 if (p != NULL) {
104 *p = '\0';
105 create_dir(pathname, 0755);
106 *p = '/';
bbc74679 107 r = system_mkdir(pathname, mode);
28d15c9a
TK
108 }
109 }
110 if (r != 0)
111 fprintf(stderr, "Could not create directory %s\n", pathname);
112}
113
114/* Create a file, including parent directory as necessary. */
115static FILE *
116create_file(char *pathname, int mode)
117{
118 FILE *f;
96be772d 119 f = fopen(pathname, "wb+");
28d15c9a
TK
120 if (f == NULL) {
121 /* Try creating parent dir and then creating file. */
122 char *p = strrchr(pathname, '/');
123 if (p != NULL) {
124 *p = '\0';
125 create_dir(pathname, 0755);
126 *p = '/';
96be772d 127 f = fopen(pathname, "wb+");
28d15c9a
TK
128 }
129 }
130 return (f);
131}
132
133/* Verify the tar checksum. */
134static int
135verify_checksum(const char *p)
136{
137 int n, u = 0;
bbc74679 138 for (n = 0; n < BLOCKSIZE; ++n) {
28d15c9a
TK
139 if (n < 148 || n > 155)
140 /* Standard tar checksum adds unsigned bytes. */
141 u += ((unsigned char *)p)[n];
142 else
143 u += 0x20;
144
145 }
bbc74679 146 return (u == (int)parseoct(p + 148, 8));
28d15c9a
TK
147}
148
149/* Extract a tar archive. */
4eb60e13 150static void
28d15c9a
TK
151untar(FILE *a, const char *path)
152{
bbc74679 153 char buff[BLOCKSIZE];
28d15c9a
TK
154 FILE *f = NULL;
155 size_t bytes_read;
bbc74679 156 unsigned long filesize;
28d15c9a
TK
157
158 printf("Extracting from %s\n", path);
159 for (;;) {
bbc74679
TK
160 bytes_read = fread(buff, 1, BLOCKSIZE, a);
161 if (bytes_read < BLOCKSIZE) {
28d15c9a 162 fprintf(stderr,
bbc74679
TK
163 "Short read on %s: expected %d, got %d\n",
164 path, BLOCKSIZE, (int)bytes_read);
28d15c9a
TK
165 return;
166 }
167 if (is_end_of_archive(buff)) {
168 printf("End of %s\n", path);
169 return;
170 }
171 if (!verify_checksum(buff)) {
172 fprintf(stderr, "Checksum failure\n");
173 return;
174 }
175 filesize = parseoct(buff + 124, 12);
176 switch (buff[156]) {
bbc74679
TK
177 case '1':
178 printf(" Ignoring hardlink %s\n", buff);
179 break;
180 case '2':
181 printf(" Ignoring symlink %s\n", buff);
182 break;
183 case '3':
184 printf(" Ignoring character device %s\n", buff);
28d15c9a 185 break;
bbc74679
TK
186 case '4':
187 printf(" Ignoring block device %s\n", buff);
188 break;
189 case '5':
190 printf(" Extracting dir %s\n", buff);
191 create_dir(buff, (int)parseoct(buff + 100, 8));
192 filesize = 0;
193 break;
194 case '6':
195 printf(" Ignoring FIFO %s\n", buff);
196 break;
197 default:
198 printf(" Extracting file %s\n", buff);
199 f = create_file(buff, (int)parseoct(buff + 100, 8));
200 break;
28d15c9a
TK
201 }
202 while (filesize > 0) {
bbc74679
TK
203 bytes_read = fread(buff, 1, BLOCKSIZE, a);
204 if (bytes_read < BLOCKSIZE) {
28d15c9a 205 fprintf(stderr,
bbc74679
TK
206 "Short read on %s: Expected %d, got %d\n",
207 path, BLOCKSIZE, (int)bytes_read);
28d15c9a
TK
208 return;
209 }
bbc74679
TK
210 if (filesize < BLOCKSIZE)
211 bytes_read = (size_t)filesize;
28d15c9a
TK
212 if (f != NULL) {
213 if (fwrite(buff, 1, bytes_read, f)
bbc74679 214 != bytes_read)
28d15c9a
TK
215 {
216 fprintf(stderr, "Failed write\n");
217 fclose(f);
218 f = NULL;
219 }
220 }
bbc74679 221 filesize -= bytes_read;
28d15c9a
TK
222 }
223 if (f != NULL) {
224 fclose(f);
225 f = NULL;
226 }
227 }
228}
229
230int
231main(int argc, char **argv)
232{
233 FILE *a;
234
235 ++argv; /* Skip program name */
236 for ( ;*argv != NULL; ++argv) {
96be772d 237 a = fopen(*argv, "rb");
28d15c9a
TK
238 if (a == NULL)
239 fprintf(stderr, "Unable to open %s\n", *argv);
240 else {
241 untar(a, *argv);
242 fclose(a);
243 }
244 }
245 return (0);
246}