Blame urt/rle_open_f.c

Packit 78deda
/* 
Packit 78deda
 * rle_open_f.c - Open a file with defaults.
Packit 78deda
 * 
Packit 78deda
 * Author :     Jerry Winters 
Packit 78deda
 *      EECS Dept.
Packit 78deda
 *      University of Michigan
Packit 78deda
 * Date:    11/14/89
Packit 78deda
 * Copyright (c) 1990, University of Michigan
Packit 78deda
 */
Packit 78deda
Packit 78deda
#define _XOPEN_SOURCE  /* Make sure fdopen() is in stdio.h */
Packit 78deda
Packit 78deda
#include <string.h>
Packit 78deda
#include <stdio.h>
Packit 78deda
#include <unistd.h>
Packit 78deda
#include <fcntl.h>
Packit 78deda
Packit 78deda
#include "netpbm/pm_c_util.h"
Packit 78deda
#include "netpbm/nstring.h"
Packit 78deda
#include "rle_config.h"
Packit 78deda
#include "rle.h"
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
#define MAX_CHILDREN 100
Packit 78deda
    /* Maximum number of children we track; any more than this remain
Packit 78deda
       zombies.
Packit 78deda
    */
Packit 78deda
Packit 78deda
Packit 78deda
#ifndef NO_OPEN_PIPES
Packit 78deda
/* Need to have a SIGCHLD signal catcher. */
Packit 78deda
#include <signal.h>
Packit 78deda
#include <sys/wait.h>
Packit 78deda
#include <errno.h>
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static FILE *
Packit 78deda
my_popen(const char * const cmd, 
Packit 78deda
         const char * const mode, 
Packit 78deda
         int  *       const pid) {
Packit 78deda
Packit 78deda
    FILE *retfile;
Packit 78deda
    int thepid = 0;
Packit 78deda
    int pipefd[2];
Packit 78deda
    int i;
Packit 78deda
Packit 78deda
    /* Check args. */
Packit 78deda
    if ( *mode != 'r' && *mode != 'w' )
Packit 78deda
    {
Packit 78deda
        errno = EINVAL;
Packit 78deda
        return NULL;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (pm_pipe(pipefd) < 0 )
Packit 78deda
        return NULL;
Packit 78deda
    
Packit 78deda
    /* Flush known files. */
Packit 78deda
    fflush(stdout);
Packit 78deda
    fflush(stderr);
Packit 78deda
    if ( (thepid = fork()) < 0 )
Packit 78deda
    {
Packit 78deda
        close(pipefd[0]);
Packit 78deda
        close(pipefd[1]);
Packit 78deda
        return NULL;
Packit 78deda
    }
Packit 78deda
    else if (thepid == 0) {
Packit 78deda
        /* In child. */
Packit 78deda
        /* Rearrange file descriptors. */
Packit 78deda
        if ( *mode == 'r' )
Packit 78deda
        {
Packit 78deda
            /* Parent reads from pipe, so reset stdout. */
Packit 78deda
            close(1);
Packit 78deda
            dup2(pipefd[1],1);
Packit 78deda
        } else {
Packit 78deda
            /* Parent writing to pipe. */
Packit 78deda
            close(0);
Packit 78deda
            dup2(pipefd[0],0);
Packit 78deda
        }
Packit 78deda
        /* Close anything above fd 2. (64 is an arbitrary magic number). */
Packit 78deda
        for ( i = 3; i < 64; i++ )
Packit 78deda
            close(i);
Packit 78deda
Packit 78deda
        /* Finally, invoke the program. */
Packit 78deda
        if ( execl("/bin/sh", "sh", "-c", cmd, NULL) < 0 )
Packit 78deda
            exit(127);
Packit 78deda
        /* NOTREACHED */
Packit 78deda
    }   
Packit 78deda
Packit 78deda
    /* Close file descriptors, and gen up a FILE ptr */
Packit 78deda
    if ( *mode == 'r' )
Packit 78deda
    {
Packit 78deda
        /* Parent reads from pipe. */
Packit 78deda
        close(pipefd[1]);
Packit 78deda
        retfile = fdopen( pipefd[0], mode );
Packit 78deda
    } else {
Packit 78deda
        /* Parent writing to pipe. */
Packit 78deda
        close(pipefd[0]);
Packit 78deda
        retfile = fdopen( pipefd[1], mode );
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Return the PID. */
Packit 78deda
    *pid = thepid;
Packit 78deda
Packit 78deda
    return retfile;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
reapChildren(int *   const catchingChildrenP,
Packit 78deda
             pid_t * const pids) {
Packit 78deda
Packit 78deda
    /* Check for dead children. */
Packit 78deda
Packit 78deda
    if (*catchingChildrenP > 0) {
Packit 78deda
        unsigned int i;
Packit 78deda
Packit 78deda
        /* Check all children to see if any are dead, reap them if so. */
Packit 78deda
        for (i = 0; i < *catchingChildrenP; ++i) {
Packit 78deda
            /* The assumption here is that if it's dead, the kill
Packit 78deda
             * will fail, but, because we haven't waited for
Packit 78deda
             * it yet, it's a zombie.
Packit 78deda
             */
Packit 78deda
            if (kill(pids[i], 0) < 0) {
Packit 78deda
                int opid = pids[i], pid = 0;
Packit 78deda
                /* Wait for processes & delete them from the list,
Packit 78deda
                 * until we get the one we know is dead.
Packit 78deda
                 * When removing one earlier in the list than
Packit 78deda
                 * the one we found, decrement our loop index.
Packit 78deda
                 */
Packit 78deda
                while (pid != opid) {
Packit 78deda
                    unsigned int j;
Packit 78deda
                    pid = wait(NULL);
Packit 78deda
                    for (j = 0;
Packit 78deda
                         j < *catchingChildrenP && pids[j] != pid;
Packit 78deda
                         ++j)
Packit 78deda
                        ;
Packit 78deda
                    if (pid < 0)
Packit 78deda
                        break;
Packit 78deda
                    if (j < *catchingChildrenP) {
Packit 78deda
                        if (i >= j)
Packit 78deda
                            --i;
Packit 78deda
                        for (++j; j < *catchingChildrenP; ++j)
Packit 78deda
                            pids[j-1] = pids[j];
Packit 78deda
                        --*catchingChildrenP;
Packit 78deda
                    }
Packit 78deda
                }
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
#endif  /* !NO_OPEN_PIPES */
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
dealWithSubprocess(const char *  const file_name,
Packit 78deda
                   const char *  const mode,
Packit 78deda
                   int *         const catchingChildrenP,
Packit 78deda
                   pid_t *       const pids,
Packit 78deda
                   FILE **       const fpP,
Packit 78deda
                   bool *        const noSubprocessP,
Packit 78deda
                   const char ** const errorP) {
Packit 78deda
    *noSubprocessP = TRUE;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
/* 
Packit 78deda
 *  Purpose : Open a file for input or ouput as controlled by the mode
Packit 78deda
 *  parameter.  If no file name is specified (ie. file_name is null) then
Packit 78deda
 *  a pointer to stdin or stdout will be returned.  The calling routine may
Packit 78deda
 *  call this routine with a file name of "-".  For this case rle_open_f
Packit 78deda
 *  will return a pointer to stdin or stdout depending on the mode.
Packit 78deda
 *    If the user specifies a non-null file name and an I/O error occurs
Packit 78deda
 *  when trying to open the file, rle_open_f will terminate execution with
Packit 78deda
 *  an appropriate error message.
Packit 78deda
 *
Packit 78deda
 *  parameters
Packit 78deda
 *   input:
Packit 78deda
 *     prog_name:   name of the calling program.
Packit 78deda
 *     file_name :  name of the file to open
Packit 78deda
 *     mode :       either "r" for read or input file or "w" for write or
Packit 78deda
 *                  output file
Packit 78deda
 *
Packit 78deda
 *   output:
Packit 78deda
 *     a file pointer
Packit 78deda
 * 
Packit 78deda
 */
Packit 78deda
FILE *
Packit 78deda
rle_open_f_noexit(const char * const prog_name, 
Packit 78deda
                  const char * const file_name, 
Packit 78deda
                  const char * const mode ) {
Packit 78deda
Packit 78deda
    FILE * retval;
Packit 78deda
    FILE * fp;
Packit 78deda
    const char * err_str;
Packit 78deda
    int catching_children;
Packit 78deda
    pid_t pids[MAX_CHILDREN];
Packit 78deda
Packit 78deda
    catching_children = 0;
Packit 78deda
Packit 78deda
    if (*mode == 'w' || *mode == 'a')
Packit 78deda
        fp = stdout;     /* Set the default value */
Packit 78deda
    else
Packit 78deda
        fp = stdin;
Packit 78deda
    
Packit 78deda
    if (file_name != NULL && !streq(file_name, "-")) {
Packit 78deda
        bool noSubprocess;
Packit 78deda
        dealWithSubprocess(file_name, mode, &catching_children, pids,
Packit 78deda
                           &fp, &noSubprocess, &err_str);
Packit 78deda
        
Packit 78deda
        if (!err_str) {
Packit 78deda
            if (noSubprocess) {
Packit 78deda
                /* Ordinary, boring file case. */
Packit 78deda
                /* In the original code, the code to add the "b" was
Packit 78deda
                   conditionally included only if the macro
Packit 78deda
                   STDIO_NEEDS_BINARY was defined.  But for Netpbm,
Packit 78deda
                   there is no need make a distinction; we always add
Packit 78deda
                   the "b".  -BJH 2000.07.20.
Packit 78deda
                */
Packit 78deda
                char mode_string[32];   /* Should be enough. */
Packit 78deda
Packit 78deda
                /* Concatenate a 'b' onto the mode. */
Packit 78deda
                mode_string[0] = mode[0];
Packit 78deda
                mode_string[1] = 'b';
Packit 78deda
                strcpy( mode_string + 2, mode + 1 );
Packit 78deda
        
Packit 78deda
                fp = fopen(file_name, mode_string);
Packit 78deda
                if (fp == NULL )
Packit 78deda
                    err_str = "%s: can't open %s for %s: ";
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    } else
Packit 78deda
        err_str = NULL;
Packit 78deda
Packit 78deda
    if (err_str) {
Packit 78deda
        fprintf(stderr, err_str,
Packit 78deda
                prog_name, file_name,
Packit 78deda
                (*mode == 'w') ? "output" :
Packit 78deda
                (*mode == 'a') ? "append" :
Packit 78deda
                "input" );
Packit 78deda
        fprintf(stderr, "errno = %d (%s)\n", errno, strerror(errno));
Packit 78deda
        retval = NULL;
Packit 78deda
    } else
Packit 78deda
        retval = fp;
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
FILE *
Packit 78deda
rle_open_f(const char * prog_name, const char * file_name, const char * mode)
Packit 78deda
{
Packit 78deda
    FILE *fp;
Packit 78deda
Packit 78deda
    if ( (fp = rle_open_f_noexit( prog_name, file_name, mode )) == NULL )
Packit 78deda
        exit( -1 );
Packit 78deda
Packit 78deda
    return fp;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
/*****************************************************************
Packit 78deda
 * TAG( rle_close_f )
Packit 78deda
 * 
Packit 78deda
 * Close a file opened by rle_open_f.  If the file is stdin or stdout,
Packit 78deda
 * it will not be closed.
Packit 78deda
 * Inputs:
Packit 78deda
 *  fd: File to close.
Packit 78deda
 * Outputs:
Packit 78deda
 *  None.
Packit 78deda
 * Assumptions:
Packit 78deda
 *  fd is open.
Packit 78deda
 * Algorithm:
Packit 78deda
 *  If fd is NULL, just return.
Packit 78deda
 *  If fd is stdin or stdout, don't close it.  Otherwise, call fclose.
Packit 78deda
 */
Packit 78deda
void
Packit 78deda
rle_close_f( fd )
Packit 78deda
    FILE *fd;
Packit 78deda
{
Packit 78deda
    if ( fd == NULL || fd == stdin || fd == stdout )
Packit 78deda
        return;
Packit 78deda
    else
Packit 78deda
        fclose( fd );
Packit 78deda
}