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