Bug fixes and added extracting files
authorRobert Pengelly <robertapengelly@hotmail.com>
Fri, 27 Feb 2026 07:22:19 +0000 (07:22 +0000)
committerRobert Pengelly <robertapengelly@hotmail.com>
Fri, 27 Feb 2026 07:22:19 +0000 (07:22 +0000)
lib.c
tar.c
tar.h

diff --git a/lib.c b/lib.c
index 0e13aa43e9b9381bb2117b263b4b0cb6226b7786..fe43aee9134250996b27d9f908c0ce971580ec68 100755 (executable)
--- 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 8ec2cb37bec506e440c7f8957b7abcc23aa974aa..5a642811eac9a9e4c3d876a0e149b33078568cb7 100755 (executable)
--- 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       <sys/stat.h>
 # include       <dirent.h>
+# include       <fcntl.h>
 # include       <unistd.h>
 
 #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       <windows.h>
@@ -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 bf05a87fbf8410679b18b1d720c2bdbbe23e6d24..3a625037a742c9de1202fbd8a96932fc58c7c439 100755 (executable)
--- 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;
 
 };