1 /*@ file_case: input file encapsulator
3 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
34 #include "file_case.h"
38 #ifdef HAVE_DECL_GETC_UNLOCKED
39 # define _getc getc_unlocked
44 // Support decompression XXX configure should say `no popen() - no unpacking'
49 // (Enclosed by HAVE_UNPACK) Directly support decompression library layer?
50 // XXX We yet only support a zlib _LAYER, which is why we directly address zlib
51 // XXX functions instead of furtherly abstracting into a struct iolayer or sth.
52 // XXX If we would, that can only have read_buf() and close() and we should
53 // XXX deal with buffer handling entirely ourselfs, in which case even the
54 // XXX popen(3) code path could be enwrapped into struct iolayer; i.e., then
55 // XXX the entire public read interface could internally be driven by iolayer
68 char const *a_mode
; // Mode for fopen(3), if used
76 uint8_t zp_ext_len
; // Extension including `.' (<period>)
78 uint8_t zp_layer
; // Uses I/O layer (zlib)
83 static zproc
const _zprocs
[] = {
84 # define __X(L,C,E) {true, sizeof(E) -1, sizeof(C) -1, L, E, C}
85 # ifdef HAVE_UNPACK_BZ2
86 __X(0, "bzip2 -cdf", ".bz2"),
88 # ifdef HAVE_UNPACK_GZ
89 __X(HAVE_ZLIB
, "gzip -cdf", ".gz"),
91 # ifdef HAVE_UNPACK_XZ
92 __X(0, "xz -cdf", ".xz")
99 // Check wether path was explicitly specified with a packer extension.
100 // This returns a ternary: false only if we knew the extension, applied the
101 // zproc and that failed to perform, true otherwise (ap->a_fp is 2nd indicator)
102 static bool _is_ext(args
*ap
);
104 // Plain file didn't exist, iterate over the supported packer extensions
105 // and see if a matching file exists instead; NULL if not / on error.
106 // Note that ap->a_errno is ENOENT on entry and only overwritten if we run
107 // a zproc and that fails XXX ENOENT is blindly used in codebase, but not ISO C
108 static bool _try_all_ext(args
*ap
);
110 // Create a FILE* according to zp, return NULL on error
111 static args
* __run_zproc(args
*ap
, zproc
const *zp
);
113 // Callee needs seek()ing or STD I/O, unpack into temporary file, NULL on error
114 static args
* __unpack(args
*ap
);
115 #endif // HAVE_UNPACK
121 for (zproc
const *zp
= _zprocs
; zp
< _zprocs
+ NELEM(_zprocs
); ++zp
) {
122 size_t el
= zp
->zp_ext_len
;
124 if (ap
->a_path_len
<= el
)
126 if (memcmp(ap
->a_path
+ ap
->a_path_len
- el
, zp
->zp_ext
, el
))
129 ap
= __run_zproc(ap
, zp
);
136 _try_all_ext(args
*ap
)
138 for (zproc
const *zp
= _zprocs
; zp
< _zprocs
+ NELEM(_zprocs
); ++zp
) {
139 char *np
= new char[ap
->a_path_len
+ zp
->zp_ext_len
+1];
141 memcpy(np
, ap
->a_path
, ap
->a_path_len
);
144 memcpy(np
+ ap
->a_path_len
, zp
->zp_ext
, zp
->zp_ext_len
+1);
150 // That's our zproc, let it make the deal
151 char const *pb_save
= ap
->a_path
;
152 size_t pl_save
= ap
->a_path_len
;
154 ap
->a_path_len
= pl_save
+ zp
->zp_ext_len
;
155 if ((ap
= __run_zproc(ap
, zp
)) != NULL
) {
156 ap
->a_path
= pb_save
;
157 ap
->a_path_len
= pl_save
;
168 __run_zproc(args
*ap
, zproc
const *zp
)
172 if ((ap
->a_layer
= gzopen(ap
->a_path
, "rb")) == NULL
) {
175 } else if (ap
->a_flags
&
176 (file_case::mux_need_seek
| file_case::mux_need_stdio
))
180 char *np
= new char[zp
->zp_cmd_len
+ 1 + ap
->a_path_len
+1];
183 memcpy(np
, zp
->zp_cmd
, l
= zp
->zp_cmd_len
);
185 memcpy(np
+ l
, ap
->a_path
, ap
->a_path_len
+1);
187 if ((ap
->a_fp
= popen(np
, "r")) == NULL
) {
190 } else if (ap
->a_flags
& file_case::mux_need_seek
)
193 ap
->a_flags
|= file_case::fc_pipe
| file_case::fc_have_stdio
;
206 size_t const buf_len
= (BUFSIZ
+ 0 > 1 << 15) ? BUFSIZ
: 1 << 15;
207 uint8_t *buf
= new uint8_t[buf_len
];
209 // xtmpfile uses binary mode and fatal()s on error
210 FILE *decomp
= xtmpfile(NULL
, "groff_unpack"), *decomp_save
= decomp
;
215 if (ap
->a_layer
!= NULL
) {
216 int i
= gzread((gzFile
)ap
->a_layer
, buf
, buf_len
);
226 if ((oc
= fread(buf
, sizeof *buf
, buf_len
, ap
->a_fp
)) == 0) {
227 if (!feof(ap
->a_fp
)) {
234 if (decomp
!= NULL
) {
235 for (uint8_t *target
= buf
; oc
> 0;) {
236 size_t i
= fwrite(target
, sizeof *buf
, oc
, decomp
);
250 if (ap
->a_layer
!= NULL
) {
251 if (gzclose((gzFile
)ap
->a_layer
) != Z_OK
)
252 error("decompressor gzclose(3) failed");
256 if (pclose(ap
->a_fp
) != 0)
257 error("decompressor pipe pclose(3) didn't exit cleanly");
259 if (decomp
!= NULL
) {
260 ap
->a_flags
|= file_case::fc_have_stdio
;
261 rewind(ap
->a_fp
= decomp
);
271 #endif // HAVE_UNPACK
274 file_case::close(void)
276 assert((_file
!= NULL
&& _layer
== NULL
) ||
277 (_file
== NULL
&& _layer
!= NULL
));
279 if (!(_flags
& fc_const_path
))
283 if (_flags
& fc_dont_close
)
286 else if (_layer
!= NULL
)
287 rv
= (gzclose((gzFile
)_layer
) == Z_OK
);
290 else if (_flags
& fc_pipe
)
291 rv
= (pclose(_file
) == 0);
294 rv
= (fclose(_file
) == 0);
306 file_case::is_eof(void) const
311 rv
= (gzeof((gzFile
)_layer
) != 0);
314 rv
= (feof(_file
) != 0);
319 file_case::get_c(void)
324 rv
= gzgetc((gzFile
)_layer
);
332 file_case::unget_c(int c
)
337 rv
= gzungetc(c
, (gzFile
)_layer
);
340 rv
= ungetc(c
, _file
);
345 file_case::get_line(char *buf
, size_t buf_size
)
349 buf
= gzgets((gzFile
)_layer
, buf
, (int)buf_size
);
352 buf
= fgets(buf
, (int)buf_size
, _file
);
357 file_case::get_buf(void *buf
, size_t buf_size
)
361 if (_layer
!= NULL
) {
362 int i
= gzread((gzFile
)_layer
, buf
, (unsigned int)buf_size
);
363 rv
= (i
<= 0) ? 0 : (size_t)i
;
366 rv
= fread(buf
, 1, buf_size
, _file
);
371 file_case::rewind(void)
375 gzrewind((gzFile
)_layer
);
382 file_case::seek(long offset
, seek_whence whence
)
384 int x
= (whence
== seek_set
? SEEK_SET
:
385 (whence
== seek_cur
? SEEK_CUR
: SEEK_END
));
388 x
= gzseek((gzFile
)_layer
, (z_off_t
)offset
, x
);
391 x
= fseek(_file
, offset
, x
);
395 /*static*/ file_case
*
396 file_case::muxer(char const *path
, uint32_t flags
)
398 enum {tmpbit
= 1<<(_mux_freebit
+0)};
400 assert(!(flags
& (fc_dont_close
| fc_pipe
)));
401 assert(!(flags
& (fc_const_path
| fc_take_path
)) ||
402 !(flags
& fc_const_path
) != !(flags
& fc_take_path
));
403 assert(!(flags
& (mux_unpack
| mux_no_unpack
)) ||
404 !(flags
& mux_unpack
) != !(flags
& mux_no_unpack
));
406 if (path
== NULL
|| (path
[0] == '-' && path
[1] == '\0')) {
408 flags
&= ~fc_take_path
;
409 flags
|= fc_const_path
| tmpbit
;
410 } else if (!(flags
& (fc_const_path
| fc_take_path
))) {
411 path
= strsave(path
);
412 flags
|= fc_take_path
;
414 if (!(flags
& (mux_unpack
| mux_no_unpack
)))
415 flags
|= _mux_unpack_default
;
421 a
.a_path_len
= strlen(a
.a_path
= path
);
422 a
.a_mode
= (flags
& mux_need_binary
) ? "rb" : "r";
426 // Shorthand: support "-" to mean stdin
427 if (flags
& tmpbit
) {
429 if (flags
& mux_need_binary
)
430 SET_BINARY(fileno(stdin
));
432 a
.a_flags
|= fc_dont_close
| fc_const_path
| fc_have_stdio
;
436 // If we support unpacking then check wether the path already includes
437 // a packer's extension, i.e., explicitly. Anyway unpack then, despite flags
440 assert(a
.a_fp
== NULL
&& a
.a_layer
== NULL
);
443 if (a
.a_fp
!= NULL
|| a
.a_layer
!= NULL
)
449 if ((a
.a_fp
= fopen(a
.a_path
, a
.a_mode
)) != NULL
) {
450 a
.a_flags
|= fc_have_stdio
;
452 assert((a
.a_fp
!= NULL
&& a
.a_layer
== NULL
) ||
453 (a
.a_fp
== NULL
&& a
.a_layer
!= NULL
));
454 fcp
= new file_case(a
.a_fp
, path
, a
.a_flags
& fc_mask
); // XXX real path?
455 fcp
->_layer
= a
.a_layer
;
460 // Then auto-expand the given path if so desired
462 if (a
.a_errno
== ENOENT
&& (a
.a_flags
& mux_unpack
) && _try_all_ext(&a
))
467 if (!(a
.a_flags
& fc_const_path
))