29#define S_IFIFO _S_IFIFO
30#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
31#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
42#if defined(__APPLE__) || defined(__MACH__)
46#include <sys/proc_info.h>
63File::File (
Internal *i,
bool w,
int c,
int p, FILE *f,
const char *n)
68 close_file (c), child_pid (
p),
file (f), _name (strdup (n)),
69 _lineno (1), _bytes (0) {
78 if (stat (path, &buf))
80 if (access (path, R_OK))
89 else if (!
strcmp (path,
"/dev/null"))
98 if (stat (path, &buf))
99 res = ((errno == ENOENT) ? 0 : -2);
100 else if (S_ISDIR (buf.st_mode))
103 res = (access (path, W_OK) ? 4 : 0);
107 size_t len =
p - path;
108 char *dirname =
new char[len + 1];
111 if (stat (dirname, &buf))
113 else if (!S_ISDIR (buf.st_mode))
115 else if (access (dirname, W_OK))
117 else if (stat (path, &buf))
118 res = (errno == ENOENT) ? 0 : -3;
120 res = access (path, W_OK) ? 9 : 0;
130 int fd = fileno (file);
131 if (fstat (fd, &stat))
133 return S_ISFIFO (stat.st_mode);
144static int xzsig[] = {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, 0x00, EOF};
145static int bz2sig[] = {0x42, 0x5A, 0x68, EOF};
146static int gzsig[] = {0x1F, 0x8B, EOF};
147static int sig7z[] = {0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, EOF};
148static int lzmasig[] = {0x5D, EOF};
152 FILE *tmp = fopen (path,
"r");
154 WARNING (
"failed to open '%s' to check signature", path);
158 for (
const int *
p = sig; res && (*
p != EOF);
p++)
162 WARNING (
"file type signature check for '%s' failed", path);
168 if (stat (path, &buf))
170 return (
size_t) buf.st_size;
177 size_t prglen =
strlen (prg);
178 const char *c =
getenv (
"PATH");
182 char *e =
new char[len + 1];
185 for (
char *
p = e, *q; !res &&
p < e + len;
p = q) {
186 for (q =
p; *q && *q !=
':'; q++)
189 size_t pathlen = (q -
p) + prglen;
190 char *path =
new char[pathlen + 1];
191 snprintf (path, pathlen + 1,
"%s/%s",
p, prg);
204FILE *File::open_file (
Internal *internal,
const char *path,
207 return fopen (path,
mode);
210FILE *File::read_file (Internal *internal,
const char *path) {
211 MSG (
"opening file to read '%s'", path);
212 return open_file (internal, path,
"r");
215FILE *File::write_file (Internal *internal,
const char *path) {
216 MSG (
"opening file to write '%s'", path);
217 return open_file (internal, path,
"wb");
222void File::split_str (
const char *command, std::vector<char *> &argv) {
223 const char *c = command;
224 while (*c && *c ==
' ')
228 while (*
p && *
p !=
' ')
230 const size_t bytes =
p - c;
231 char *arg =
new char[bytes + 1];
232 (void)
strncpy (arg, c, bytes);
234 argv.push_back (arg);
235 while (*
p && *
p ==
' ')
241void File::delete_str_vector (std::vector<char *> &argv) {
242 for (
auto str : argv)
246FILE *File::open_pipe (Internal *internal,
const char *fmt,
247 const char *path,
const char *
mode) {
252 while (fmt[prglen] && fmt[prglen] !=
' ')
254 char *prg =
new char[prglen + 1];
257 char *found = find_program (prg);
259 MSG (
"found '%s' in path for '%s'", found, prg);
261 MSG (
"did not find '%s' in path", prg);
267 char *cmd =
new char[cmd_size];
268 snprintf (cmd, cmd_size, fmt, path);
274FILE *File::read_pipe (Internal *internal,
const char *fmt,
const int *sig,
276 if (!File::exists (path)) {
277 LOG (
"file '%s' does not exist", path);
280 LOG (
"file '%s' exists", path);
281 if (sig && !File::match (internal, path, sig))
283 LOG (
"file '%s' matches signature for '%s'", path, fmt);
284 MSG (
"opening pipe to read '%s'", path);
285 return open_pipe (internal, fmt, path,
"r");
290#if defined(__APPLE__) || defined(__MACH__)
291static std::mutex compressed_file_writing_mutex;
294FILE *File::write_pipe (Internal *internal,
const char *command,
295 const char *path,
int &child_pid) {
297 MSG (
"writing through command '%s' to '%s'", command, path);
301 std::vector<char *> args;
302 split_str (command, args);
305 char **argv = args.data ();
306 char *absolute_command_path = find_program (argv[0]);
307 int pipe_fds[2], out;
309#if defined(__APPLE__) || defined(__MACH__)
310 compressed_file_writing_mutex.lock ();
312 if (!absolute_command_path)
313 MSG (
"could not find '%s' in 'PATH' environment variable", argv[0]);
314 else if (::pipe (pipe_fds) < 0)
315 MSG (
"could not generate pipe to '%s' command", command);
316 else if ((out = ::open (path, O_CREAT | O_TRUNC | O_WRONLY, 0644)) < 0)
317 MSG (
"could not open '%s' for writing", path);
318 else if ((child_pid = ::fork ()) < 0) {
319 MSG (
"could not fork process to execute '%s' command", command);
321 }
else if (child_pid) {
322 ::close (pipe_fds[0]);
323 res = ::fdopen (pipe_fds[1],
"wb");
328 ::dup2 (pipe_fds[0], 0);
341 if (command[0] ==
'7') {
350#ifndef CADICAL_NCLOSEFROM
354 for (
int fd = 3; fd != FD_SETSIZE; fd++)
357 execv (absolute_command_path, argv);
360 if (absolute_command_path)
361 delete[] absolute_command_path;
362 delete_str_vector (args);
366#if defined(__APPLE__) || defined(__MACH__)
368 compressed_file_writing_mutex.unlock ();
378 return new File (internal,
false, 0, 0, f, n);
382 return new File (internal,
true, 0, 0, f, n);
389 file = read_pipe (internal,
"xz -c -d %s", xzsig, path);
393 file = read_pipe (internal,
"lzma -c -d %s", lzmasig, path);
397 file = read_pipe (internal,
"bzip2 -c -d %s", bz2sig, path);
401 file = read_pipe (internal,
"gzip -c -d %s", gzsig, path);
405 file = read_pipe (internal,
"7z x -so %s 2>/dev/null", sig7z, path);
410 file = read_file (internal, path);
417 return new File (internal,
false, close_input, 0, file, path);
422 int close_output = 3, child_pid = 0;
425 file = write_pipe (internal,
"xz -c", path, child_pid);
427 file = write_pipe (internal,
"bzip2 -c", path, child_pid);
429 file = write_pipe (internal,
"gzip -c", path, child_pid);
431 file = write_pipe (internal,
"7z a -an -txz -si -so", path, child_pid);
434 file = write_file (internal, path), close_output = 1;
439 return new File (internal,
true, close_output, child_pid, file, path);
445 if (internal->opts.quiet)
447 else if (internal->opts.verbose > 0)
450 if (close_file == 0) {
452 MSG (
"disconnecting from '%s'",
name ());
454 if (close_file == 1) {
456 MSG (
"closing file '%s'",
name ());
459 if (close_file == 2) {
461 MSG (
"closing input pipe to read '%s'",
name ());
465 if (close_file == 3) {
467 MSG (
"closing output pipe to write '%s'",
name ());
469 waitpid (child_pid, 0, 0);
470#if defined(__APPLE__) || defined(__MACH__)
471 compressed_file_writing_mutex.unlock ();
482 uint64_t written_bytes =
bytes ();
483 double written_mb = written_bytes / (double) (1 << 20);
484 MSG (
"after writing %" PRIu64
" bytes %.1f MB", written_bytes,
486 if (close_file == 3) {
487 size_t actual_bytes =
size (
name ());
489 double actual_mb = actual_bytes / (double) (1 << 20);
490 MSG (
"deflated to %zd bytes %.1f MB", actual_bytes, actual_mb);
491 MSG (
"factor %.2f (%.2f%% compression)",
492 relative (written_bytes, actual_bytes),
493 percent (actual_bytes, written_bytes));
495 MSG (
"but could not determine actual size of written file");
498 uint64_t read_bytes =
bytes ();
499 double read_mb = read_bytes / (double) (1 << 20);
500 MSG (
"after reading %" PRIu64
" bytes %.1f MB", read_bytes, read_mb);
501 if (close_file == 2) {
502 size_t actual_bytes =
size (
name ());
503 double actual_mb = actual_bytes / (double) (1 << 20);
504 MSG (
"inflated from %zd bytes %.1f MB", actual_bytes, actual_mb);
505 MSG (
"factor %.2f (%.2f%% compression)",
506 relative (read_bytes, actual_bytes),
507 percent (actual_bytes, read_bytes));
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
const char * name() const
static bool match(Internal *, const char *path, const int *sig)
static char * find_program(const char *prg)
void close(bool print=false)
static bool exists(const char *path)
static File * write(Internal *, FILE *, const char *name)
static File * read(Internal *, FILE *f, const char *name)
static size_t size(const char *path)
static bool writable(const char *path)
#define cadical_getc_unlocked
bool has_suffix(const char *str, const char *suffix)
double relative(double a, double b)
double percent(double a, double b)
else if(last->value< newval)