/* * The Npic library * * Copyright (C) 2003 Edouard Thiel * * This library is free software under the terms of the * GNU Lesser General Public License (LGPL) version 2.1. */ /* * files_gz.c - 15/03/2007 * * Read or write gzip compressed files */ #include /*--------------------- P U B L I C - I N T E R F A C E ----------------------*/ /*-------------------- P R I V A T E - F U N C T I O N S ---------------------*/ /* * Open a file for writing if no compression, else * Create a pipe, then fork. * - The child open a file for writing, redirect 1 to the file * and 0 to the pipe, then execlp with gzip -9 or other command. * - The parent catch SIGPIPE, then fdopen the pipe for writing. * After this call, one can write in fgz->f2 as if it was fopened. Verbose. */ void NpicWriteGZ_Open (Npic_file_gz *fgz, const char *filename, const char *funcname, int do_append, Npic_file_compress comp) { int fp[2], fd; if (fgz == NULL) { NpicError (__func__, NPIC_ERROR, ": NULL fgz"); return; } fgz->res = NPIC_SUCCESS; fgz->pid = 0; fgz->f2 = NULL; fgz->funcname = funcname; fgz->filename = filename; if (comp == NPIC_COMPRESS_NONE) { /* No compression */ fgz->f2 = fopen (filename, (do_append) ? "ab" : "wb"); if (fgz->f2 == NULL) fgz->res = NpicError (funcname, NPIC_ERR_OPEN, ": file \"%s\"", filename); return; } /* Open filename */ if (do_append) fd = open (filename, O_WRONLY | O_APPEND); else fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { fgz->res = NpicError (funcname, NPIC_ERR_OPEN, ": file \"%s\"", filename); return; } if (pipe (fp) < 0) { fgz->res = NpicError (funcname, NPIC_ERR_PIPE, ""); close (fd); return; } fgz->pid = fork (); if (fgz->pid < 0) { fgz->res = NpicError (funcname, NPIC_ERR_FORK, ""); close (fp[0]); close (fp[1]); close (fd); return; } if (fgz->pid == 0) { /* child process ; exit on error */ /* Redirect 1 to fd */ if (dup2 (fd, 1) < 0) exit (EXIT_FAILURE); close (fd); /* Redirect 0 to fp[0] */ if (dup2 (fp[0], 0) < 0) exit (EXIT_FAILURE); close (fp[0]); close (fp[1]); switch (comp) { case NPIC_COMPRESS_GZ : execlp ("gzip", "gzip", "-9", NULL); perror ("gzip"); break; case NPIC_COMPRESS_BZ2 : execlp ("bzip2", "bzip2", NULL); perror ("bzip2"); break; case NPIC_COMPRESS_7Z : execlp ("p7zip", "p7zip", NULL); perror ("p7zip"); break; default : fprintf (stderr, "compression type %d not handled\n", (int) comp); } exit (EXIT_FAILURE); } /* parent process continues here */ /* If child is dead, then write (fp[1], ..) will generate SIGPIPE. * Default action when receiving SIGPIPE is to terminate. Here we * ask to ignore SIGPIPE, so that the function can return an error. * We save the previous sighandler in old_sa. */ fgz->new_sa.sa_handler = SIG_IGN; sigemptyset (&fgz->new_sa.sa_mask); fgz->new_sa.sa_flags = SA_RESTART; sigaction (SIGPIPE, &fgz->new_sa, &fgz->old_sa); close (fp[0]); close (fd); /* Associate a stream to fp[1] ; fwrite is A LOT FASTER than write. */ fgz->f2 = fdopen (fp[1], "wb"); if (fgz->f2 == NULL) fgz->res = NPIC_ERR_FDOPEN; } /* * Close fgz->f2 ; if there was a child, waitpid for it. */ void NpicWriteGZ_Close (Npic_file_gz *fgz) { int status; if (fgz == NULL) { NpicError (__func__, NPIC_ERROR, ": NULL fgz"); return; } if (fgz->f2 != NULL) { fclose (fgz->f2); fgz->f2 = NULL; } if (fgz->pid <= 0) return; /* No compression, or fork failure */ waitpid (fgz->pid, &status, 0); if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0) fgz->res = NpicError (fgz->funcname, NPIC_ERR_CHILD, ": file \"%s\"", fgz->filename); /* Set back the old SIGPIPE handler */ sigaction (SIGPIPE, &fgz->old_sa, NULL); } /* * Open a file for reading if no compression, else * create a pipe, then fork. * - The child open a file for reading, redirect 0 to the file * and 1 to the pipe, then execlp with gunzip or other command. * - The parent fdopen the pipe for reading. * After this call, one can read in fgz->f2 as if it was fopened. Verbose. */ void NpicReadGZ_Open (Npic_file_gz *fgz, const char *filename, const char *funcname, int append_pos, Npic_file_compress comp) { int fp[2], fd; if (fgz == NULL) { NpicError (__func__, NPIC_ERROR, ": NULL fgz"); return; } fgz->res = NPIC_SUCCESS; fgz->pid = 0; fgz->f2 = NULL; fgz->funcname = funcname; fgz->filename = filename; if (comp == NPIC_COMPRESS_NONE) { /* No compression */ fgz->f2 = fopen (filename, "rb"); if (fgz->f2 == NULL) { fgz->res = NpicError (funcname, NPIC_ERR_OPEN, ": file \"%s\"", filename); return; } if (append_pos > 0 && fseek (fgz->f2, append_pos, SEEK_SET) < 0) { fgz->res = NpicError (funcname, NPIC_ERR_SETPOS, ": file \"%s\"", filename); fclose (fgz->f2); fgz->f2 = NULL; } return; } /* Open filename at beginning */ fd = open (filename, O_RDONLY); if (fd < 0) { fgz->res = NpicError (funcname, NPIC_ERR_OPEN, ": file \"%s\"", filename); return; } /* Position to append_pos, else stay at beginning */ if (append_pos > 0 && lseek (fd, append_pos, SEEK_SET) < 0) { fgz->res = NpicError (funcname, NPIC_ERR_SETPOS, ": file \"%s\"", filename); close (fd); return; } if (pipe (fp) < 0) { fgz->res = NpicError (funcname, NPIC_ERR_PIPE, ""); close (fd); return; } fgz->pid = fork (); if (fgz->pid < 0) { fgz->res = NpicError (funcname, NPIC_ERR_FORK, ""); close (fp[0]); close (fp[1]); close (fd); return; } if (fgz->pid == 0) { /* child process ; exit on error */ /* Redirect 0 to fd */ if (dup2 (fd, 0) < 0) exit (EXIT_FAILURE); close (fd); /* Redirect 1 to fp[1] */ if (dup2 (fp[1], 1) < 0) exit (EXIT_FAILURE); close (fp[1]); close (fp[0]); switch (comp) { case NPIC_COMPRESS_GZ : execlp ("gunzip", "gunzip", NULL); perror ("gunzip"); break; case NPIC_COMPRESS_BZ2 : execlp ("bunzip2", "bunzip2", NULL); perror ("bunzip2"); break; case NPIC_COMPRESS_7Z : execlp ("p7zip", "p7zip", "-d", NULL); perror ("p7zip"); break; default : fprintf (stderr, "compression type %d not handled\n", (int) comp); } exit (EXIT_FAILURE); } /* parent process continues here */ close (fp[1]); close (fd); /* Associate a stream to fp[0] ; fread is A LOT FASTER than read. */ fgz->f2 = fdopen (fp[0], "rb"); if (fgz->f2 == NULL) fgz->res = NPIC_ERR_FDOPEN; } /* * Close fgz->f2 ; if there was a child, waitpid for it. */ void NpicReadGZ_Close (Npic_file_gz *fgz) { int status; if (fgz == NULL) { NpicError (__func__, NPIC_ERROR, ": NULL fgz"); return; } if (fgz->f2 != NULL) { fclose (fgz->f2); fgz->f2 = NULL; } if (fgz->pid <= 0) return; /* No compression, or fork failure */ waitpid (fgz->pid, &status, 0); /* Read was interrupted before end of pipe: not an error */ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGPIPE) return; if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0) fgz->res = NpicError (fgz->funcname, NPIC_ERR_CHILD, ": file \"%s\"", fgz->filename); }