1 /* simple .tar.gz extractor for gretl -- based on untgz.c from zlib
2    distribution.
3 */
4 
5 #include "libgretl.h"
6 
7 #include <string.h>
8 #include <time.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <utime.h>
16 #include <dirent.h>
17 
18 /* values used in typeflag field */
19 
20 #define REGTYPE	 '0'		/* regular file */
21 #define AREGTYPE '\0'		/* regular file */
22 #define LNKTYPE  '1'		/* link */
23 #define SYMTYPE  '2'		/* reserved */
24 #define CHRTYPE  '3'		/* character special */
25 #define BLKTYPE  '4'		/* block special */
26 #define DIRTYPE  '5'		/* directory */
27 #define FIFOTYPE '6'		/* FIFO special */
28 #define CONTTYPE '7'		/* reserved */
29 
30 #define BLOCKSIZE 512
31 
32 struct tar_header
33 {				/* byte offset */
34     char name[100];		/*   0 */
35     char mode[8];               /* 100 */
36     char uid[8];                /* 108 */
37     char gid[8];                /* 116 */
38     char size[12];		/* 124 */
39     char mtime[12];		/* 136 */
40     char chksum[8];		/* 148 */
41     char typeflag;		/* 156 */
42     char linkname[100];		/* 157 */
43     char magic[6];		/* 257 */
44     char version[2];		/* 263 */
45     char uname[32];		/* 265 */
46     char gname[32];		/* 297 */
47     char devmajor[8];		/* 329 */
48     char devminor[8];		/* 337 */
49     char prefix[155];		/* 345 */
50 };
51 
52 union tar_buffer {
53     char buffer[BLOCKSIZE];
54     struct tar_header header;
55 };
56 
57 struct attr_item {
58     char *fname;
59     int mode;
60     time_t time;
61 };
62 
63 /* helper functions */
64 
getoct(char * p,int width)65 static int getoct (char *p, int width)
66 {
67     int result = 0;
68     char c;
69 
70     while (width--) {
71 	c = *p++;
72 	if (c == ' ')
73 	    continue;
74 	if (c == 0)
75 	    break;
76 	result = result * 8 + (c - '0');
77     }
78 
79     return result;
80 }
81 
untar(gzFile in)82 static int untar (gzFile in)
83 {
84     union tar_buffer buffer;
85     int getheader = 1;
86     int len, remaining = 0;
87     FILE *outfile = NULL;
88     char fname[BLOCKSIZE];
89     int tarmode = 0;
90     time_t tartime = (time_t) 0;
91     int err = 0;
92 
93     while (!err) {
94 	len = gzread(in, &buffer, BLOCKSIZE);
95 
96 	if (len < 0) {
97 	    gretl_errmsg_set(gzerror(in, &err));
98 	    return E_FOPEN;
99 	} else if (len != BLOCKSIZE) {
100 	    gretl_errmsg_set("gzread: incomplete block read");
101 	    return E_DATA;
102 	}
103 
104 	if (getheader == 1) {
105 	    if (len == 0 || buffer.header.name[0] == '\0') {
106 		break;
107 	    }
108 	    tarmode = getoct(buffer.header.mode, 8);
109 	    tartime = (time_t) getoct(buffer.header.mtime, 12);
110 	    strcpy(fname, buffer.header.name);
111 
112 	    switch (buffer.header.typeflag) {
113 	    case DIRTYPE:
114 		err = gretl_mkdir(fname);
115 		break;
116 	    case REGTYPE:
117 	    case AREGTYPE:
118 		remaining = getoct(buffer.header.size, 12);
119 		if (remaining) {
120 		    outfile = gretl_fopen(fname, "wb");
121 		    if (outfile == NULL) {
122 			/* try creating directory */
123 			char *p = strrchr(fname, '/');
124 
125 			if (p != NULL) {
126 			    *p = '\0';
127 			    err = gretl_mkdir(fname);
128 			    *p = '/';
129 			    outfile = gretl_fopen(fname, "wb");
130 			}
131 		    }
132 		    if (outfile != NULL) {
133 			fprintf(stderr, "Extracting %s\n", fname);
134 		    } else {
135 			fprintf(stderr, "Couldn't create %s\n", fname);
136 			err = E_FOPEN;
137 		    }
138 		} else {
139 		    outfile = NULL;
140 		}
141 		/* could have no contents */
142 		getheader = (remaining)? 0 : 1;
143 		break;
144 	    }
145 	} else {
146 	    unsigned bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
147 
148 	    if (outfile != NULL) {
149 		if (fwrite(&buffer, 1, bytes, outfile) != bytes) {
150 		    fprintf(stderr, "error writing to %s\n", fname);
151 		    fclose(outfile);
152 		    gretl_remove(fname);
153 		    err = E_DATA;
154 		    break;
155 		}
156 	    }
157 	    remaining -= bytes;
158 	    if (remaining == 0) {
159 		getheader = 1;
160 		if (outfile != NULL) {
161 		    struct utimbuf settime;
162 
163 		    settime.actime = settime.modtime = tartime;
164 		    fclose(outfile);
165 		    outfile = NULL;
166 		    utime(fname, &settime);
167 		    chmod(fname, tarmode);
168 		}
169 	    }
170 	}
171     }
172 
173     return err;
174 }
175 
gretl_untar(const char * fname)176 int gretl_untar (const char *fname)
177 {
178     gzFile fz = gretl_gzopen(fname, "rb");
179     int err = 0;
180 
181     if (fz == NULL) {
182 	gretl_errmsg_sprintf("Couldn't gzopen %s", fname);
183 	err = E_FOPEN;
184     } else {
185 	err = untar(fz);
186 	gzclose(fz);
187     }
188 
189     return err;
190 }
191