1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 2002-2013 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * dss file support
23  *
24  * Glenn Fowler
25  * AT&T Research
26  */
27 
28 #include "dsshdr.h"
29 
30 #include <ls.h>
31 #include <pzip.h>
32 
33 /*
34  * not open for read
35  */
36 
37 static int
noreadf(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)38 noreadf(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
39 {
40 	if (disc->errorf)
41 		(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "%s: not open for read", file->path);
42 	return -1;
43 }
44 
45 /*
46  * empty input
47  */
48 
49 static int
nullreadf(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)50 nullreadf(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
51 {
52 	return 0;
53 }
54 
55 /*
56  * not open for write
57  */
58 
59 static int
nowritef(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)60 nowritef(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
61 {
62 	if (disc->errorf)
63 		(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "%s: not open for write", file->path);
64 	return -1;
65 }
66 
67 /*
68  * file open
69  */
70 
71 Dssfile_t*
dssfopen(Dss_t * dss,const char * path,Sfio_t * io,Dssflags_t flags,Dssformat_t * format)72 dssfopen(Dss_t* dss, const char* path, Sfio_t* io, Dssflags_t flags, Dssformat_t* format)
73 {
74 	Dssfile_t*	file;
75 	Vmalloc_t*	vm;
76 	char*		s;
77 	size_t		n;
78 	int		i;
79 	struct stat	st;
80 	Sfdisc_t	top;
81 	char		buf[PATH_MAX];
82 
83 	if (flags & DSS_FILE_WRITE)
84 	{
85 		if (io)
86 		{
87 			memset(&top, 0, sizeof(top));
88 			if (sfdisc(io, &top))
89 			{
90 				n = top.disc == &dss->state->compress_preferred;
91 				sfdisc(io, SF_POPDISC);
92 				if (n)
93 				{
94 					sfdisc(io, SF_POPDISC);
95 					sfdczip(io, path, dss->meth->compress ? dss->meth->compress : "gzip", dss->disc->errorf);
96 				}
97 			}
98 		}
99 		if (dss->flags & DSS_APPEND)
100 			flags |= DSS_FILE_APPEND;
101 	}
102 	if (!path || !*path || streq(path, "-"))
103 	{
104 		if (flags & DSS_FILE_WRITE)
105 		{
106 			if (io)
107 				path = "output-stream";
108 			else
109 			{
110 				path = "/dev/stdout";
111 				io = sfstdout;
112 			}
113 		}
114 		else if (io)
115 			path = "input-stream";
116 		else
117 		{
118 			path = "/dev/stdin";
119 			io = sfstdin;
120 		}
121 		flags |= DSS_FILE_KEEP;
122 	}
123 	else if (io)
124 		flags |= DSS_FILE_KEEP;
125 	else if (flags & DSS_FILE_WRITE)
126 	{
127 		if (!(io = sfopen(NiL, path, (flags & DSS_FILE_APPEND) ? "a" : "w")))
128 		{
129 			if (dss->disc->errorf)
130 				(*dss->disc->errorf)(NiL, dss->disc, ERROR_SYSTEM|2, "%s: cannot open", path);
131 			return 0;
132 		}
133 	}
134 	else if (!(io = dssfind(path, "", DSS_VERBOSE, buf, sizeof(buf), dss->disc)))
135 		return 0;
136 	else
137 		path = (const char*)buf;
138 	if (!(vm = vmopen(Vmdcheap, Vmbest, 0)))
139 	{
140 		if (dss->disc->errorf)
141 			(*dss->disc->errorf)(NiL, dss->disc, ERROR_SYSTEM|2, "out of space");
142 		return 0;
143 	}
144 	if (!(file = vmnewof(vm, 0, Dssfile_t, 1, strlen(path) + 1)))
145 	{
146 		if (dss->disc->errorf)
147 			(*dss->disc->errorf)(NiL, dss->disc, ERROR_SYSTEM|2, "out of space");
148 		if (!(flags & DSS_FILE_KEEP))
149 			sfclose(io);
150 		vmclose(vm);
151 		return 0;
152 	}
153 	strcpy(file->path = (char*)(file + 1), path);
154 	file->dss = dss;
155 	file->vm = vm;
156 	file->io = io;
157 	file->flags = flags;
158 	if (flags & DSS_FILE_WRITE)
159 	{
160 		if (!(file->format = format) && !(file->format = dss->format))
161 		{
162 			if (dss->disc->errorf)
163 				(*dss->disc->errorf)(NiL, dss->disc, 2, "output method format must be specified");
164 			if (!(flags & DSS_FILE_KEEP))
165 				sfclose(io);
166 			return 0;
167 		}
168 		file->readf = noreadf;
169 		file->writef = file->format->writef;
170 	}
171 	else
172 	{
173 		if (sfsize(file->io) || !fstat(sffileno(file->io), &st) && (S_ISFIFO(st.st_mode)
174 #ifdef S_ISSOCK
175 			|| S_ISSOCK(st.st_mode)
176 #endif
177 			))
178 		{
179 			if (sfdczip(file->io, file->path, NiL, dss->disc->errorf) < 0)
180 			{
181 				if (dss->disc->errorf)
182 					(*dss->disc->errorf)(NiL, dss->disc, ERROR_SYSTEM|2, "%s: inflate error", file->path);
183 				dssfclose(file);
184 				return 0;
185 			}
186 			s = sfreserve(file->io, SF_UNBOUND, SF_LOCKR);
187 			n = sfvalue(file->io);
188 			if (!s)
189 			{
190 				if (n && dss->disc->errorf)
191 					(*dss->disc->errorf)(NiL, dss->disc, ERROR_SYSTEM|2, "%s: cannot peek", file->path);
192 				dssfclose(file);
193 				return 0;
194 			}
195 			for (file->format = (Dssformat_t*)dtfirst(dss->meth->formats); file->format && !(i = (*file->format->identf)(file, s, n, dss->disc)); file->format = (Dssformat_t*)dtnext(dss->meth->formats, file->format));
196 			sfread(file->io, s, 0);
197 			if (!file->format)
198 			{
199 				if (dss->disc->errorf)
200 					(*dss->disc->errorf)(NiL, dss->disc, 2, "%s: unknown %s format", file->path, dss->meth->name);
201 				dssfclose(file);
202 				return 0;
203 			}
204 			if (i < 0)
205 				return 0;
206 			if (format && format != file->format)
207 			{
208 				if (dss->disc->errorf)
209 					(*dss->disc->errorf)(NiL, dss->disc, 2, "%s: %s file format %s incompatible with %s", file->path, dss->meth->name, file->format->name, format->name);
210 				dssfclose(file);
211 				return 0;
212 			}
213 			if ((dss->flags & DSS_VERBOSE) && dss->disc->errorf)
214 				(*dss->disc->errorf)(dss, dss->disc, 1, "%s: %s method %s format", file->path, dss->meth->name, file->format->name);
215 			file->readf = file->format->readf;
216 		}
217 		else
218 		{
219 			file->format = format ? format : dss->format ? dss->format : (Dssformat_t*)dtfirst(dss->meth->formats);
220 			file->readf = nullreadf;
221 		}
222 		file->writef = nowritef;
223 		if (!dss->format)
224 			dss->format = file->format;
225 	}
226 	if (!file->format)
227 	{
228 		if (dss->disc->errorf)
229 			(*dss->disc->errorf)(NiL, dss->disc, 2, "%s: %s method did not set file format", file->path, dss->meth->name);
230 		dssfclose(file);
231 		return 0;
232 	}
233 	file->record.file = file;
234 	if ((*file->format->openf)(file, dss->disc))
235 	{
236 		dssfclose(file);
237 		return 0;
238 	}
239 	return file;
240 }
241 
242 /*
243  * file close
244  */
245 
246 int
dssfclose(Dssfile_t * file)247 dssfclose(Dssfile_t* file)
248 {
249 	int		r;
250 	Dss_t*		dss;
251 
252 	if (!file)
253 		return -1;
254 	dss = file->dss;
255 	if (!file->io)
256 		r = -1;
257 	else
258 	{
259 		r = file->format ? (*file->format->closef)(file, dss->disc) : 0;
260 		if ((file->flags & DSS_FILE_WRITE) && sfsync(file->io))
261 		{
262 			if (dss->disc->errorf)
263 				(*dss->disc->errorf)(NiL, dss->disc, ERROR_SYSTEM|2, "%s: write error", file->path);
264 			r = -1;
265 		}
266 		if (!(file->flags & DSS_FILE_KEEP))
267 			sfclose(file->io);
268 		if (!r && (file->flags & DSS_FILE_ERROR))
269 			r = -1;
270 	}
271 	vmclose(file->vm);
272 	return r;
273 }
274 
275 /*
276  * file read
277  */
278 
279 Dssrecord_t*
dssfread(Dssfile_t * file)280 dssfread(Dssfile_t* file)
281 {
282 	int	r;
283 
284 	file->count++;
285 	file->offset += file->length;
286 	if ((r = (*file->readf)(file, &file->record, file->dss->disc)) <= 0)
287 	{
288 		if (r < 0)
289 			file->flags |= DSS_FILE_ERROR;
290 		file->count--;
291 		return 0;
292 	}
293 	file->length = sftell(file->io) - file->offset;
294 	return &file->record;
295 }
296 
297 /*
298  * file write
299  */
300 
301 int
dssfwrite(Dssfile_t * file,Dssrecord_t * record)302 dssfwrite(Dssfile_t* file, Dssrecord_t* record)
303 {
304 	return (*file->writef)(file, record, file->dss->disc);
305 }
306 
307 /*
308  * file tell
309  */
310 
311 Sfoff_t
dssftell(Dssfile_t * file)312 dssftell(Dssfile_t* file)
313 {
314 	return file->seekf ? (*file->seekf)(file, DSS_TELL, file->dss->disc) : file->offset;
315 }
316 
317 /*
318  * file seek
319  */
320 
321 int
dssfseek(Dssfile_t * file,Sfoff_t offset)322 dssfseek(Dssfile_t* file, Sfoff_t offset)
323 {
324 	return (file->seekf ? (*file->seekf)(file, offset, file->dss->disc) : sfseek(file->io, offset, SEEK_SET)) == offset ? 0 : -1;
325 }
326 
327 /*
328  * save record state
329  */
330 
331 Dssrecord_t*
dsssave(Dssrecord_t * record)332 dsssave(Dssrecord_t* record)
333 {
334 	return record->file->format->savef ? (*record->file->format->savef)(record->file, record, record->file->dss->disc) : (Dssrecord_t*)0;
335 }
336 
337 /*
338  * drop saved record
339  */
340 
341 int
dssdrop(Dssrecord_t * record)342 dssdrop(Dssrecord_t* record)
343 {
344 	return record->file->format->dropf ? (*record->file->format->dropf)(record->file, record, record->file->dss->disc) : -1;
345 }
346 
347 /*
348  * common diagnostics
349  */
350 
351 Dssrecord_t*
dss_no_fread(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)352 dss_no_fread(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
353 {
354 	if (disc->errorf)
355 		(*disc->errorf)(NiL, disc, 2, "%s: %s file format %s read not implemented", file->path, file->dss->meth->name, file->format->name);
356 	return 0;
357 }
358 
359 int
dss_no_fwrite(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)360 dss_no_fwrite(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
361 {
362 	if (disc->errorf)
363 		(*disc->errorf)(NiL, disc, 2, "%s: %s file format %s write not implemented", file->path, file->dss->meth->name, file->format->name);
364 	return -1;
365 }
366 
367 Sfoff_t
dss_no_fseek(Dssfile_t * file,Sfoff_t offset,Dssdisc_t * disc)368 dss_no_fseek(Dssfile_t* file, Sfoff_t offset, Dssdisc_t* disc)
369 {
370 	if (disc->errorf)
371 		(*disc->errorf)(NiL, disc, 2, "%s: %s file format %s seek not implemented", file->path, file->dss->meth->name, file->format->name);
372 	return -1;
373 }
374