OPTION_IGNORED = 0,
OPTION_APPEND,
OPTION_CREATE,
+ OPTION_EXTRACT,
OPTION_FILE,
OPTION_HELP,
OPTION_MODE,
{ "-c", OPTION_CREATE, OPTION_NO_ARG },
{ "-f", OPTION_FILE, OPTION_HAS_ARG },
{ "-r", OPTION_APPEND, OPTION_NO_ARG },
+ { "-x", OPTION_EXTRACT, OPTION_NO_ARG },
{ "--append", OPTION_APPEND, OPTION_NO_ARG },
{ "--create", OPTION_CREATE, OPTION_NO_ARG },
+ { "--extract", OPTION_EXTRACT, OPTION_NO_ARG },
{ "--file", OPTION_FILE, OPTION_HAS_ARG },
{ "--help", OPTION_HELP, OPTION_NO_ARG },
{ "--mode", OPTION_MODE, OPTION_HAS_ARG },
fprintf (stderr, "Options:\n\n");
fprintf (stderr, " -c, --create Create a new archive.\n");
/*fprintf (stderr, " -r, --append Append files to the end of the archive.\n");*/
+ fprintf (stderr, " -x, --extract Extract files dfrom an archive.\n");
fprintf (stderr, "\n");
fprintf (stderr, " -f ARCHIVE, --file ARCHIVE Use archive file or device ARCHIVE.\n");
fprintf (stderr, " --help Print this help information.\n");
}
+ case OPTION_EXTRACT: {
+
+ state->extract = 1;
+ break;
+
+ }
+
case OPTION_FILE: {
if (state->target) {
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
# include <sys/stat.h>
# include <dirent.h>
+# include <fcntl.h>
# include <unistd.h>
#ifndef S_IFDIR
sprintf (header.checksum, "%06o", checksum (&header));
header.checksum[7] = ' ';
+ header.name[strlen(p)] = '/';
+
if (fwrite (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
report_at (program_name, 0, REPORT_ERROR, "failed to write header to '%s'", state->target);
}
if (!(fp = fopen (p2, "r+b"))) {
- report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", path);
+ report_at (program_name, 0, REPORT_WARNING, "failed to open '%s' for reading", path);
goto _end;
}
free (p2);
+}
+
+static void extract_files (FILE *tarfp, char *root) {
+
+ struct tar_raw_header header;
+
+ char *path, *p2;
+ int fp;
+
+ unsigned long len;
+ int mode;
+
+ if (root) {
+
+ len = strlen (root);
+
+ while (root[len - 1] == '/') {
+
+ root[len - 1] = '\0';
+ len--;
+
+ }
+
+ if (mkdir (root, 0755)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to root directory %s", root);
+ return;
+
+ }
+
+ p2 = xmalloc (len + 1);
+ sprintf (p2, "%s/", root);
+
+ } else {
+ p2 = xstrdup ("");
+ }
+
+ for (;;) {
+
+ if (feof (tarfp)) {
+ break;
+ }
+
+ if (fread (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to read archive header");
+ break;
+
+ }
+
+ if (!header.name[0]) {
+ break;
+ }
+
+ mode = strtol (header.mode, 0, 8);
+
+ path = xmalloc (strlen (p2) + strlen (header.name) + 1);
+ sprintf (path, "%s%s", p2, header.name);
+
+ printf ("extracting: %s\n", path);
+
+ if (path[strlen (path) - 1] == '/') {
+
+ path[strlen (path) - 1] = '\0';
+
+ if (mkdir (path, mode)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to create directory %s, %lx", path, ftell (tarfp));
+
+ free (path);
+ free (p2);
+
+ return;
+
+ }
+
+ } else {
+
+ unsigned long padding = 0, bytes, read;
+ unsigned char *data;
+
+ if ((fp = open (path, O_CREAT | O_WRONLY | O_TRUNC, mode)) < 0) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to create %s", path);
+
+ free (path);
+ free (p2);
+
+ return;
+
+ }
+
+ bytes = strtol (header.size, 0, 8);
+
+ if ((bytes % 512) != 0) {
+ padding = 512 - (bytes % 512);
+ }
+
+ data = xmalloc (512);
+
+ for (;;) {
+
+ memset (data, 0, 512);
+
+ if (bytes == 0) {
+ break;
+ }
+
+ read = (bytes > 512 ? 512 : bytes);
+
+ if (fread (data, 1, read, tarfp) != read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to read %lu bytes from %s", read, path);
+ break;
+
+ }
+
+ if (write (fp, data, read) != (long) read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to write %lu bytes to %s", read);
+ break;
+
+ }
+
+ bytes -= read;
+
+ }
+
+ fseek (tarfp, padding, SEEK_CUR);
+
+ free (data);
+ close (fp);
+
+ }
+
+ free (path);
+
+ }
+
+ free (p2);
+
}
#elif defined (_WIN32)
# include <windows.h>
p += 2;
}
- while (*p == '/') {
+ while (*p == '/' || *p == '\\') {
p++;
}
len = strlen (root);
- if (root[len - 1] == '/') {
+ if (root[len - 1] == '/' || root[len - 1] == '\\') {
len += (strlen (p) + 1);
sprintf (header.checksum, "%06o", checksum (&header));
header.checksum[7] = ' ';
+ header.name[strlen(p)] = '/';
+
if (fwrite (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
report_at (program_name, 0, REPORT_ERROR, "failed to write header to '%s'", state->target);
}
if (!(fp = fopen (p2, "r+b"))) {
- report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", path);
+ report_at (program_name, 0, REPORT_WARNING, "failed to open '%s' for reading", path);
goto _end;
}
free (p2);
+}
+
+static void extract_files (FILE *tarfp, char *root) {
+
+ struct tar_raw_header header;
+
+ char *path, *p2;
+ FILE *fp;
+
+ unsigned long len, i;
+
+ if (root) {
+
+ len = strlen (root);
+
+ while (root[len - 1] == '/' || root[len - 1] == '\\') {
+
+ root[len - 1] = '\0';
+ len--;
+
+ }
+
+ for (i = 0; i < len; i++) {
+
+ if (root[i] == '\\') {
+ root[i] = '/';
+ }
+
+ }
+
+ if (!CreateDirectory (root, NULL)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to root directory %s", root);
+ return;
+
+ }
+
+ p2 = xmalloc (len + 1);
+ sprintf (p2, "%s/", root);
+
+ } else {
+ p2 = xstrdup ("");
+ }
+
+ for (;;) {
+
+ if (feof (tarfp)) {
+ break;
+ }
+
+ if (fread (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to read archive header");
+ break;
+
+ }
+
+ if (!header.name[0]) {
+ break;
+ }
+
+ path = xmalloc (strlen (p2) + strlen (header.name) + 1);
+ sprintf (path, "%s%s", p2, header.name);
+
+ printf ("extracting: %s\n", path);
+
+ if (path[strlen (path) - 1] == '/') {
+
+ path[strlen (path) - 1] = '\0';
+
+ if (!CreateDirectory (path, NULL)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to create directory %s, %lx", path);
+
+ free (path);
+ free (p2);
+
+ return;
+
+ }
+
+ } else {
+
+ unsigned long padding = 0, bytes, read;
+ unsigned char *data;
+
+ if (!(fp = fopen (path, "w+b"))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to create %s", path);
+
+ free (path);
+ free (p2);
+
+ return;
+
+ }
+
+ bytes = strtol (header.size, 0, 8);
+
+ if ((bytes % 512) != 0) {
+ padding = 512 - (bytes % 512);
+ }
+
+ data = xmalloc (512);
+
+ for (;;) {
+
+ memset (data, 0, 512);
+
+ if (bytes == 0) {
+ break;
+ }
+
+ read = (bytes > 512 ? 512 : bytes);
+
+ if (fread (data, 1, read, tarfp) != read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to read %lu bytes from %s", read, path);
+ break;
+
+ }
+
+ if (fwrite (data, 1, read, fp) != read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to write %lu bytes to %s", read, state->target);
+ break;
+
+ }
+
+ bytes -= read;
+
+ }
+
+ fseek (tarfp, padding, SEEK_CUR);
+
+ free (data);
+ fclose (fp);
+
+ }
+
+ free (path);
+
+ }
+
+ free (p2);
+
}
#endif
}
- action = (state->create + state->append);
+ action = (state->create + state->append + state->extract);
if (action != 1) {
if (action < 1) {
- report_at (program_name, 0, REPORT_ERROR, "you must specify one of the '-cr' options");
+ report_at (program_name, 0, REPORT_ERROR, "you must specify one of the '-crx' options");
} else {
- report_at (program_name, 0, REPORT_ERROR, "you may not specify more than one '-cr' option");
+ report_at (program_name, 0, REPORT_ERROR, "you may not specify more than one '-crx' option");
}
return EXIT_FAILURE;
}
- if (state->append) { fseek (tarfp, 0, SEEK_END); }
+ if (state->extract) {
+ extract_files (tarfp, state->root);
+ } else {
- for (i = 0; i < state->nb_paths; i++) {
- add_entry (tarfp, state->root, state->paths[i]);
- }
+ if (state->append) { fseek (tarfp, 0, SEEK_END); }
+
+ for (i = 0; i < state->nb_paths; i++) {
+ add_entry (tarfp, state->root, state->paths[i]);
+ }
+
+ write_null_bytes (tarfp, sizeof (struct tar_raw_header) * 2);
- write_null_bytes (tarfp, sizeof (struct tar_raw_header) * 2);
+ }
fclose (tarfp);
return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS);