diff options
Diffstat (limited to 'lib/libpm.c')
-rw-r--r-- | lib/libpm.c | 477 |
1 files changed, 247 insertions, 230 deletions
diff --git a/lib/libpm.c b/lib/libpm.c index 910d5666..4374bbe3 100644 --- a/lib/libpm.c +++ b/lib/libpm.c @@ -8,8 +8,12 @@ Netpbm library subroutines. **************************************************************************/ +#define _BSD_SOURCE /* Make sure strdup is in string.h */ #define _XOPEN_SOURCE 500 /* Make sure ftello, fseeko are defined */ +#include "netpbm/pm_config.h" + +#include <assert.h> #include <unistd.h> #include <stdio.h> #include <stdarg.h> @@ -18,13 +22,17 @@ #include <setjmp.h> #include <time.h> #include <limits.h> +#if HAVE_FORK +#include <sys/wait.h> +#endif +#include <sys/types.h> -#include "pm_c_util.h" -#include "mallocvar.h" -#include "version.h" +#include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" +#include "netpbm/version.h" +#include "netpbm/nstring.h" +#include "netpbm/shhopt.h" #include "compile.h" -#include "nstring.h" -#include "shhopt.h" #include "pm.h" @@ -95,6 +103,86 @@ pm_longjmp(void) { void +pm_fork(int * const iAmParentP, + pid_t * const childPidP, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Same as POSIX fork, except with a nicer interface and works + (fails cleanly) on systems that don't have POSIX fork(). +-----------------------------------------------------------------------------*/ +#if HAVE_FORK + int rc; + + rc = fork(); + + if (rc < 0) { + pm_asprintf(errorP, "Failed to fork a process. errno=%d (%s)", + errno, strerror(errno)); + } else { + *errorP = NULL; + + if (rc == 0) { + *iAmParentP = FALSE; + } else { + *iAmParentP = TRUE; + *childPidP = rc; + } + } +#else + pm_asprintf(errorP, "Cannot fork a process, because this system does " + "not have POSIX fork()"); +#endif +} + + + +void +pm_waitpid(pid_t const pid, + int * const statusP, + int const options, + pid_t * const exitedPidP, + const char ** const errorP) { + +#if HAVE_FORK + pid_t rc; + rc = waitpid(pid, statusP, options); + if (rc == (pid_t)-1) { + pm_asprintf(errorP, "Failed to wait for process exit. " + "waitpid() errno = %d (%s)", + errno, strerror(errno)); + } else { + *exitedPidP = rc; + *errorP = NULL; + } +#else + pm_error("INTERNAL ERROR: Attempt to wait for a process we created on " + "a system on which we can't create processes"); +#endif +} + + + +void +pm_waitpidSimple(pid_t const pid) { + + int status; + pid_t exitedPid; + const char * error; + + pm_waitpid(pid, &status, 0, &exitedPid, &error); + + if (error) { + pm_errormsg("%s", error); + pm_strfree(error); + pm_longjmp(); + } else { + assert(exitedPid != 0); + } +} + + + +void pm_setusererrormsgfn(pm_usererrormsgfn * fn) { userErrorMsgFn = fn; @@ -126,14 +214,14 @@ pm_message(const char format[], ...) { if (pm_showmessages) { const char * msg; - vasprintfN(&msg, format, args); + pm_vasprintf(&msg, format, args); if (userMessageFn) userMessageFn(msg); else fprintf(stderr, "%s: %s\n", pm_progname, msg); - strfree(msg); + pm_strfree(msg); } va_end(args); } @@ -159,11 +247,11 @@ pm_errormsg(const char format[], ...) { va_start(args, format); - vasprintfN(&msg, format, args); + pm_vasprintf(&msg, format, args); errormsg(msg); - strfree(msg); + pm_strfree(msg); va_end(args); } @@ -177,11 +265,11 @@ pm_error(const char format[], ...) { va_start(args, format); - vasprintfN(&msg, format, args); + pm_vasprintf(&msg, format, args); errormsg(msg); - strfree(msg); + pm_strfree(msg); va_end(args); @@ -190,6 +278,20 @@ pm_error(const char format[], ...) { +int +pm_have_float_format(void) { +/*---------------------------------------------------------------------------- + Return 1 if %f, %e, and %g work in format strings for pm_message, etc.; + 0 otherwise. + + Where they don't "work," that means the specifier just appears itself in + the formatted strings, e.g. "the number is g". +-----------------------------------------------------------------------------*/ + return pm_vasprintf_knows_float(); +} + + + static void * mallocz(size_t const size) { @@ -224,122 +326,30 @@ pm_freerow(void * const itrow) { -static void -allocarrayNoHeap(unsigned char ** const rowIndex, - unsigned int const cols, - unsigned int const rows, - unsigned int const size, - const char ** const errorP) { - - if (cols != 0 && UINT_MAX / cols < size) - asprintfN(errorP, - "Arithmetic overflow multiplying %u by %u to get the " - "size of a row to allocate.", cols, size); - else { - unsigned int rowsDone; - - rowsDone = 0; - *errorP = NULL; - - while (rowsDone < rows && !*errorP) { - unsigned char * const rowSpace = mallocz(cols * size); - if (rowSpace == NULL) - asprintfN(errorP, - "Unable to allocate a %u-column by %u byte row", - cols, size); - else - rowIndex[rowsDone++] = rowSpace; - } - if (*errorP) { - unsigned int row; - for (row = 0; row < rowsDone; ++row) - free(rowIndex[row]); - } - } -} - - - -static unsigned char * -allocRowHeap(unsigned int const cols, - unsigned int const rows, - unsigned int const size) { - - unsigned char * retval; - - if (cols != 0 && rows != 0 && UINT_MAX / cols / rows < size) - /* Too big even to request the memory ! */ - retval = NULL; - else - retval = mallocz(rows * cols * size); - - return retval; -} - - - char ** pm_allocarray(int const cols, int const rows, - int const size ) { + int const elementSize ) { /*---------------------------------------------------------------------------- - Allocate an array of 'rows' rows of 'cols' columns each, with each - element 'size' bytes. - - We use a special format where we tack on an extra element to the row - index to indicate the format of the array. - - We have two ways of allocating the space: fragmented and - unfragmented. In both, the row index (plus the extra element) is - in one block of memory. In the fragmented format, each row is - also in an independent memory block, and the extra row pointer is - NULL. In the unfragmented format, all the rows are in a single - block of memory called the row heap and the extra row pointer is - the address of that block. - - We use unfragmented format if possible, but if the allocation of the - row heap fails, we fall back to fragmented. ------------------------------------------------------------------------------*/ - unsigned char ** rowIndex; - const char * error; + This is for backward compatibility. MALLOCARRAY2 is usually better. - MALLOCARRAY(rowIndex, rows + 1); - if (rowIndex == NULL) - asprintfN(&error, - "out of memory allocating row index (%u rows) for an array", - rows); - else { - unsigned char * rowheap; + A problem with pm_allocarray() is that its return type is char ** + even though 'elementSize' can be other than 1. So users have + traditionally type cast the result. In the old days, that was just + messy; modern compilers can produce the wrong code if you do that. +-----------------------------------------------------------------------------*/ + char ** retval; + void * result; - rowheap = allocRowHeap(cols, rows, size); + pm_mallocarray2(&result, rows, cols, elementSize); - if (rowheap) { - /* It's unfragmented format */ + if (result == NULL) + pm_error("Failed to allocate a raster array of %u columns x %u rows", + cols, rows); - rowIndex[rows] = rowheap; /* Declare it unfragmented format */ + retval = result; - if (rowheap) { - unsigned int row; - - for (row = 0; row < rows; ++row) - rowIndex[row] = &(rowheap[row * cols * size]); - } - error = NULL; - } else { - /* We couldn't get the whole heap in one block, so try fragmented - format. - */ - rowIndex[rows] = NULL; /* Declare it fragmented format */ - - allocarrayNoHeap(rowIndex, cols, rows, size, &error); - } - } - if (error) { - pm_errormsg("Couldn't allocate %u-row array. %s", rows, error); - strfree(error); - pm_longjmp(); - } - return (char **)rowIndex; + return retval; } @@ -348,16 +358,9 @@ void pm_freearray(char ** const rowIndex, int const rows) { - void * const rowheap = rowIndex[rows]; + void * const rowIndexVoid = rowIndex; - if (rowheap != NULL) - free(rowheap); - else { - unsigned int row; - for (row = 0; row < rows; ++row) - pm_freerow(rowIndex[row]); - } - free(rowIndex); + pm_freearray2(rowIndexVoid); } @@ -475,37 +478,6 @@ pm_lcm(unsigned int const x, } -/* Initialization. */ - - -#ifdef VMS -static const char * -vmsProgname(int * const argcP, char * argv[]) { - char **temp_argv = argv; - int old_argc = *argcP; - int i; - const char * retval; - - getredirection( argcP, &temp_argv ); - if (*argcP > old_argc) { - /* Number of command line arguments has increased */ - fprintf( stderr, "Sorry!! getredirection() for VMS has " - "changed the argument list!!!\n"); - fprintf( stderr, "This is intolerable at the present time, " - "so we must stop!!!\n"); - exit(1); - } - for (i=0; i<*argcP; i++) - argv[i] = temp_argv[i]; - retval = strrchr( argv[0], '/'); - if ( retval == NULL ) retval = rindex( argv[0], ']'); - if ( retval == NULL ) retval = rindex( argv[0], '>'); - - return retval; -} -#endif - - void pm_init(const char * const progname, @@ -555,11 +527,7 @@ showVersion(void) { pm_message( "BSD defined" ); #endif /*BSD*/ #ifdef SYSV -#ifdef VMS - pm_message( "VMS & SYSV defined" ); -#else pm_message( "SYSV defined" ); -#endif #endif /*SYSV*/ #ifdef MSDOS pm_message( "MSDOS defined" ); @@ -626,6 +594,8 @@ showNetpbmHelp(const char progname[]) { if (docurl == NULL) pm_message("No 'docurl=' line in Netpbm configuration file '%s'.", netpbmConfigFileName); + + fclose(netpbmConfigFile); } if (docurl == NULL) pm_message("We have no reliable indication of where the Netpbm " @@ -639,71 +609,85 @@ showNetpbmHelp(const char progname[]) { +static void +parseCommonOptions(int * const argcP, + const char ** const argv, + bool * const showMessagesP, + bool * const showVersionP, + bool * const showHelpP, + bool * const plainOutputP) { + + unsigned int inCursor; + unsigned int outCursor; + + *showMessagesP = true; /* initial assumption */ + *showVersionP = false; /* initial assumption */ + *showHelpP = false; /* initial assumption */ + *plainOutputP = false; /* initial assumption */ + + for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) { + if (strcaseeq(argv[inCursor], "-quiet") || + strcaseeq(argv[inCursor], "--quiet")) + *showMessagesP = false; + else if (strcaseeq(argv[inCursor], "-version") || + strcaseeq(argv[inCursor], "--version")) + *showVersionP = true; + else if (strcaseeq(argv[inCursor], "-help") || + strcaseeq(argv[inCursor], "--help") || + strcaseeq(argv[inCursor], "-?")) + *showHelpP = true; + else if (strcaseeq(argv[inCursor], "-plain") || + strcaseeq(argv[inCursor], "--plain")) + *plainOutputP = true; + else + argv[outCursor++] = argv[inCursor]; + } + *argcP = outCursor; +} + + + void -pm_proginit(int * const argcP, const char * argv[]) { +pm_proginit(int * const argcP, + const char ** const argv) { /*---------------------------------------------------------------------------- Do various initialization things that all programs in the Netpbm package, and programs that emulate such programs, should do. - This includes processing global options. + This includes processing global options. We scan argv[], which has *argcP + elements, for common options and execute the functions for the ones we + find. We remove the common options from argv[], updating *argcP + accordingly. This includes calling pm_init() to initialize the Netpbm libraries. -----------------------------------------------------------------------------*/ - int argn, i; - const char * progname; - bool showmessages; - bool show_version; + const char * const progname = pm_arg0toprogname(argv[0]); + /* points to static memory in this library */ + bool showMessages; + bool plain; + bool justShowVersion; /* We're supposed to just show the version information, then exit the program. */ - bool show_help; + bool justShowHelp; /* We're supposed to just tell user where to get help, then exit the program. */ - - /* Extract program name. */ -#ifdef VMS - progname = vmsProgname(argcP, argv); -#else - progname = strrchr( argv[0], '/'); -#endif - if (progname == NULL) - progname = argv[0]; - else - ++progname; pm_init(progname, 0); - /* Check for any global args. */ - showmessages = TRUE; - show_version = FALSE; - show_help = FALSE; - pm_plain_output = FALSE; - for (argn = i = 1; argn < *argcP; ++argn) { - if (pm_keymatch(argv[argn], "-quiet", 6) || - pm_keymatch(argv[argn], "--quiet", 7)) - showmessages = FALSE; - else if (pm_keymatch(argv[argn], "-version", 8) || - pm_keymatch(argv[argn], "--version", 9)) - show_version = TRUE; - else if (pm_keymatch(argv[argn], "-help", 5) || - pm_keymatch(argv[argn], "--help", 6) || - pm_keymatch(argv[argn], "-?", 2)) - show_help = TRUE; - else if (pm_keymatch(argv[argn], "-plain", 6) || - pm_keymatch(argv[argn], "--plain", 7)) - pm_plain_output = TRUE; - else - argv[i++] = argv[argn]; - } - *argcP=i; + parseCommonOptions(argcP, argv, + &showMessages, &justShowVersion, &justShowHelp, + &plain); - pm_setMessage((unsigned int) showmessages, NULL); + pm_plain_output = plain ? 1 : 0; - if (show_version) { + pm_setMessage(showMessages ? 1 : 0, NULL); + + if (justShowVersion) { showVersion(); - exit( 0 ); - } else if (show_help) { + exit(0); + } else if (justShowHelp) { pm_error("Use 'man %s' for help.", progname); /* If we can figure out a way to distinguish Netpbm programs from other programs using the Netpbm libraries, we can do better here. @@ -715,8 +699,10 @@ pm_proginit(int * const argcP, const char * argv[]) { } + void -pm_setMessage(int const newState, int * const oldStateP) { +pm_setMessage(int const newState, + int * const oldStateP) { if (oldStateP) *oldStateP = pm_showmessages; @@ -726,6 +712,39 @@ pm_setMessage(int const newState, int * const oldStateP) { +int +pm_getMessage(void) { + + return pm_showmessages; +} + + + +static void +extractAfterLastSlash(const char * const fullPath, + char * const retval, + size_t const retvalSize) { + + char * slashPos; + + /* Chop any directories off the left end */ + slashPos = strrchr(fullPath, '/'); + + if (slashPos == NULL) { + strncpy(retval, fullPath, retvalSize); + retval[retvalSize-1] = '\0'; + } else { + strncpy(retval, slashPos +1, retvalSize); + retval[retvalSize-1] = '\0'; + } + + /* Chop any .exe off the right end */ + if (strlen(retval) >= 4 && strcmp(retval+strlen(retval)-4, ".exe") == 0) + retval[strlen(retval)-4] = 0; +} + + + char * pm_arg0toprogname(const char arg0[]) { /*---------------------------------------------------------------------------- @@ -742,25 +761,23 @@ pm_arg0toprogname(const char arg0[]) { The return value is in static storage within. It is NUL-terminated, but truncated at 64 characters. -----------------------------------------------------------------------------*/ - static char retval[64+1]; - char *slash_pos; - - /* Chop any directories off the left end */ - slash_pos = strrchr(arg0, '/'); - - if (slash_pos == NULL) { - strncpy(retval, arg0, sizeof(retval)); - retval[sizeof(retval)-1] = '\0'; - } else { - strncpy(retval, slash_pos +1, sizeof(retval)); - retval[sizeof(retval)-1] = '\0'; - } - - /* Chop any .exe off the right end */ - if (strlen(retval) >= 4 && strcmp(retval+strlen(retval)-4, ".exe") == 0) - retval[strlen(retval)-4] = 0; +#define MAX_RETVAL_SIZE 64 +#if MSVCRT + /* Note that there exists _splitpath_s, which takes a size argument, + but it is only in "secure" extensions of MSVCRT that come only with + MSVC; but _splitpath() comes with Windows. MinGW has a header file + for it. + */ + static char retval[_MAX_FNAME]; + _splitpath(arg0, 0, 0, retval, 0); + if (MAX_RETVAL_SIZE < _MAX_FNAME) + retval[MAX_RETVAL_SIZE] = '\0'; +#else + static char retval[MAX_RETVAL_SIZE+1]; + extractAfterLastSlash(arg0, retval, sizeof(retval)); +#endif - return(retval); + return retval; } @@ -784,11 +801,11 @@ pm_parse_width(const char * const arg) { unsigned int width; const char * error; - interpret_uint(arg, &width, &error); + pm_interpret_uint(arg, &width, &error); if (error) { pm_error("'%s' is invalid as an image width. %s", arg, error); - strfree(error); + pm_strfree(error); } else { if (width > INT_MAX-10) pm_error("Width %u is too large for computations.", width); @@ -809,11 +826,11 @@ pm_parse_height(const char * const arg) { unsigned int height; const char * error; - interpret_uint(arg, &height, &error); + pm_interpret_uint(arg, &height, &error); if (error) { pm_error("'%s' is invalid as an image height. %s", arg, error); - strfree(error); + pm_strfree(error); } else { if (height > INT_MAX-10) pm_error("Height %u is too large for computations.", height); |