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