Blame lib/libpm.c

Packit 78deda
/**************************************************************************
Packit 78deda
                                  libpm.c
Packit 78deda
***************************************************************************
Packit 78deda
  This file contains fundamental libnetpbm services.
Packit 78deda
Packit 78deda
  Some of the subroutines in this library are intended and documented
Packit 78deda
  for use by Netpbm users, but most of them are just used by other
Packit 78deda
  Netpbm library subroutines.
Packit 78deda
**************************************************************************/
Packit 78deda
Packit 78deda
#define _DEFAULT_SOURCE      /* New name for SVID & BSD source defines */
Packit 78deda
#define _BSD_SOURCE          /* Make sure strdup is in string.h */
Packit 78deda
#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
Packit 78deda
Packit 78deda
#include "netpbm/pm_config.h"
Packit 78deda
Packit 78deda
#include <assert.h>
Packit 78deda
#include <unistd.h>
Packit 78deda
#include <stdio.h>
Packit 78deda
#include <stdarg.h>
Packit 78deda
#include <string.h>
Packit 78deda
#include <errno.h>
Packit 78deda
#include <setjmp.h>
Packit 78deda
#include <time.h>
Packit 78deda
#include <limits.h>
Packit 78deda
#if HAVE_FORK
Packit 78deda
#include <sys/wait.h>
Packit 78deda
#endif
Packit 78deda
#include <sys/types.h>
Packit 78deda
Packit 78deda
#include "netpbm/pm_c_util.h"
Packit 78deda
#include "netpbm/mallocvar.h"
Packit 78deda
#include "netpbm/version.h"
Packit 78deda
#include "netpbm/nstring.h"
Packit 78deda
#include "netpbm/shhopt.h"
Packit 78deda
#include "compile.h"
Packit 78deda
Packit 78deda
#include "pm.h"
Packit 78deda
Packit 78deda
/* The following are set by pm_init(), then used by subsequent calls to other
Packit 78deda
   pm_xxx() functions.
Packit 78deda
   */
Packit 78deda
const char * pm_progname;
Packit 78deda
Packit 78deda
int pm_plain_output;
Packit 78deda
    /* Boolean: programs should produce output in plain format */
Packit 78deda
Packit 78deda
static bool pm_showmessages;  
Packit 78deda
    /* Programs should display informational messages (because the user didn't
Packit 78deda
       specify the --quiet option).
Packit 78deda
    */
Packit 78deda
static jmp_buf * pm_jmpbufP = NULL;
Packit 78deda
    /* A description of the point to which the program should hyperjump
Packit 78deda
       if a libnetpbm function encounters an error (libnetpbm functions
Packit 78deda
       don't normally return in that case).
Packit 78deda
Packit 78deda
       User sets this to something in his own extra-library context.
Packit 78deda
       Libnetpbm routines that have something that needs to be cleaned up
Packit 78deda
       preempt it.
Packit 78deda
Packit 78deda
       NULL, which is the default value, means when a libnetpbm function
Packit 78deda
       encounters an error, it causes the process to exit.
Packit 78deda
    */
Packit 78deda
static pm_usererrormsgfn * userErrorMsgFn = NULL;
Packit 78deda
    /* A function to call to issue an error message.
Packit 78deda
Packit 78deda
       NULL means use the library default: print to Standard Error
Packit 78deda
    */
Packit 78deda
Packit 78deda
static pm_usermessagefn * userMessageFn = NULL;
Packit 78deda
    /* A function to call to issue a non-error message.
Packit 78deda
Packit 78deda
       NULL means use the library default: print to Standard Error
Packit 78deda
    */
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_setjmpbuf(jmp_buf * const jmpbufP) {
Packit 78deda
    pm_jmpbufP = jmpbufP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_setjmpbufsave(jmp_buf *  const jmpbufP,
Packit 78deda
                 jmp_buf ** const oldJmpbufPP) {
Packit 78deda
Packit 78deda
    *oldJmpbufPP = pm_jmpbufP;
Packit 78deda
    pm_jmpbufP = jmpbufP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_longjmp(void) {
Packit 78deda
Packit 78deda
    if (pm_jmpbufP)
Packit 78deda
        longjmp(*pm_jmpbufP, 1);
Packit 78deda
    else
Packit 78deda
        exit(1);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_fork(int *         const iAmParentP,
Packit 78deda
        pid_t *       const childPidP,
Packit 78deda
        const char ** const errorP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Same as POSIX fork, except with a nicer interface and works
Packit 78deda
   (fails cleanly) on systems that don't have POSIX fork().
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
#if HAVE_FORK
Packit 78deda
    int rc;
Packit 78deda
Packit 78deda
    rc = fork();
Packit 78deda
Packit 78deda
    if (rc < 0) {
Packit 78deda
        pm_asprintf(errorP, "Failed to fork a process.  errno=%d (%s)",
Packit 78deda
                    errno, strerror(errno));
Packit 78deda
    } else {
Packit 78deda
        *errorP = NULL;
Packit 78deda
Packit 78deda
        if (rc == 0) {
Packit 78deda
            *iAmParentP = FALSE;
Packit 78deda
        } else {
Packit 78deda
            *iAmParentP = TRUE;
Packit 78deda
            *childPidP = rc;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
#else
Packit 78deda
    pm_asprintf(errorP, "Cannot fork a process, because this system does "
Packit 78deda
                "not have POSIX fork()");
Packit 78deda
#endif
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_waitpid(pid_t         const pid,
Packit 78deda
           int *         const statusP,
Packit 78deda
           int           const options,
Packit 78deda
           pid_t *       const exitedPidP,
Packit 78deda
           const char ** const errorP) {
Packit 78deda
Packit 78deda
#if HAVE_FORK
Packit 78deda
    pid_t rc;
Packit 78deda
    rc = waitpid(pid, statusP, options);
Packit 78deda
    if (rc == (pid_t)-1) {
Packit 78deda
        pm_asprintf(errorP, "Failed to wait for process exit.  "
Packit 78deda
                    "waitpid() errno = %d (%s)",
Packit 78deda
                    errno, strerror(errno));
Packit 78deda
    } else {
Packit 78deda
        *exitedPidP = rc;
Packit 78deda
        *errorP = NULL;
Packit 78deda
    }
Packit 78deda
#else
Packit 78deda
    pm_error("INTERNAL ERROR: Attempt to wait for a process we created on "
Packit 78deda
             "a system on which we can't create processes");
Packit 78deda
#endif
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_waitpidSimple(pid_t const pid) {
Packit 78deda
Packit 78deda
    int status;
Packit 78deda
    pid_t exitedPid;
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    pm_waitpid(pid, &status, 0, &exitedPid, &error);
Packit 78deda
Packit 78deda
    if (error) {
Packit 78deda
        pm_errormsg("%s", error);
Packit 78deda
        pm_strfree(error);
Packit 78deda
        pm_longjmp();
Packit 78deda
    } else {
Packit 78deda
        assert(exitedPid != 0);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_setusererrormsgfn(pm_usererrormsgfn * fn) {
Packit 78deda
Packit 78deda
    userErrorMsgFn = fn;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_setusermessagefn(pm_usermessagefn * fn) {
Packit 78deda
Packit 78deda
    userMessageFn = fn;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_usage(const char usage[]) {
Packit 78deda
    pm_error("usage:  %s %s", pm_progname, usage);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void PM_GNU_PRINTF_ATTR(1,2)
Packit 78deda
pm_message(const char format[], ...) {
Packit 78deda
Packit 78deda
    va_list args;
Packit 78deda
Packit 78deda
    va_start(args, format);
Packit 78deda
Packit 78deda
    if (pm_showmessages) {
Packit 78deda
        const char * msg;
Packit 78deda
        pm_vasprintf(&msg, format, args);
Packit 78deda
Packit 78deda
        if (userMessageFn)
Packit 78deda
            userMessageFn(msg);
Packit 78deda
        else
Packit 78deda
            fprintf(stderr, "%s: %s\n", pm_progname, msg);
Packit 78deda
Packit 78deda
        pm_strfree(msg);
Packit 78deda
    }
Packit 78deda
    va_end(args);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
errormsg(const char * const msg) {
Packit 78deda
Packit 78deda
    if (userErrorMsgFn)
Packit 78deda
        userErrorMsgFn(msg);
Packit 78deda
    else
Packit 78deda
        fprintf(stderr, "%s: %s\n", pm_progname, msg);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void PM_GNU_PRINTF_ATTR(1,2)
Packit 78deda
pm_errormsg(const char format[], ...) {
Packit 78deda
Packit 78deda
    va_list args;
Packit 78deda
    const char * msg;
Packit 78deda
Packit 78deda
    va_start(args, format);
Packit 78deda
Packit 78deda
    pm_vasprintf(&msg, format, args);
Packit 78deda
    
Packit 78deda
    errormsg(msg);
Packit 78deda
Packit 78deda
    pm_strfree(msg);
Packit 78deda
Packit 78deda
    va_end(args);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void PM_GNU_PRINTF_ATTR(1,2)
Packit 78deda
pm_error(const char format[], ...) {
Packit 78deda
    va_list args;
Packit 78deda
    const char * msg;
Packit 78deda
Packit 78deda
    va_start(args, format);
Packit 78deda
Packit 78deda
    pm_vasprintf(&msg, format, args);
Packit 78deda
    
Packit 78deda
    errormsg(msg);
Packit 78deda
Packit 78deda
    pm_strfree(msg);
Packit 78deda
Packit 78deda
    va_end(args);
Packit 78deda
Packit 78deda
    pm_longjmp();
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
pm_have_float_format(void) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Return 1 if %f, %e, and %g work in format strings for pm_message, etc.;
Packit 78deda
  0 otherwise.
Packit 78deda
Packit 78deda
  Where they don't "work," that means the specifier just appears itself in
Packit 78deda
  the formatted strings, e.g. "the number is g".
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    return pm_vasprintf_knows_float();
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void *
Packit 78deda
mallocz(size_t const size) {
Packit 78deda
Packit 78deda
    return malloc(MAX(1, size));
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void *
Packit 78deda
pm_allocrow(unsigned int const cols,
Packit 78deda
            unsigned int const size) {
Packit 78deda
Packit 78deda
    unsigned char * itrow;
Packit 78deda
Packit 78deda
    if (cols != 0 && UINT_MAX / cols < size)
Packit 78deda
        pm_error("Arithmetic overflow multiplying %u by %u to get the "
Packit 78deda
                 "size of a row to allocate.", cols, size);
Packit 78deda
Packit 78deda
    itrow = mallocz(cols * size);
Packit 78deda
    if (itrow == NULL)
Packit 78deda
        pm_error("out of memory allocating a row");
Packit 78deda
Packit 78deda
    return itrow;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_freerow(void * const itrow) {
Packit 78deda
    free(itrow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
char **
Packit 78deda
pm_allocarray(int const cols,
Packit 78deda
              int const rows,
Packit 78deda
              int const elementSize ) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   This is for backward compatibility.  MALLOCARRAY2 is usually better.
Packit 78deda
Packit 78deda
   A problem with pm_allocarray() is that its return type is char **
Packit 78deda
   even though 'elementSize' can be other than 1.  So users have
Packit 78deda
   traditionally type cast the result.  In the old days, that was just
Packit 78deda
   messy; modern compilers can produce the wrong code if you do that.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    char ** retval;
Packit 78deda
    void * result;
Packit 78deda
Packit 78deda
    pm_mallocarray2(&result, rows, cols, elementSize);
Packit 78deda
Packit 78deda
    if (result == NULL)
Packit 78deda
        pm_error("Failed to allocate a raster array of %u columns x %u rows",
Packit 78deda
                 cols, rows);
Packit 78deda
Packit 78deda
    retval = result;
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_freearray(char ** const rowIndex, 
Packit 78deda
             int     const rows) {
Packit 78deda
Packit 78deda
    void * const rowIndexVoid = rowIndex;
Packit 78deda
Packit 78deda
    pm_freearray2(rowIndexVoid);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
/* Case-insensitive keyword matcher. */
Packit 78deda
Packit 78deda
int
Packit 78deda
pm_keymatch(const char *       const strarg, 
Packit 78deda
            const char * const keywordarg, 
Packit 78deda
            int          const minchars) {
Packit 78deda
    int len;
Packit 78deda
    const char * keyword;
Packit 78deda
    const char * str;
Packit 78deda
Packit 78deda
    str = strarg;
Packit 78deda
    keyword = keywordarg;
Packit 78deda
Packit 78deda
    len = strlen( str );
Packit 78deda
    if ( len < minchars )
Packit 78deda
        return 0;
Packit 78deda
    while ( --len >= 0 )
Packit 78deda
        {
Packit 78deda
        register char c1, c2;
Packit 78deda
Packit 78deda
        c1 = *str++;
Packit 78deda
        c2 = *keyword++;
Packit 78deda
        if ( c2 == '\0' )
Packit 78deda
            return 0;
Packit 78deda
        if ( ISUPPER( c1 ) )
Packit 78deda
            c1 = tolower( c1 );
Packit 78deda
        if ( ISUPPER( c2 ) )
Packit 78deda
            c2 = tolower( c2 );
Packit 78deda
        if ( c1 != c2 )
Packit 78deda
            return 0;
Packit 78deda
        }
Packit 78deda
    return 1;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
/* Log base two hacks. */
Packit 78deda
Packit 78deda
int
Packit 78deda
pm_maxvaltobits(int const maxval) {
Packit 78deda
    if ( maxval <= 1 )
Packit 78deda
        return 1;
Packit 78deda
    else if ( maxval <= 3 )
Packit 78deda
        return 2;
Packit 78deda
    else if ( maxval <= 7 )
Packit 78deda
        return 3;
Packit 78deda
    else if ( maxval <= 15 )
Packit 78deda
        return 4;
Packit 78deda
    else if ( maxval <= 31 )
Packit 78deda
        return 5;
Packit 78deda
    else if ( maxval <= 63 )
Packit 78deda
        return 6;
Packit 78deda
    else if ( maxval <= 127 )
Packit 78deda
        return 7;
Packit 78deda
    else if ( maxval <= 255 )
Packit 78deda
        return 8;
Packit 78deda
    else if ( maxval <= 511 )
Packit 78deda
        return 9;
Packit 78deda
    else if ( maxval <= 1023 )
Packit 78deda
        return 10;
Packit 78deda
    else if ( maxval <= 2047 )
Packit 78deda
        return 11;
Packit 78deda
    else if ( maxval <= 4095 )
Packit 78deda
        return 12;
Packit 78deda
    else if ( maxval <= 8191 )
Packit 78deda
        return 13;
Packit 78deda
    else if ( maxval <= 16383 )
Packit 78deda
        return 14;
Packit 78deda
    else if ( maxval <= 32767 )
Packit 78deda
        return 15;
Packit 78deda
    else if ( (long) maxval <= 65535L )
Packit 78deda
        return 16;
Packit Service 2370ca
    else
Packit 78deda
        pm_error( "maxval of %d is too large!", maxval );
Packit 78deda
        return -1;  /* Should never come here */
Packit 78deda
}
Packit 78deda
Packit 78deda
int
Packit 78deda
pm_bitstomaxval(int const bits) {
Packit 78deda
    return ( 1 << bits ) - 1;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
unsigned int PURE_FN_ATTR
Packit 78deda
pm_lcm(unsigned int const x, 
Packit 78deda
       unsigned int const y,
Packit 78deda
       unsigned int const z,
Packit 78deda
       unsigned int const limit) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Compute the least common multiple of 'x', 'y', and 'z'.  If it's bigger than
Packit 78deda
  'limit', though, just return 'limit'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int biggest;
Packit 78deda
    unsigned int candidate;
Packit 78deda
Packit 78deda
    if (x == 0 || y == 0 || z == 0)
Packit 78deda
        pm_error("pm_lcm(): Least common multiple of zero taken.");
Packit 78deda
Packit 78deda
    biggest = MAX(x, MAX(y,z));
Packit 78deda
Packit 78deda
    candidate = biggest;
Packit 78deda
    while (((candidate % x) != 0 ||       /* not a multiple of x */
Packit 78deda
            (candidate % y) != 0 ||       /* not a multiple of y */
Packit 78deda
            (candidate % z) != 0 ) &&     /* not a multiple of z */
Packit 78deda
           candidate <= limit)
Packit 78deda
        candidate += biggest;
Packit 78deda
Packit 78deda
    if (candidate > limit) 
Packit 78deda
        candidate = limit;
Packit 78deda
Packit 78deda
    return candidate;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_init(const char * const progname,
Packit 78deda
        unsigned int const flags) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Initialize static variables that Netpbm library routines use.
Packit 78deda
Packit 78deda
   Any user of Netpbm library routines is expected to call this at the
Packit 78deda
   beginning of this program, before any other Netpbm library routines.
Packit 78deda
Packit 78deda
   A program may call this via pm_proginit() instead, though.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    pm_setMessage(FALSE, NULL);
Packit 78deda
Packit 78deda
    pm_progname = progname;
Packit 78deda
Packit 78deda
#ifdef O_BINARY
Packit 78deda
#ifdef HAVE_SETMODE
Packit 78deda
    /* Set the stdin and stdout mode to binary.  This means nothing on Unix,
Packit 78deda
       but matters on Windows.
Packit 78deda
       
Packit 78deda
       Note that stdin and stdout aren't necessarily image files.  In
Packit 78deda
       particular, stdout is sometimes text for human consumption,
Packit 78deda
       typically printed on the terminal.  Binary mode isn't really
Packit 78deda
       appropriate for that case.  We do this setting here without
Packit 78deda
       any knowledge of how stdin and stdout are being used because it is
Packit 78deda
       easy.  But we do make an exception for the case that we know the
Packit 78deda
       file is a terminal, to get a little closer to doing the right
Packit 78deda
       thing.  
Packit 78deda
    */
Packit 78deda
    if (!isatty(0)) setmode(0,O_BINARY);  /* Standard Input */
Packit 78deda
    if (!isatty(1)) setmode(1,O_BINARY);  /* Standard Output */
Packit 78deda
#endif /* HAVE_SETMODE */
Packit 78deda
#endif /* O_BINARY */
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static const char *
Packit 78deda
dtMsg(time_t const dateTime) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Text for the version message to indicate datetime 'dateTime'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    struct tm * const brokenTimeP = localtime(&dateTime);
Packit 78deda
Packit 78deda
    char buffer[100];
Packit 78deda
    
Packit 78deda
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", brokenTimeP);
Packit 78deda
Packit 78deda
    return pm_strdup(buffer);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
showVersion(void) {
Packit 78deda
Packit 78deda
    pm_message("Using libnetpbm from Netpbm Version: %s", NETPBM_VERSION);
Packit 78deda
Packit 78deda
    /* SOURCE_DATETIME is defined when the user wants a reproducible build,
Packit 78deda
       so wants the source code modification datetime instead of the build
Packit 78deda
       datetime in the object code.
Packit 78deda
    */
Packit 78deda
#if defined(SOURCE_DATETIME)
Packit 78deda
    {
Packit 78deda
        const char * const sourceDtMsg = dtMsg(SOURCE_DATETIME);
Packit 78deda
        pm_message("Built from source dated %s", sourceDtMsg);
Packit 78deda
        pm_strfree(sourceDtMsg);
Packit 78deda
    }
Packit 78deda
#else
Packit 78deda
  #if defined(BUILD_DATETIME)
Packit 78deda
    {
Packit 78deda
        const char * const buildDtMsg = dtMsg(BUILD_DATETIME);
Packit 78deda
        pm_message("Built at %s", buildDtMsg);
Packit 78deda
        pm_strfree(buildDtMsg);
Packit 78deda
    }
Packit 78deda
  #endif
Packit 78deda
#endif
Packit 78deda
Packit 78deda
#if defined(COMPILED_BY)
Packit 78deda
    pm_message("Built by %s", COMPILED_BY);
Packit 78deda
#endif
Packit 78deda
Packit 78deda
#ifdef BSD
Packit 78deda
    pm_message( "BSD defined" );
Packit 78deda
#endif /*BSD*/
Packit 78deda
#ifdef SYSV
Packit 78deda
    pm_message( "SYSV defined" );
Packit 78deda
#endif /*SYSV*/
Packit 78deda
#ifdef MSDOS
Packit 78deda
    pm_message( "MSDOS defined" );
Packit 78deda
#endif /*MSDOS*/
Packit 78deda
#ifdef AMIGA
Packit 78deda
    pm_message( "AMIGA defined" );
Packit 78deda
#endif /* AMIGA */
Packit 78deda
    {
Packit 78deda
        const char * rgbdef;
Packit 78deda
        pm_message( "RGB_ENV='%s'", RGBENV );
Packit 78deda
        rgbdef = getenv(RGBENV);
Packit 78deda
        if( rgbdef )
Packit 78deda
            pm_message( "RGBENV= '%s' (env vbl set to '%s')", 
Packit 78deda
                        RGBENV, rgbdef );
Packit 78deda
        else
Packit 78deda
            pm_message( "RGBENV= '%s' (env vbl is unset)", RGBENV);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
showNetpbmHelp(const char progname[]) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Tell the user where to get help for this program, assuming it is a Netpbm
Packit 78deda
  program (a program that comes with the Netpbm package, as opposed to a 
Packit 78deda
  program that just uses the Netpbm libraries).
Packit 78deda
Packit 78deda
  Tell him to go to the URL listed in the Netpbm configuration file.
Packit 78deda
  The Netpbm configuration file is the file named by the NETPBM_CONF
Packit 78deda
  environment variable, or /etc/netpbm if there is no such environment
Packit 78deda
  variable.
Packit 78deda
Packit 78deda
  If the configuration file doesn't exist or can't be read, or doesn't
Packit 78deda
  contain a DOCURL value, tell him to go to a hardcoded source for
Packit 78deda
  documentation.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    const char * netpbmConfigFileName;
Packit 78deda
    FILE * netpbmConfigFile;
Packit 78deda
    char * docurl;
Packit 78deda
Packit 78deda
    if (getenv("NETPBM_CONF"))
Packit 78deda
        netpbmConfigFileName = getenv("NETPBM_CONF");
Packit 78deda
    else 
Packit 78deda
        netpbmConfigFileName = "/etc/netpbm";
Packit 78deda
    
Packit 78deda
    netpbmConfigFile = fopen(netpbmConfigFileName, "r");
Packit 78deda
    if (netpbmConfigFile == NULL) {
Packit 78deda
        pm_message("Unable to open Netpbm configuration file '%s'.  "
Packit 78deda
                   "Errno = %d (%s).  "
Packit 78deda
                   "Use the NETPBM_CONF environment variable "
Packit 78deda
                   "to control the identity of the Netpbm configuration file.",
Packit 78deda
                   netpbmConfigFileName,errno, strerror(errno));
Packit 78deda
        docurl = NULL;
Packit 78deda
    } else {
Packit 78deda
        docurl = NULL;  /* default */
Packit 78deda
        while (!feof(netpbmConfigFile) && !ferror(netpbmConfigFile)) {
Packit 78deda
            char line[80+1];
Packit 78deda
            fgets(line, sizeof(line), netpbmConfigFile);
Packit 78deda
            if (line[0] != '#') {
Packit 78deda
                sscanf(line, "docurl=%s", docurl);
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        if (docurl == NULL)
Packit 78deda
            pm_message("No 'docurl=' line in Netpbm configuration file '%s'.",
Packit 78deda
                       netpbmConfigFileName);
Packit 78deda
Packit 78deda
        fclose(netpbmConfigFile);
Packit 78deda
    }
Packit 78deda
    if (docurl == NULL)
Packit 78deda
        pm_message("We have no reliable indication of where the Netpbm "
Packit 78deda
                   "documentation is, but try "
Packit 78deda
                   "http://netpbm.sourceforge.net or email "
Packit 78deda
                   "Bryan Henderson (bryanh@giraffe-data.com) for help.");
Packit 78deda
    else
Packit 78deda
        pm_message("This program is part of the Netpbm package.  Find "
Packit 78deda
                   "documentation for it at %s/%s\n", docurl, progname);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommonOptions(int *         const argcP,
Packit 78deda
                   const char ** const argv,
Packit 78deda
                   bool *        const showMessagesP,
Packit 78deda
                   bool *        const showVersionP,
Packit 78deda
                   bool *        const showHelpP,
Packit 78deda
                   bool *        const plainOutputP) {
Packit 78deda
Packit 78deda
    unsigned int inCursor;
Packit 78deda
    unsigned int outCursor;
Packit 78deda
Packit 78deda
    *showMessagesP = true;   /* initial assumption */
Packit 78deda
    *showVersionP  = false;  /* initial assumption */
Packit 78deda
    *showHelpP     = false;  /* initial assumption */
Packit 78deda
    *plainOutputP  = false;  /* initial assumption */
Packit 78deda
Packit 78deda
    for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) {
Packit 78deda
            if (strcaseeq(argv[inCursor], "-quiet") ||
Packit 78deda
                strcaseeq(argv[inCursor], "--quiet")) 
Packit 78deda
                *showMessagesP = false;
Packit 78deda
            else if (strcaseeq(argv[inCursor], "-version") ||
Packit 78deda
                     strcaseeq(argv[inCursor], "--version")) 
Packit 78deda
                *showVersionP = true;
Packit 78deda
            else if (strcaseeq(argv[inCursor], "-help") ||
Packit 78deda
                     strcaseeq(argv[inCursor], "--help") ||
Packit 78deda
                     strcaseeq(argv[inCursor], "-?")) 
Packit 78deda
                *showHelpP = true;
Packit 78deda
            else if (strcaseeq(argv[inCursor], "-plain") ||
Packit 78deda
                     strcaseeq(argv[inCursor], "--plain"))
Packit 78deda
                *plainOutputP = true;
Packit 78deda
            else
Packit 78deda
                argv[outCursor++] = argv[inCursor];
Packit 78deda
        }
Packit 78deda
    *argcP = outCursor;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_proginit(int *         const argcP,
Packit 78deda
            const char ** const argv) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Do various initialization things that all programs in the Netpbm package,
Packit 78deda
   and programs that emulate such programs, should do.
Packit 78deda
Packit 78deda
   This includes processing global options.  We scan argv[], which has *argcP
Packit 78deda
   elements, for common options and execute the functions for the ones we
Packit 78deda
   find.  We remove the common options from argv[], updating *argcP
Packit 78deda
   accordingly.
Packit 78deda
Packit 78deda
   This includes calling pm_init() to initialize the Netpbm libraries.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    const char * const progname = pm_arg0toprogname(argv[0]);
Packit 78deda
        /* points to static memory in this library */
Packit 78deda
    bool showMessages;
Packit 78deda
    bool plain;
Packit 78deda
    bool justShowVersion;
Packit 78deda
        /* We're supposed to just show the version information, then exit the
Packit 78deda
           program.
Packit 78deda
        */
Packit 78deda
    bool justShowHelp;
Packit 78deda
        /* We're supposed to just tell user where to get help, then exit the
Packit 78deda
           program.
Packit 78deda
        */
Packit 78deda
Packit 78deda
    pm_init(progname, 0);
Packit 78deda
Packit 78deda
    parseCommonOptions(argcP, argv,
Packit 78deda
                       &showMessages, &justShowVersion, &justShowHelp,
Packit 78deda
                       &plain);
Packit 78deda
Packit 78deda
    pm_plain_output = plain ? 1 : 0;
Packit 78deda
Packit 78deda
    pm_setMessage(showMessages ? 1 : 0, NULL);
Packit 78deda
Packit 78deda
    if (justShowVersion) {
Packit 78deda
        showVersion();
Packit 78deda
        exit(0);
Packit 78deda
    } else if (justShowHelp) {
Packit 78deda
        pm_error("Use 'man %s' for help.", progname);
Packit 78deda
        /* If we can figure out a way to distinguish Netpbm programs from 
Packit 78deda
           other programs using the Netpbm libraries, we can do better here.
Packit 78deda
        */
Packit 78deda
        if (0)
Packit 78deda
            showNetpbmHelp(progname);
Packit 78deda
        exit(0);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pm_setMessage(int   const newState,
Packit 78deda
              int * const oldStateP) {
Packit 78deda
    
Packit 78deda
    if (oldStateP)
Packit 78deda
        *oldStateP = pm_showmessages;
Packit 78deda
Packit 78deda
    pm_showmessages = !!newState;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
pm_getMessage(void) {
Packit 78deda
Packit 78deda
    return pm_showmessages;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
extractAfterLastSlash(const char * const fullPath,
Packit 78deda
                      char *       const retval,
Packit 78deda
                      size_t       const retvalSize) {
Packit 78deda
    
Packit 78deda
    char * slashPos;
Packit 78deda
Packit 78deda
    /* Chop any directories off the left end */
Packit 78deda
    slashPos = strrchr(fullPath, '/');
Packit 78deda
Packit 78deda
    if (slashPos == NULL) {
Packit 78deda
        strncpy(retval, fullPath, retvalSize);
Packit 78deda
        retval[retvalSize-1] = '\0';
Packit 78deda
    } else {
Packit 78deda
        strncpy(retval, slashPos +1, retvalSize);
Packit 78deda
        retval[retvalSize-1] = '\0';
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
chopOffExe(char * const arg) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Chop any .exe off the right end of 'arg'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if (strlen(arg) >= 4 && strcmp(arg+strlen(arg)-4, ".exe") == 0)
Packit 78deda
        arg[strlen(arg)-4] = 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
char *
Packit 78deda
pm_arg0toprogname(const char arg0[]) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Given a value for argv[0] (a command name or file name passed to a 
Packit 78deda
   program in the standard C calling sequence), return the name of the
Packit 78deda
   Netpbm program to which it refers.
Packit 78deda
Packit 78deda
   In the most ordinary case, this is simply the argument itself.
Packit 78deda
Packit 78deda
   But if the argument contains a slash, it is the part of the argument 
Packit 78deda
   after the last slash, and if there is a .exe on it (as there is for
Packit 78deda
   DJGPP), that is removed.
Packit 78deda
Packit 78deda
   The return value is in static storage within.  It is NUL-terminated,
Packit 78deda
   but truncated at 64 characters.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
#define MAX_RETVAL_SIZE 64
Packit 78deda
#if MSVCRT
Packit 78deda
    /* Note that there exists _splitpath_s, which takes a size argument,
Packit 78deda
       but it is only in "secure" extensions of MSVCRT that come only with
Packit 78deda
       MSVC; but _splitpath() comes with Windows.  MinGW has a header file
Packit 78deda
       for it.
Packit 78deda
    */
Packit 78deda
    static char retval[_MAX_FNAME];
Packit 78deda
    _splitpath(arg0, 0, 0,  retval, 0);
Packit 78deda
    if (MAX_RETVAL_SIZE < _MAX_FNAME)
Packit 78deda
        retval[MAX_RETVAL_SIZE] = '\0';
Packit 78deda
#else
Packit 78deda
    static char retval[MAX_RETVAL_SIZE+1];
Packit 78deda
    extractAfterLastSlash(arg0, retval, sizeof(retval));
Packit 78deda
    chopOffExe(retval);
Packit 78deda
#endif
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
unsigned int
Packit 78deda
pm_randseed(void) {
Packit 78deda
Packit 78deda
    return time(NULL) ^ getpid();
Packit 78deda
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
unsigned int
Packit 78deda
pm_parse_width(const char * const arg) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Return the image width represented by the decimal ASCIIZ string
Packit 78deda
   'arg'.  Fail if it doesn't validly represent a width or represents
Packit 78deda
   a width that can't be conveniently used in computation.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int width;
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    pm_interpret_uint(arg, &width, &error);
Packit 78deda
Packit 78deda
    if (error) {
Packit 78deda
        pm_error("'%s' is invalid as an image width.  %s", arg, error);
Packit 78deda
        pm_strfree(error);
Packit 78deda
    } else {
Packit 78deda
        if (width > INT_MAX-10)
Packit 78deda
            pm_error("Width %u is too large for computations.", width);
Packit 78deda
        if (width == 0)
Packit 78deda
            pm_error("Width argument must be a positive number.  You "
Packit 78deda
                     "specified 0.");
Packit 78deda
    }
Packit 78deda
    return width;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
unsigned int
Packit 78deda
pm_parse_height(const char * const arg) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Same as pm_parse_width(), but for height.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int height;
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    pm_interpret_uint(arg, &height, &error);
Packit 78deda
Packit 78deda
    if (error) {
Packit 78deda
        pm_error("'%s' is invalid as an image height.  %s", arg, error);
Packit 78deda
        pm_strfree(error);
Packit 78deda
    } else {
Packit 78deda
        if (height > INT_MAX-10)
Packit 78deda
            pm_error("Height %u is too large for computations.", height);
Packit 78deda
        if (height == 0)
Packit 78deda
            pm_error("Height argument must be a positive number.  You "
Packit 78deda
                     "specified 0.");
Packit 78deda
    }
Packit 78deda
    return height;
Packit 78deda
}
Packit 78deda
Packit 38c941
Packit 78deda