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