update.
[chise/xemacs-chise.git.1] / nt / minitar.c
1
2 /* Minitar:  extract .tar.gz files on Win32 platforms. 
3    Uses zlib for decompression.
4    
5    This is very simple-minded, it ignores checksums, and any type of file 
6    that is not a plain file or a directory.  Nonetheless it is useful.
7
8    Author: Charles G. Waldman (cgw@pgt.com),  Aug 4 1998
9
10    This file is placed in the public domain; you can
11    do whatever you like with it.  There is NO WARRANTY. 
12    If it breaks, you get to keep both pieces */
13
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <io.h>
20 #ifdef WIN32_NATIVE
21 # include <direct.h>    /* For mkdir */
22 #endif
23
24 #include <zlib.h>
25
26 static void
27 Usage (char *name)
28 {
29   fprintf (stderr, "Usage: %s file.tar.gz [base-dir]\n", name);
30   fprintf (stderr, "\tExtracts the contents compressed tar file to base-dir\n");
31   exit (-1);
32 }
33
34
35 #define BLOCKSIZE 512
36 #define MAXNAMELEN 1024
37
38 static int
39 octal (char *str)
40 {
41   int ret = -1;
42   sscanf (str, "%o", &ret);
43   return ret;
44 }
45
46 /* this is like mkdir -p, except if there is no trailing slash,
47    the final component is assumed to be a file, rather than a
48    path component, so it is not created as a directory */
49
50 static int
51 makepath (char *path)
52 {
53   char tmp[MAXNAMELEN];
54   char *cp;
55
56   for (cp=path; cp; cp = (char*)strchr (cp+1, '/'))
57     {
58       if (!*cp)
59         break;
60       if (*cp != '/')
61         continue;
62       strncpy (tmp, path, cp-path);
63       tmp[cp-path] = '\0';
64       if (strlen (tmp) == 0)
65         continue;
66 #ifdef WIN32_NATIVE
67       if (mkdir (tmp))
68 #else
69         if (mkdir (tmp, 0777))
70 #endif
71           {
72             if (errno == EEXIST)
73               continue;
74             else
75               return -1;
76           }
77     }
78   return 0;
79 }
80
81
82
83 int
84 main (int argc, char **argv)
85 {
86   char fullname[MAXNAMELEN];
87   char *basedir = ".";
88   char *tarfile;
89   int size;
90   char osize[13];
91   char name[101];
92   char magic[7];
93   char type;
94   
95   gzFile *infile = (gzFile*)0;
96   FILE *outfile = (FILE*)0;
97
98   char block[BLOCKSIZE];
99   int nbytes, nread, nwritten;
100
101   int in_block = 0;
102   int directory = 0;
103
104   if (argc < 2 || argc > 3)
105     Usage (argv[0]);
106
107   tarfile = argv[1];
108   if (argc==3)
109     basedir = argv[2];
110
111   if (! (infile = gzopen (tarfile, "rb")))
112     {
113       fprintf (stderr, "Cannot open %s\n", tarfile);
114       exit (-2);
115     }
116   
117   while (1)
118     {
119       nread = gzread (infile, block, 512);
120
121       if (!in_block && nread == 0)
122         break;
123
124       if (nread != BLOCKSIZE)
125         {
126           fprintf (stderr, "Error: incomplete block read. Exiting.\n");
127           exit (-2);
128         }
129
130       if (!in_block)
131         {
132           if (block[0]=='\0')  /* We're done */
133             break;
134
135           strncpy (magic, block+257, 6);
136           magic[6] = '\0';
137           if (strcmp (magic, "ustar "))
138             {
139               fprintf (stderr,
140                        "Error: incorrect magic number in tar header. Exiting\n");
141               exit (-2);
142             }
143
144           strncpy (name, block, 100);
145           name[100] = '\0';
146           sprintf (fullname, "%s/%s", basedir, name);
147           printf ("%s\n", fullname);
148           type = block[156];
149       
150           switch (type)
151             {
152             case '0':
153             case '\0':
154               directory = 0;
155               break;
156             case '5':
157               directory = 1;
158               break;
159             default:
160               fprintf (stderr, "Error: unknown type flag %c. Exiting.\n", type);
161               exit (-2);
162               break;
163             }
164       
165           if (directory)
166             {
167               in_block = 0;
168         
169               /* makepath will ignore the final path component, so make sure 
170                  dirnames have a trailing slash */
171
172               if (fullname[strlen (fullname)-1] != '/')
173                 strcat (fullname, "/");
174               if (makepath (fullname))
175                 {
176                   fprintf (stderr, "Error: cannot create directory %s. Exiting.\n",
177                            fullname);
178                   exit (-2);
179                 }
180               continue;
181             }
182           else
183             { /*file */
184               in_block = 1;
185               if (outfile)
186                 {
187                   if (fclose (outfile))
188                     {
189                       fprintf (stderr, "Error: cannot close file %s. Exiting.\n",
190                                fullname);
191                       exit (-2);
192                     }
193                   outfile = (FILE*)0;
194                 }
195
196               if (!(outfile = fopen (fullname, "wb")))
197                 {
198                   /*try creating the directory, maybe it's not there */
199                   if (makepath (fullname))
200                     {
201                       fprintf (stderr, "Error: cannot create file %s. Exiting.\n",
202                                fullname);
203                       exit (-2);
204                     }
205                   /* now try again to open the file */
206                   if (!(outfile = fopen (fullname, "wb")))
207                     {
208                       fprintf (stderr, "Error: cannot create file %s. Exiting.\n",
209                                fullname);
210                       exit (-2);
211                     }
212                 }
213
214               strncpy (osize, block+124, 12);
215               osize[12] = '\0';
216               size = octal (osize);
217               if (size<0)
218                 {
219                   fprintf (stderr, "Error: invalid size in tar header. Exiting.\n");
220                   exit (-2);
221                 }
222               if (size==0)      /* file of size 0 is done */
223                 in_block = 0;
224             }
225         }
226       else
227         { /* write or continue writing file contents */
228           nbytes = size>512? 512:size;
229       
230           nwritten = fwrite (block, 1, nbytes, outfile);
231           if (nwritten != nbytes)
232             {
233               fprintf (stderr, "Error: only wrote %d bytes to file %s. Exiting.\n",
234                        nwritten, fullname);
235               exit (-2);
236             }
237           size -= nbytes;
238           if (size==0)
239             in_block = 0;
240         }
241     }
242   return 0;
243 }
244
245
246