1 /**************************************************************************
2                                   libpm.c
3 ***************************************************************************
4   This file contains fundamental libnetpbm services.
5 
6   Some of the subroutines in this library are intended and documented
7   for use by Netpbm users, but most of them are just used by other
8   Netpbm library subroutines.
9 **************************************************************************/
10 
11 #define _DEFAULT_SOURCE      /* New name for SVID & BSD source defines */
12 #define _BSD_SOURCE 1
13 
14 #include "netpbm/pm_config.h"
15 
16 #include <assert.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <setjmp.h>
24 #include <time.h>
25 #include <limits.h>
26 #if HAVE_FORK
27 #include <sys/wait.h>
28 #endif
29 #include <sys/types.h>
30 
31 #include "netpbm/pm_c_util.h"
32 #include "netpbm/mallocvar.h"
33 #include "netpbm/version.h"
34 #include "netpbm/nstring.h"
35 #include "netpbm/shhopt.h"
36 #include "compile.h"
37 
38 #include "pm.h"
39 
40 /* The following are set by pm_init(), then used by subsequent calls to other
41    pm_xxx() functions.
42    */
43 const char * pm_progname;
44 
45 int pm_plain_output;
46     /* Boolean: programs should produce output in plain format */
47 
48 static bool pm_showmessages;
49     /* Programs should display informational messages (because the user didn't
50        specify the --quiet option).
51     */
52 static jmp_buf * pm_jmpbufP = NULL;
53     /* A description of the point to which the program should hyperjump
54        if a libnetpbm function encounters an error (libnetpbm functions
55        don't normally return in that case).
56 
57        User sets this to something in his own extra-library context.
58        Libnetpbm routines that have something that needs to be cleaned up
59        preempt it.
60 
61        NULL, which is the default value, means when a libnetpbm function
62        encounters an error, it causes the process to exit.
63     */
64 static pm_usererrormsgfn * userErrorMsgFn = NULL;
65     /* A function to call to issue an error message.
66 
67        NULL means use the library default: print to Standard Error
68     */
69 
70 static pm_usermessagefn * userMessageFn = NULL;
71     /* A function to call to issue a non-error message.
72 
73        NULL means use the library default: print to Standard Error
74     */
75 
76 
77 
78 void
pm_setjmpbuf(jmp_buf * const jmpbufP)79 pm_setjmpbuf(jmp_buf * const jmpbufP) {
80     pm_jmpbufP = jmpbufP;
81 }
82 
83 
84 
85 void
pm_setjmpbufsave(jmp_buf * const jmpbufP,jmp_buf ** const oldJmpbufPP)86 pm_setjmpbufsave(jmp_buf *  const jmpbufP,
87                  jmp_buf ** const oldJmpbufPP) {
88 
89     *oldJmpbufPP = pm_jmpbufP;
90     pm_jmpbufP = jmpbufP;
91 }
92 
93 
94 
95 void
pm_longjmp(void)96 pm_longjmp(void) {
97 
98     if (pm_jmpbufP)
99         longjmp(*pm_jmpbufP, 1);
100     else
101         exit(1);
102 }
103 
104 
105 
106 void
pm_fork(int * const iAmParentP,pid_t * const childPidP,const char ** const errorP)107 pm_fork(int *         const iAmParentP,
108         pid_t *       const childPidP,
109         const char ** const errorP) {
110 /*----------------------------------------------------------------------------
111    Same as POSIX fork, except with a nicer interface and works
112    (fails cleanly) on systems that don't have POSIX fork().
113 -----------------------------------------------------------------------------*/
114 #if HAVE_FORK
115     int rc;
116 
117     rc = fork();
118 
119     if (rc < 0) {
120         pm_asprintf(errorP, "Failed to fork a process.  errno=%d (%s)",
121                     errno, strerror(errno));
122     } else {
123         *errorP = NULL;
124 
125         if (rc == 0) {
126             *iAmParentP = FALSE;
127         } else {
128             *iAmParentP = TRUE;
129             *childPidP = rc;
130         }
131     }
132 #else
133     pm_asprintf(errorP, "Cannot fork a process, because this system does "
134                 "not have POSIX fork()");
135 #endif
136 }
137 
138 
139 
140 void
pm_waitpid(pid_t const pid,int * const statusP,int const options,pid_t * const exitedPidP,const char ** const errorP)141 pm_waitpid(pid_t         const pid,
142            int *         const statusP,
143            int           const options,
144            pid_t *       const exitedPidP,
145            const char ** const errorP) {
146 
147 #if HAVE_FORK
148     pid_t rc;
149     rc = waitpid(pid, statusP, options);
150     if (rc == (pid_t)-1) {
151         pm_asprintf(errorP, "Failed to wait for process exit.  "
152                     "waitpid() errno = %d (%s)",
153                     errno, strerror(errno));
154     } else {
155         *exitedPidP = rc;
156         *errorP = NULL;
157     }
158 #else
159     pm_error("INTERNAL ERROR: Attempt to wait for a process we created on "
160              "a system on which we can't create processes");
161 #endif
162 }
163 
164 
165 
166 void
pm_waitpidSimple(pid_t const pid)167 pm_waitpidSimple(pid_t const pid) {
168 
169     int status;
170     pid_t exitedPid;
171     const char * error;
172 
173     pm_waitpid(pid, &status, 0, &exitedPid, &error);
174 
175     if (error) {
176         pm_errormsg("%s", error);
177         pm_strfree(error);
178         pm_longjmp();
179     } else {
180         assert(exitedPid != 0);
181     }
182 }
183 
184 
185 
186 void
pm_setusererrormsgfn(pm_usererrormsgfn * fn)187 pm_setusererrormsgfn(pm_usererrormsgfn * fn) {
188 
189     userErrorMsgFn = fn;
190 }
191 
192 
193 
194 void
pm_setusermessagefn(pm_usermessagefn * fn)195 pm_setusermessagefn(pm_usermessagefn * fn) {
196 
197     userMessageFn = fn;
198 }
199 
200 
201 
202 void
pm_usage(const char usage[])203 pm_usage(const char usage[]) {
204     pm_error("usage:  %s %s", pm_progname, usage);
205 }
206 
207 
208 
209 void PM_GNU_PRINTF_ATTR(1,2)
pm_message(const char format[],...)210 pm_message(const char format[], ...) {
211 
212     va_list args;
213 
214     va_start(args, format);
215 
216     if (pm_showmessages) {
217         const char * msg;
218         pm_vasprintf(&msg, format, args);
219 
220         if (userMessageFn)
221             userMessageFn(msg);
222         else
223             fprintf(stderr, "%s: %s\n", pm_progname, msg);
224 
225         pm_strfree(msg);
226     }
227     va_end(args);
228 }
229 
230 
231 
232 static void
errormsg(const char * const msg)233 errormsg(const char * const msg) {
234 
235     if (userErrorMsgFn)
236         userErrorMsgFn(msg);
237     else
238         fprintf(stderr, "%s: %s\n", pm_progname, msg);
239 }
240 
241 
242 
243 void PM_GNU_PRINTF_ATTR(1,2)
pm_errormsg(const char format[],...)244 pm_errormsg(const char format[], ...) {
245 
246     va_list args;
247     const char * msg;
248 
249     va_start(args, format);
250 
251     pm_vasprintf(&msg, format, args);
252 
253     errormsg(msg);
254 
255     pm_strfree(msg);
256 
257     va_end(args);
258 }
259 
260 
261 
262 void PM_GNU_PRINTF_ATTR(1,2)
pm_error(const char format[],...)263 pm_error(const char format[], ...) {
264     va_list args;
265     const char * msg;
266 
267     va_start(args, format);
268 
269     pm_vasprintf(&msg, format, args);
270 
271     errormsg(msg);
272 
273     pm_strfree(msg);
274 
275     va_end(args);
276 
277     pm_longjmp();
278 }
279 
280 
281 
282 int
pm_have_float_format(void)283 pm_have_float_format(void) {
284 /*----------------------------------------------------------------------------
285   Return 1 if %f, %e, and %g work in format strings for pm_message, etc.;
286   0 otherwise.
287 
288   Where they don't "work," that means the specifier just appears itself in
289   the formatted strings, e.g. "the number is g".
290 -----------------------------------------------------------------------------*/
291     return pm_vasprintf_knows_float();
292 }
293 
294 
295 
296 static void *
mallocz(size_t const size)297 mallocz(size_t const size) {
298 
299     return malloc(MAX(1, size));
300 }
301 
302 
303 
304 void *
pm_allocrow(unsigned int const cols,unsigned int const size)305 pm_allocrow(unsigned int const cols,
306             unsigned int const size) {
307 
308     unsigned char * itrow;
309 
310     if (cols != 0 && UINT_MAX / cols < size)
311         pm_error("Arithmetic overflow multiplying %u by %u to get the "
312                  "size of a row to allocate.", cols, size);
313 
314     itrow = mallocz(cols * size);
315     if (itrow == NULL)
316         pm_error("out of memory allocating a row");
317 
318     return itrow;
319 }
320 
321 
322 
323 void
pm_freerow(void * const itrow)324 pm_freerow(void * const itrow) {
325     free(itrow);
326 }
327 
328 
329 
330 char **
pm_allocarray(int const cols,int const rows,int const elementSize)331 pm_allocarray(int const cols,
332               int const rows,
333               int const elementSize ) {
334 /*----------------------------------------------------------------------------
335    This is for backward compatibility.  MALLOCARRAY2 is usually better.
336 
337    A problem with pm_allocarray() is that its return type is char **
338    even though 'elementSize' can be other than 1.  So users have
339    traditionally type cast the result.  In the old days, that was just
340    messy; modern compilers can produce the wrong code if you do that.
341 -----------------------------------------------------------------------------*/
342     char ** retval;
343     void * result;
344 
345     pm_mallocarray2(&result, rows, cols, elementSize);
346 
347     if (result == NULL)
348         pm_error("Failed to allocate a raster array of %u columns x %u rows",
349                  cols, rows);
350 
351     retval = result;
352 
353     return retval;
354 }
355 
356 
357 
358 void
pm_freearray(char ** const rowIndex,int const rows)359 pm_freearray(char ** const rowIndex,
360              int     const rows) {
361 
362     void * const rowIndexVoid = rowIndex;
363 
364     pm_freearray2(rowIndexVoid);
365 }
366 
367 
368 
369 /* Case-insensitive keyword matcher. */
370 
371 int
pm_keymatch(const char * const strarg,const char * const keywordarg,int const minchars)372 pm_keymatch(const char *       const strarg,
373             const char * const keywordarg,
374             int          const minchars) {
375     int len;
376     const char * keyword;
377     const char * str;
378 
379     str = strarg;
380     keyword = keywordarg;
381 
382     len = strlen( str );
383     if ( len < minchars )
384         return 0;
385     while ( --len >= 0 )
386         {
387         register char c1, c2;
388 
389         c1 = *str++;
390         c2 = *keyword++;
391         if ( c2 == '\0' )
392             return 0;
393         if ( ISUPPER( c1 ) )
394             c1 = tolower( c1 );
395         if ( ISUPPER( c2 ) )
396             c2 = tolower( c2 );
397         if ( c1 != c2 )
398             return 0;
399         }
400     return 1;
401 }
402 
403 
404 /* Log base two hacks. */
405 
406 int
pm_maxvaltobits(int const maxval)407 pm_maxvaltobits(int const maxval) {
408     if ( maxval <= 1 )
409         return 1;
410     else if ( maxval <= 3 )
411         return 2;
412     else if ( maxval <= 7 )
413         return 3;
414     else if ( maxval <= 15 )
415         return 4;
416     else if ( maxval <= 31 )
417         return 5;
418     else if ( maxval <= 63 )
419         return 6;
420     else if ( maxval <= 127 )
421         return 7;
422     else if ( maxval <= 255 )
423         return 8;
424     else if ( maxval <= 511 )
425         return 9;
426     else if ( maxval <= 1023 )
427         return 10;
428     else if ( maxval <= 2047 )
429         return 11;
430     else if ( maxval <= 4095 )
431         return 12;
432     else if ( maxval <= 8191 )
433         return 13;
434     else if ( maxval <= 16383 )
435         return 14;
436     else if ( maxval <= 32767 )
437         return 15;
438     else if ( (long) maxval <= 65535L )
439         return 16;
440     else
441         pm_error( "maxval of %d is too large!", maxval );
442 
443     assert(false);
444 }
445 
446 int
pm_bitstomaxval(int const bits)447 pm_bitstomaxval(int const bits) {
448     return ( 1 << bits ) - 1;
449 }
450 
451 
452 unsigned int PURE_FN_ATTR
pm_lcm(unsigned int const x,unsigned int const y,unsigned int const z,unsigned int const limit)453 pm_lcm(unsigned int const x,
454        unsigned int const y,
455        unsigned int const z,
456        unsigned int const limit) {
457 /*----------------------------------------------------------------------------
458   Compute the least common multiple of 'x', 'y', and 'z'.  If it's bigger than
459   'limit', though, just return 'limit'.
460 -----------------------------------------------------------------------------*/
461     unsigned int biggest;
462     unsigned int candidate;
463 
464     if (x == 0 || y == 0 || z == 0)
465         pm_error("pm_lcm(): Least common multiple of zero taken.");
466 
467     biggest = MAX(x, MAX(y,z));
468 
469     candidate = biggest;
470     while (((candidate % x) != 0 ||       /* not a multiple of x */
471             (candidate % y) != 0 ||       /* not a multiple of y */
472             (candidate % z) != 0 ) &&     /* not a multiple of z */
473            candidate <= limit)
474         candidate += biggest;
475 
476     if (candidate > limit)
477         candidate = limit;
478 
479     return candidate;
480 }
481 
482 
483 
484 void
pm_init(const char * const progname,unsigned int const flags)485 pm_init(const char * const progname,
486         unsigned int const flags) {
487 /*----------------------------------------------------------------------------
488    Initialize static variables that Netpbm library routines use.
489 
490    Any user of Netpbm library routines is expected to call this at the
491    beginning of this program, before any other Netpbm library routines.
492 
493    A program may call this via pm_proginit() instead, though.
494 -----------------------------------------------------------------------------*/
495     pm_setMessage(FALSE, NULL);
496 
497     pm_progname = progname;
498 
499 #ifdef O_BINARY
500 #ifdef HAVE_SETMODE
501     /* Set the stdin and stdout mode to binary.  This means nothing on Unix,
502        but matters on Windows.
503 
504        Note that stdin and stdout aren't necessarily image files.  In
505        particular, stdout is sometimes text for human consumption,
506        typically printed on the terminal.  Binary mode isn't really
507        appropriate for that case.  We do this setting here without
508        any knowledge of how stdin and stdout are being used because it is
509        easy.  But we do make an exception for the case that we know the
510        file is a terminal, to get a little closer to doing the right
511        thing.
512     */
513     if (!isatty(0)) setmode(0,O_BINARY);  /* Standard Input */
514     if (!isatty(1)) setmode(1,O_BINARY);  /* Standard Output */
515 #endif /* HAVE_SETMODE */
516 #endif /* O_BINARY */
517 }
518 
519 
520 
521 static const char *
dtMsg(time_t const dateTime)522 dtMsg(time_t const dateTime) {
523 /*----------------------------------------------------------------------------
524    Text for the version message to indicate datetime 'dateTime'.
525 -----------------------------------------------------------------------------*/
526     struct tm * const brokenTimeP = localtime(&dateTime);
527 
528     char buffer[100];
529 
530     strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", brokenTimeP);
531 
532     return pm_strdup(buffer);
533 }
534 
535 
536 
537 static void
showVersion(void)538 showVersion(void) {
539 
540     pm_message("Using libnetpbm from Netpbm Version: %s", NETPBM_VERSION);
541 
542     /* SOURCE_DATETIME is defined when the user wants a reproducible build,
543        so wants the source code modification datetime instead of the build
544        datetime in the object code.
545     */
546 #if defined(SOURCE_DATETIME)
547     {
548         const char * const sourceDtMsg = dtMsg(SOURCE_DATETIME);
549         pm_message("Built from source dated %s", sourceDtMsg);
550         pm_strfree(sourceDtMsg);
551     }
552 #else
553   #if defined(BUILD_DATETIME)
554     {
555         const char * const buildDtMsg = dtMsg(BUILD_DATETIME);
556         pm_message("Built at %s", buildDtMsg);
557         pm_strfree(buildDtMsg);
558     }
559   #endif
560 #endif
561 
562 #if defined(COMPILED_BY)
563     pm_message("Built by %s", COMPILED_BY);
564 #endif
565 
566 #ifdef BSD
567     pm_message( "BSD defined" );
568 #endif /*BSD*/
569 #ifdef SYSV
570     pm_message( "SYSV defined" );
571 #endif /*SYSV*/
572 #ifdef MSDOS
573     pm_message( "MSDOS defined" );
574 #endif /*MSDOS*/
575 #ifdef AMIGA
576     pm_message( "AMIGA defined" );
577 #endif /* AMIGA */
578     {
579         const char * rgbdef;
580         pm_message( "RGB_ENV='%s'", RGBENV );
581         rgbdef = getenv(RGBENV);
582         if( rgbdef )
583             pm_message( "RGBENV= '%s' (env vbl set to '%s')",
584                         RGBENV, rgbdef );
585         else
586             pm_message( "RGBENV= '%s' (env vbl is unset)", RGBENV);
587     }
588 }
589 
590 
591 
592 static void
showNetpbmHelp(const char progname[])593 showNetpbmHelp(const char progname[]) {
594 /*----------------------------------------------------------------------------
595   Tell the user where to get help for this program, assuming it is a Netpbm
596   program (a program that comes with the Netpbm package, as opposed to a
597   program that just uses the Netpbm libraries).
598 
599   Tell him to go to the URL listed in the Netpbm configuration file.
600   The Netpbm configuration file is the file named by the NETPBM_CONF
601   environment variable, or /etc/netpbm if there is no such environment
602   variable.
603 
604   If the configuration file doesn't exist or can't be read, or doesn't
605   contain a DOCURL value, tell him to go to a hardcoded source for
606   documentation.
607 -----------------------------------------------------------------------------*/
608     const char * netpbmConfigFileName;
609     FILE * netpbmConfigFile;
610     char * docurl;
611 
612     if (getenv("NETPBM_CONF"))
613         netpbmConfigFileName = getenv("NETPBM_CONF");
614     else
615         netpbmConfigFileName = "/etc/netpbm";
616 
617     netpbmConfigFile = fopen(netpbmConfigFileName, "r");
618     if (netpbmConfigFile == NULL) {
619         pm_message("Unable to open Netpbm configuration file '%s'.  "
620                    "Errno = %d (%s).  "
621                    "Use the NETPBM_CONF environment variable "
622                    "to control the identity of the Netpbm configuration file.",
623                    netpbmConfigFileName,errno, strerror(errno));
624         docurl = NULL;
625     } else {
626         docurl = NULL;  /* default */
627         while (!feof(netpbmConfigFile) && !ferror(netpbmConfigFile)) {
628             char line[80+1];
629             fgets(line, sizeof(line), netpbmConfigFile);
630             if (line[0] != '#') {
631                 sscanf(line, "docurl=%s", docurl);
632             }
633         }
634         if (docurl == NULL)
635             pm_message("No 'docurl=' line in Netpbm configuration file '%s'.",
636                        netpbmConfigFileName);
637 
638         fclose(netpbmConfigFile);
639     }
640     if (docurl == NULL)
641         pm_message("We have no reliable indication of where the Netpbm "
642                    "documentation is, but try "
643                    "http://netpbm.sourceforge.net or email "
644                    "Bryan Henderson (bryanh@giraffe-data.com) for help.");
645     else
646         pm_message("This program is part of the Netpbm package.  Find "
647                    "documentation for it at %s/%s\n", docurl, progname);
648 }
649 
650 
651 
652 static void
parseCommonOptions(int * const argcP,const char ** const argv,bool * const showMessagesP,bool * const showVersionP,bool * const showHelpP,bool * const plainOutputP)653 parseCommonOptions(int *         const argcP,
654                    const char ** const argv,
655                    bool *        const showMessagesP,
656                    bool *        const showVersionP,
657                    bool *        const showHelpP,
658                    bool *        const plainOutputP) {
659 
660     unsigned int inCursor;
661     unsigned int outCursor;
662 
663     *showMessagesP = true;   /* initial assumption */
664     *showVersionP  = false;  /* initial assumption */
665     *showHelpP     = false;  /* initial assumption */
666     *plainOutputP  = false;  /* initial assumption */
667 
668     for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) {
669             if (strcaseeq(argv[inCursor], "-quiet") ||
670                 strcaseeq(argv[inCursor], "--quiet"))
671                 *showMessagesP = false;
672             else if (strcaseeq(argv[inCursor], "-version") ||
673                      strcaseeq(argv[inCursor], "--version"))
674                 *showVersionP = true;
675             else if (strcaseeq(argv[inCursor], "-help") ||
676                      strcaseeq(argv[inCursor], "--help") ||
677                      strcaseeq(argv[inCursor], "-?"))
678                 *showHelpP = true;
679             else if (strcaseeq(argv[inCursor], "-plain") ||
680                      strcaseeq(argv[inCursor], "--plain"))
681                 *plainOutputP = true;
682             else
683                 argv[outCursor++] = argv[inCursor];
684         }
685     *argcP = outCursor;
686 }
687 
688 
689 
690 void
pm_proginit(int * const argcP,const char ** const argv)691 pm_proginit(int *         const argcP,
692             const char ** const argv) {
693 /*----------------------------------------------------------------------------
694    Do various initialization things that all programs in the Netpbm package,
695    and programs that emulate such programs, should do.
696 
697    This includes processing global options.  We scan argv[], which has *argcP
698    elements, for common options and execute the functions for the ones we
699    find.  We remove the common options from argv[], updating *argcP
700    accordingly.
701 
702    This includes calling pm_init() to initialize the Netpbm libraries.
703 -----------------------------------------------------------------------------*/
704     const char * const progname = pm_arg0toprogname(argv[0]);
705         /* points to static memory in this library */
706     bool showMessages;
707     bool plain;
708     bool justShowVersion;
709         /* We're supposed to just show the version information, then exit the
710            program.
711         */
712     bool justShowHelp;
713         /* We're supposed to just tell user where to get help, then exit the
714            program.
715         */
716 
717     pm_init(progname, 0);
718 
719     parseCommonOptions(argcP, argv,
720                        &showMessages, &justShowVersion, &justShowHelp,
721                        &plain);
722 
723     pm_plain_output = plain ? 1 : 0;
724 
725     pm_setMessage(showMessages ? 1 : 0, NULL);
726 
727     if (justShowVersion) {
728         showVersion();
729         exit(0);
730     } else if (justShowHelp) {
731         pm_error("Use 'man %s' for help.", progname);
732         /* If we can figure out a way to distinguish Netpbm programs from
733            other programs using the Netpbm libraries, we can do better here.
734         */
735         if (0)
736             showNetpbmHelp(progname);
737         exit(0);
738     }
739 }
740 
741 
742 
743 void
pm_setMessage(int const newState,int * const oldStateP)744 pm_setMessage(int   const newState,
745               int * const oldStateP) {
746 
747     if (oldStateP)
748         *oldStateP = pm_showmessages;
749 
750     pm_showmessages = !!newState;
751 }
752 
753 
754 
755 int
pm_getMessage(void)756 pm_getMessage(void) {
757 
758     return pm_showmessages;
759 }
760 
761 
762 
763 static void
extractAfterLastSlash(const char * const fullPath,char * const retval,size_t const retvalSize)764 extractAfterLastSlash(const char * const fullPath,
765                       char *       const retval,
766                       size_t       const retvalSize) {
767 
768     char * slashPos;
769 
770     /* Chop any directories off the left end */
771     slashPos = strrchr(fullPath, '/');
772 
773     if (slashPos == NULL) {
774         strncpy(retval, fullPath, retvalSize-1);
775         retval[retvalSize-1] = '\0';
776     } else {
777         strncpy(retval, slashPos + 1, retvalSize-1);
778         retval[retvalSize-1] = '\0';
779     }
780 }
781 
782 
783 
784 static void
chopOffExe(char * const arg)785 chopOffExe(char * const arg) {
786 /*----------------------------------------------------------------------------
787   Chop any .exe off the right end of 'arg'.
788 -----------------------------------------------------------------------------*/
789     if (strlen(arg) >= 4 && strcmp(arg+strlen(arg)-4, ".exe") == 0)
790         arg[strlen(arg)-4] = 0;
791 }
792 
793 
794 
795 char *
pm_arg0toprogname(const char arg0[])796 pm_arg0toprogname(const char arg0[]) {
797 /*----------------------------------------------------------------------------
798    Given a value for argv[0] (a command name or file name passed to a
799    program in the standard C calling sequence), return the name of the
800    Netpbm program to which it refers.
801 
802    In the most ordinary case, this is simply the argument itself.
803 
804    But if the argument contains a slash, it is the part of the argument
805    after the last slash, and if there is a .exe on it (as there is for
806    DJGPP), that is removed.
807 
808    The return value is in static storage within.  It is NUL-terminated,
809    but truncated at 64 characters.
810 -----------------------------------------------------------------------------*/
811 #define MAX_RETVAL_SIZE 64
812 #if MSVCRT
813     /* Note that there exists _splitpath_s, which takes a size argument,
814        but it is only in "secure" extensions of MSVCRT that come only with
815        MSVC; but _splitpath() comes with Windows.  MinGW has a header file
816        for it.
817     */
818     static char retval[_MAX_FNAME];
819     _splitpath(arg0, 0, 0,  retval, 0);
820     if (MAX_RETVAL_SIZE < _MAX_FNAME)
821         retval[MAX_RETVAL_SIZE] = '\0';
822 #else
823     static char retval[MAX_RETVAL_SIZE+1];
824     extractAfterLastSlash(arg0, retval, sizeof(retval));
825     chopOffExe(retval);
826 #endif
827 
828     return retval;
829 }
830 
831 
832 
833 unsigned int
pm_randseed(void)834 pm_randseed(void) {
835 
836     return arc4random();
837 
838 }
839 
840 
841 
842 unsigned int
pm_parse_width(const char * const arg)843 pm_parse_width(const char * const arg) {
844 /*----------------------------------------------------------------------------
845    Return the image width represented by the decimal ASCIIZ string
846    'arg'.  Fail if it doesn't validly represent a width or represents
847    a width that can't be conveniently used in computation.
848 -----------------------------------------------------------------------------*/
849     unsigned int width;
850     const char * error;
851 
852     pm_string_to_uint(arg, &width, &error);
853 
854     if (error) {
855         pm_error("'%s' is invalid as an image width.  %s", arg, error);
856         pm_strfree(error);
857     } else {
858         if (width > INT_MAX-10)
859             pm_error("Width %u is too large for computations.", width);
860         if (width == 0)
861             pm_error("Width argument must be a positive number.  You "
862                      "specified 0.");
863     }
864     return width;
865 }
866 
867 
868 
869 unsigned int
pm_parse_height(const char * const arg)870 pm_parse_height(const char * const arg) {
871 /*----------------------------------------------------------------------------
872   Same as pm_parse_width(), but for height.
873 -----------------------------------------------------------------------------*/
874     unsigned int height;
875     const char * error;
876 
877     pm_string_to_uint(arg, &height, &error);
878 
879     if (error) {
880         pm_error("'%s' is invalid as an image height.  %s", arg, error);
881         pm_strfree(error);
882     } else {
883         if (height > INT_MAX-10)
884             pm_error("Height %u is too large for computations.", height);
885         if (height == 0)
886             pm_error("Height argument must be a positive number.  You "
887                      "specified 0.");
888     }
889     return height;
890 }
891 
892 
893 
894