From 88e766bfea73167e39c8ebeddbd2120cb5918c92 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Fri, 27 Feb 2026 07:22:19 +0000 Subject: [PATCH] Bug fixes and added extracting files --- lib.c | 11 ++ tar.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- tar.h | 2 +- 3 files changed, 322 insertions(+), 13 deletions(-) diff --git a/lib.c b/lib.c index 0e13aa4..fe43aee 100755 --- a/lib.c +++ b/lib.c @@ -24,6 +24,7 @@ enum options { OPTION_IGNORED = 0, OPTION_APPEND, OPTION_CREATE, + OPTION_EXTRACT, OPTION_FILE, OPTION_HELP, OPTION_MODE, @@ -38,9 +39,11 @@ static struct option opts[] = { { "-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 }, @@ -110,6 +113,7 @@ static void print_help (int exitval) { 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"); @@ -237,6 +241,13 @@ void parse_args (int argc, char **argv, int optind) { } + case OPTION_EXTRACT: { + + state->extract = 1; + break; + + } + case OPTION_FILE: { if (state->target) { diff --git a/tar.c b/tar.c index 8ec2cb3..5a64281 100755 --- a/tar.c +++ b/tar.c @@ -66,6 +66,7 @@ static int write_null_bytes (FILE *fp, int n) { #if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) # include # include +# include # include #ifndef S_IFDIR @@ -156,6 +157,8 @@ static void add_entry (FILE *tarfp, const char *root, const char *path) { 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); } @@ -219,7 +222,7 @@ static void add_entry (FILE *tarfp, const char *root, const char *path) { 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; } @@ -285,6 +288,147 @@ _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 @@ -310,7 +454,7 @@ static void add_entry (FILE *tarfp, const char *root, const char *path) { p += 2; } - while (*p == '/') { + while (*p == '/' || *p == '\\') { p++; } @@ -318,7 +462,7 @@ static void add_entry (FILE *tarfp, const char *root, const char *path) { len = strlen (root); - if (root[len - 1] == '/') { + if (root[len - 1] == '/' || root[len - 1] == '\\') { len += (strlen (p) + 1); @@ -373,6 +517,8 @@ static void add_entry (FILE *tarfp, const char *root, const char *path) { 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); } @@ -454,7 +600,7 @@ static void add_entry (FILE *tarfp, const char *root, const char *path) { 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; } @@ -520,6 +666,152 @@ _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 @@ -553,14 +845,14 @@ int main (int argc, char **argv) { } - 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; @@ -580,13 +872,19 @@ int main (int argc, char **argv) { } - 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); diff --git a/tar.h b/tar.h index bf05a87..3a62503 100755 --- a/tar.h +++ b/tar.h @@ -10,7 +10,7 @@ struct tar_state { long nb_paths; char *target, *root, *mode; - int append, create; + int append, create, extract; }; -- 2.34.1