Created
July 24, 2014 14:37
-
-
Save javiermon/ae212daf66275f468616 to your computer and use it in GitHub Desktop.
untar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * "untar" is an extremely simple tar extractor: | |
| * * A single C source file, so it should be easy to compile | |
| * and run on any system with a C compiler. | |
| * * Extremely portable standard C. The only non-ANSI function | |
| * used is mkdir(). | |
| * * Reads basic ustar tar archives. | |
| * * Does not require libarchive or any other special library. | |
| * | |
| * To compile: cc -o untar untar.c | |
| * | |
| * Usage: untar destination <archive> | |
| * | |
| * In particular, this program should be sufficient to extract the | |
| * distribution for libarchive, allowing people to bootstrap | |
| * libarchive on systems that do not already have a tar program. | |
| * | |
| * To unpack libarchive-x.y.z.tar.gz: | |
| * * gunzip libarchive-x.y.z.tar.gz | |
| * * untar libarchive-x.y.z.tar | |
| * | |
| * Written by Tim Kientzle, March 2009. | |
| * | |
| * Released into the public domain. | |
| */ | |
| /* These are all highly standard and portable headers. */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| /* This is for mkdir(); this may need to be changed for some platforms. */ | |
| #include <sys/stat.h> /* For mkdir() */ | |
| /* Parse an octal number, ignoring leading and trailing nonsense. */ | |
| static int | |
| parseoct(const char *p, size_t n) | |
| { | |
| int i = 0; | |
| while (*p < '0' || *p > '7') { | |
| ++p; | |
| --n; | |
| } | |
| while (*p >= '0' && *p <= '7' && n > 0) { | |
| i *= 8; | |
| i += *p - '0'; | |
| ++p; | |
| --n; | |
| } | |
| return (i); | |
| } | |
| /* Returns true if this is 512 zero bytes. */ | |
| static int | |
| is_end_of_archive(const char *p) | |
| { | |
| int n; | |
| for (n = 511; n >= 0; --n) | |
| if (p[n] != '\0') | |
| return (0); | |
| return (1); | |
| } | |
| /* Create a directory, including parent directories as necessary. */ | |
| static void | |
| create_dir(char *pathname, int mode, const char *destination) | |
| { | |
| char *p; | |
| int r; | |
| char fullpath[512]; | |
| /* sanity checks */ | |
| if (pathname == NULL) | |
| { | |
| return; | |
| } | |
| if ((strcmp(pathname, ".") || strcmp(pathname, "./")) || | |
| (strcmp(pathname, "..") || strcmp(pathname, "../"))) | |
| { | |
| return; | |
| } | |
| if (snprintf(fullpath, 512, "%s/%s", destination, pathname) >= 512) | |
| { | |
| return; | |
| } | |
| /* Strip trailing '/' */ | |
| if (fullpath[strlen(fullpath) - 1] == '/') | |
| fullpath[strlen(fullpath) - 1] = '\0'; | |
| /* Try creating the directory. */ | |
| r = mkdir(fullpath, mode); | |
| if (r != 0) { | |
| /* On failure, try creating parent directory. */ | |
| p = strrchr(fullpath, '/'); | |
| if (p != NULL) { | |
| *p = '\0'; | |
| create_dir(fullpath, 0755, fullpath); | |
| *p = '/'; | |
| r = mkdir(fullpath, mode); | |
| } | |
| } | |
| if (r != 0) | |
| fprintf(stderr, "Could not create directory %s\n", fullpath); | |
| } | |
| /* Create a file, including parent directory as necessary. */ | |
| static FILE * | |
| create_file(char *pathname, const char *destination) | |
| { | |
| FILE *f; | |
| char fullpath[512]; | |
| if (snprintf(fullpath, 512, "%s/%s", destination, pathname) >= 512) | |
| { | |
| return NULL; | |
| } | |
| f = fopen(fullpath, "w+"); | |
| if (f == NULL) { | |
| /* Try creating parent dir and then creating file. */ | |
| char *p = strrchr(fullpath, '/'); | |
| if (p != NULL) { | |
| *p = '\0'; | |
| create_dir(fullpath, 0755, fullpath); | |
| *p = '/'; | |
| f = fopen(fullpath, "w+"); | |
| } | |
| } | |
| return (f); | |
| } | |
| /* Verify the tar checksum. */ | |
| static int | |
| verify_checksum(const char *p) | |
| { | |
| int n, u = 0; | |
| for (n = 0; n < 512; ++n) { | |
| if (n < 148 || n > 155) | |
| /* Standard tar checksum adds unsigned bytes. */ | |
| u += ((unsigned char *)p)[n]; | |
| else | |
| u += 0x20; | |
| } | |
| return (u == parseoct(p + 148, 8)); | |
| } | |
| /* Extract a tar archive. */ | |
| static int | |
| untar(FILE *a, const char *path, const char *destination) | |
| { | |
| char buff[512]; | |
| FILE *f = NULL; | |
| size_t bytes_read; | |
| int filesize, mode; | |
| printf("Extracting from %s\n", path); | |
| for (;;) { | |
| bytes_read = fread(buff, 1, 512, a); | |
| if (bytes_read < 512) { | |
| fprintf(stderr, | |
| "Short read on %s: expected 512, got %d\n", | |
| path, bytes_read); | |
| return -1; | |
| } | |
| if (is_end_of_archive(buff)) { | |
| printf("End of %s\n", path); | |
| return 0; | |
| } | |
| if (!verify_checksum(buff)) { | |
| fprintf(stderr, "Checksum failure\n"); | |
| return -1; | |
| } | |
| filesize = parseoct(buff + 124, 12); | |
| switch (buff[156]) { | |
| case '1': | |
| printf(" Ignoring hardlink %s\n", buff); | |
| break; | |
| case '2': | |
| printf(" Ignoring symlink %s\n", buff); | |
| break; | |
| case '3': | |
| printf(" Ignoring character device %s\n", buff); | |
| break; | |
| case '4': | |
| printf(" Ignoring block device %s\n", buff); | |
| break; | |
| case '5': | |
| printf(" Extracting dir %s\n", buff); | |
| create_dir(buff, parseoct(buff + 100, 8), destination); | |
| filesize = 0; | |
| break; | |
| case '6': | |
| printf(" Ignoring FIFO %s\n", buff); | |
| break; | |
| default: | |
| printf(" Extracting file %s\n", buff); | |
| mode = parseoct(buff + 100, 8); | |
| f = create_file(buff, destination); | |
| break; | |
| } | |
| while (filesize > 0) { | |
| bytes_read = fread(buff, 1, 512, a); | |
| if (bytes_read < 512) { | |
| fprintf(stderr, | |
| "Short read on %s: Expected 512, got %d\n", | |
| path, bytes_read); | |
| return -1; | |
| } | |
| if (filesize < 512) | |
| bytes_read = filesize; | |
| if (f != NULL) { | |
| if (fwrite(buff, 1, bytes_read, f) | |
| != bytes_read) | |
| { | |
| fprintf(stderr, "Failed write\n"); | |
| fclose(f); | |
| f = NULL; | |
| } | |
| } | |
| filesize -= bytes_read; | |
| } | |
| if (f != NULL) { | |
| if (mode != 0 && (fchmod(fileno(f), mode) < 0)) | |
| { | |
| fprintf(stderr, | |
| "Unable to chmod %u file\n", mode); | |
| } | |
| fclose(f); | |
| f = NULL; | |
| } | |
| } | |
| return 0; | |
| } | |
| int | |
| main(int argc, char **argv) | |
| { | |
| FILE *a; | |
| char *destination; | |
| struct stat sb; | |
| ++argv; /* Skip program name */ | |
| destination = *argv; | |
| if (stat(destination, &sb) != 0 || !S_ISDIR(sb.st_mode)) | |
| { | |
| fprintf(stderr, "Unable to open %s\n", destination); | |
| return -1; | |
| } | |
| ++argv; /* Skip destination folder name */ | |
| for ( ;*argv != NULL; ++argv) { | |
| a = fopen(*argv, "r"); | |
| if (a == NULL) | |
| fprintf(stderr, "Unable to open %s\n", *argv); | |
| else { | |
| untar(a, *argv, destination); | |
| fclose(a); | |
| } | |
| } | |
| return (0); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment