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
7
* * Reads basic ustar tar archives.
8
* * Does not require libarchive or any other special library.
10
* To compile: cc -o untar untar.c
12
* Usage: untar <archive>
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.
18
* To unpack libarchive-x.y.z.tar.gz:
19
* * gunzip libarchive-x.y.z.tar.gz
20
* * untar libarchive-x.y.z.tar
22
* Written by Tim Kientzle, March 2009.
24
* Released into the public domain.
27
/* These are all highly standard and portable headers. */
32
/* This is for mkdir(); this may need to be changed for some platforms. */
33
#include <sys/stat.h> /* For mkdir() */
35
/* Parse an octal number, ignoring leading and trailing nonsense. */
37
parseoct(const char *p, size_t n)
41
while (*p < '0' || *p > '7') {
45
while (*p >= '0' && *p <= '7' && n > 0) {
54
/* Returns true if this is 512 zero bytes. */
56
is_end_of_archive(const char *p)
59
for (n = 511; n >= 0; --n)
65
/* Create a directory, including parent directories as necessary. */
67
create_dir(char *pathname, int mode)
72
/* Strip trailing '/' */
73
if (pathname[strlen(pathname) - 1] == '/')
74
pathname[strlen(pathname) - 1] = '\0';
76
/* Try creating the directory. */
77
r = mkdir(pathname, mode);
80
/* On failure, try creating parent directory. */
81
p = strrchr(pathname, '/');
84
create_dir(pathname, 0755);
86
r = mkdir(pathname, mode);
90
fprintf(stderr, "Could not create directory %s\n", pathname);
93
/* Create a file, including parent directory as necessary. */
95
create_file(char *pathname, int mode)
98
f = fopen(pathname, "w+");
100
/* Try creating parent dir and then creating file. */
101
char *p = strrchr(pathname, '/');
104
create_dir(pathname, 0755);
106
f = fopen(pathname, "w+");
112
/* Verify the tar checksum. */
114
verify_checksum(const char *p)
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];
125
return (u == parseoct(p + 148, 8));
128
/* Extract a tar archive. */
130
untar(FILE *a, const char *path)
137
printf("Extracting from %s\n", path);
139
bytes_read = fread(buff, 1, 512, a);
140
if (bytes_read < 512) {
142
"Short read on %s: expected 512, got %d\n",
146
if (is_end_of_archive(buff)) {
147
printf("End of %s\n", path);
150
if (!verify_checksum(buff)) {
151
fprintf(stderr, "Checksum failure\n");
154
filesize = parseoct(buff + 124, 12);
157
printf(" Ignoring hardlink %s\n", buff);
160
printf(" Ignoring symlink %s\n", buff);
163
printf(" Ignoring character device %s\n", buff);
166
printf(" Ignoring block device %s\n", buff);
169
printf(" Extracting dir %s\n", buff);
170
create_dir(buff, parseoct(buff + 100, 8));
174
printf(" Ignoring FIFO %s\n", buff);
177
printf(" Extracting file %s\n", buff);
178
f = create_file(buff, parseoct(buff + 100, 8));
181
while (filesize > 0) {
182
bytes_read = fread(buff, 1, 512, a);
183
if (bytes_read < 512) {
185
"Short read on %s: Expected 512, got %d\n",
190
bytes_read = filesize;
192
if (fwrite(buff, 1, bytes_read, f)
195
fprintf(stderr, "Failed write\n");
200
filesize -= bytes_read;
210
main(int argc, char **argv)
214
++argv; /* Skip program name */
215
for ( ;*argv != NULL; ++argv) {
216
a = fopen(*argv, "r");
218
fprintf(stderr, "Unable to open %s\n", *argv);