Improve single-threaded operation. It's now safe(r?) to close a canvas while it...
[synfig.git] / gtkmm-osx / trunk / libpng-1.2.5 / contrib / gregbook / wpng.c
1 /*---------------------------------------------------------------------------
2
3    wpng - simple PNG-writing program                                 wpng.c
4
5    This program converts certain NetPBM binary files (grayscale and RGB,
6    maxval = 255) to PNG.  Non-interlaced PNGs are written progressively;
7    interlaced PNGs are read and written in one memory-intensive blast.
8    Thanks to Jean-loup Gailly for providing the necessary trick to read
9    interactive text from the keyboard while stdin is redirected.
10
11    NOTE:  includes provisional support for PNM type "8" (portable alphamap)
12           images, presumed to be a 32-bit interleaved RGBA format; no pro-
13           vision for possible interleaved grayscale+alpha (16-bit) format.
14           THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT!
15
16    to do:
17     - delete output file if quit before calling any writepng routines
18     - process backspace with -text option under DOS/Win? (currently get ^H)
19
20   ---------------------------------------------------------------------------
21
22    Changelog:
23     - 1.01:  initial public release
24     - 1.02:  modified to allow abbreviated options
25     - 1.03:  removed extraneous character from usage screen; fixed bug in
26               command-line parsing
27
28   ---------------------------------------------------------------------------
29
30       Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.
31
32       This software is provided "as is," without warranty of any kind,
33       express or implied.  In no event shall the author or contributors
34       be held liable for any damages arising in any way from the use of
35       this software.
36
37       Permission is granted to anyone to use this software for any purpose,
38       including commercial applications, and to alter it and redistribute
39       it freely, subject to the following restrictions:
40
41       1. Redistributions of source code must retain the above copyright
42          notice, disclaimer, and this list of conditions.
43       2. Redistributions in binary form must reproduce the above copyright
44          notice, disclaimer, and this list of conditions in the documenta-
45          tion and/or other materials provided with the distribution.
46       3. All advertising materials mentioning features or use of this
47          software must display the following acknowledgment:
48
49             This product includes software developed by Greg Roelofs
50             and contributors for the book, "PNG: The Definitive Guide,"
51             published by O'Reilly and Associates.
52
53   ---------------------------------------------------------------------------*/
54
55 #define PROGNAME  "wpng"
56 #define VERSION   "1.03 of 19 March 2000"
57 #define APPNAME   "Simple PGM/PPM/PAM to PNG Converter"
58
59 #if defined(__MSDOS__) || defined(__OS2__)
60 #  define DOS_OS2_W32
61 #elif defined(_WIN32) || defined(__WIN32__)
62 #  define DOS_OS2_W32
63 #endif
64
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <setjmp.h>     /* for jmpbuf declaration in writepng.h */
69 #include <time.h>
70
71 #ifdef DOS_OS2_W32
72 #  include <io.h>       /* for isatty(), setmode() prototypes */
73 #  include <fcntl.h>    /* O_BINARY for fdopen() without text translation */
74 #  ifdef __EMX__
75 #    ifndef getch
76 #      define getch() _read_kbd(0, 1, 0)    /* need getche() */
77 #    endif
78 #  else /* !__EMX__ */
79 #    ifdef __GO32__
80 #      include <pc.h>
81 #      define getch() getkey()  /* GRR:  need getche() */
82 #    else
83 #      include <conio.h>        /* for getche() console input */
84 #    endif
85 #  endif /* ?__EMX__ */
86 #  define FGETS(buf,len,stream)  dos_kbd_gets(buf,len)
87 #else
88 #  include <unistd.h>           /* for isatty() prototype */
89 #  define FGETS fgets
90 #endif
91
92 /* #define DEBUG  :  this enables the Trace() macros */
93
94 /* #define FORBID_LATIN1_CTRL  :  this requires the user to re-enter any
95    text that includes control characters discouraged by the PNG spec; text
96    that includes an escape character (27) must be re-entered regardless */
97
98 #include "writepng.h"   /* typedefs, common macros, writepng prototypes */
99
100
101
102 /* local prototypes */
103
104 static int  wpng_isvalid_latin1(uch *p, int len);
105 static void wpng_cleanup(void);
106
107 #ifdef DOS_OS2_W32
108    static char *dos_kbd_gets(char *buf, int len);
109 #endif
110
111
112
113 static mainprog_info wpng_info;   /* lone global */
114
115
116
117 int main(int argc, char **argv)
118 {
119 #ifndef DOS_OS2_W32
120     FILE *keybd;
121 #endif
122 #ifdef sgi
123     FILE *tmpfile;      /* or we could just use keybd, since no overlap */
124     char tmpline[80];
125 #endif
126     char *inname = NULL, outname[256];
127     char *p, pnmchar, pnmline[256];
128     char *bgstr, *textbuf = NULL;
129     ulg rowbytes;
130     int rc, len = 0;
131     int error = 0;
132     int text = FALSE;
133     int maxval;
134     double LUT_exponent;                /* just the lookup table */
135     double CRT_exponent = 2.2;          /* just the monitor */
136     double default_display_exponent;    /* whole display system */
137     double default_gamma = 0.0;
138
139
140     wpng_info.infile = NULL;
141     wpng_info.outfile = NULL;
142     wpng_info.image_data = NULL;
143     wpng_info.row_pointers = NULL;
144     wpng_info.filter = FALSE;
145     wpng_info.interlaced = FALSE;
146     wpng_info.have_bg = FALSE;
147     wpng_info.have_time = FALSE;
148     wpng_info.have_text = 0;
149     wpng_info.gamma = 0.0;
150
151
152     /* First get the default value for our display-system exponent, i.e.,
153      * the product of the CRT exponent and the exponent corresponding to
154      * the frame-buffer's lookup table (LUT), if any.  If the PNM image
155      * looks correct on the user's display system, its file gamma is the
156      * inverse of this value.  (Note that this is not an exhaustive list
157      * of LUT values--e.g., OpenStep has a lot of weird ones--but it should
158      * cover 99% of the current possibilities.  This section must ensure
159      * that default_display_exponent is positive.) */
160
161 #if defined(NeXT)
162     /* third-party utilities can modify the default LUT exponent */
163     LUT_exponent = 1.0 / 2.2;
164     /*
165     if (some_next_function_that_returns_gamma(&next_gamma))
166         LUT_exponent = 1.0 / next_gamma;
167      */
168 #elif defined(sgi)
169     LUT_exponent = 1.0 / 1.7;
170     /* there doesn't seem to be any documented function to
171      * get the "gamma" value, so we do it the hard way */
172     tmpfile = fopen("/etc/config/system.glGammaVal", "r");
173     if (tmpfile) {
174         double sgi_gamma;
175
176         fgets(tmpline, 80, tmpfile);
177         fclose(tmpfile);
178         sgi_gamma = atof(tmpline);
179         if (sgi_gamma > 0.0)
180             LUT_exponent = 1.0 / sgi_gamma;
181     }
182 #elif defined(Macintosh)
183     LUT_exponent = 1.8 / 2.61;
184     /*
185     if (some_mac_function_that_returns_gamma(&mac_gamma))
186         LUT_exponent = mac_gamma / 2.61;
187      */
188 #else
189     LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
190 #endif
191
192     /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
193     default_display_exponent = LUT_exponent * CRT_exponent;
194
195
196     /* If the user has set the SCREEN_GAMMA environment variable as suggested
197      * (somewhat imprecisely) in the libpng documentation, use that; otherwise
198      * use the default value we just calculated.  Either way, the user may
199      * override this via a command-line option. */
200
201     if ((p = getenv("SCREEN_GAMMA")) != NULL) {
202         double exponent = atof(p);
203
204         if (exponent > 0.0)
205             default_gamma = 1.0 / exponent;
206     }
207
208     if (default_gamma == 0.0)
209         default_gamma = 1.0 / default_display_exponent;
210
211
212     /* Now parse the command line for options and the PNM filename. */
213
214     while (*++argv && !error) {
215         if (!strncmp(*argv, "-i", 2)) {
216             wpng_info.interlaced = TRUE;
217         } else if (!strncmp(*argv, "-time", 3)) {
218             wpng_info.modtime = time(NULL);
219             wpng_info.have_time = TRUE;
220         } else if (!strncmp(*argv, "-text", 3)) {
221             text = TRUE;
222         } else if (!strncmp(*argv, "-gamma", 2)) {
223             if (!*++argv)
224                 ++error;
225             else {
226                 wpng_info.gamma = atof(*argv);
227                 if (wpng_info.gamma <= 0.0)
228                     ++error;
229                 else if (wpng_info.gamma > 1.01)
230                     fprintf(stderr, PROGNAME
231                       " warning:  file gammas are usually less than 1.0\n");
232             }
233         } else if (!strncmp(*argv, "-bgcolor", 4)) {
234             if (!*++argv)
235                 ++error;
236             else {
237                 bgstr = *argv;
238                 if (strlen(bgstr) != 7 || bgstr[0] != '#')
239                     ++error;
240                 else {
241                     unsigned r, g, b;  /* this way quiets compiler warnings */
242
243                     sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
244                     wpng_info.bg_red   = (uch)r;
245                     wpng_info.bg_green = (uch)g;
246                     wpng_info.bg_blue  = (uch)b;
247                     wpng_info.have_bg = TRUE;
248                 }
249             }
250         } else {
251             if (**argv != '-') {
252                 inname = *argv;
253                 if (argv[1])   /* shouldn't be any more args after filename */
254                     ++error;
255             } else
256                 ++error;   /* not expecting any other options */
257         }
258     }
259
260
261     /* open the input and output files, or register an error and abort */
262
263     if (!inname) {
264         if (isatty(0)) {
265             fprintf(stderr, PROGNAME
266               ":  must give input filename or provide image data via stdin\n");
267             ++error;
268         } else {
269 #ifdef DOS_OS2_W32
270             /* some buggy C libraries require BOTH setmode() and fdopen(bin) */
271             setmode(fileno(stdin), O_BINARY);
272             setmode(fileno(stdout), O_BINARY);
273 #endif
274             if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) {
275                 fprintf(stderr, PROGNAME
276                   ":  unable to reopen stdin in binary mode\n");
277                 ++error;
278             } else
279             if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) {
280                 fprintf(stderr, PROGNAME
281                   ":  unable to reopen stdout in binary mode\n");
282                 fclose(wpng_info.infile);
283                 ++error;
284             } else
285                 wpng_info.filter = TRUE;
286         }
287     } else if ((len = strlen(inname)) > 250) {
288         fprintf(stderr, PROGNAME ":  input filename is too long [%d chars]\n",
289           len);
290         ++error;
291     } else if (!(wpng_info.infile = fopen(inname, "rb"))) {
292         fprintf(stderr, PROGNAME ":  can't open input file [%s]\n", inname);
293         ++error;
294     }
295
296     if (!error) {
297         fgets(pnmline, 256, wpng_info.infile);
298         if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' &&
299             pnmchar != '6' && pnmchar != '8'))
300         {
301             fprintf(stderr, PROGNAME
302               ":  input file [%s] is not a binary PGM, PPM or PAM file\n",
303               inname);
304             ++error;
305         } else {
306             wpng_info.pnmtype = (int)(pnmchar - '0');
307             if (wpng_info.pnmtype != 8)
308                 wpng_info.have_bg = FALSE;  /* no need for bg if opaque */
309             do {
310                 fgets(pnmline, 256, wpng_info.infile);  /* lose any comments */
311             } while (pnmline[0] == '#');
312             sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height);
313             do {
314                 fgets(pnmline, 256, wpng_info.infile);  /* more comment lines */
315             } while (pnmline[0] == '#');
316             sscanf(pnmline, "%d", &maxval);
317             if (wpng_info.width <= 0L || wpng_info.height <= 0L ||
318                 maxval != 255)
319             {
320                 fprintf(stderr, PROGNAME
321                   ":  only positive width/height, maxval == 255 allowed \n");
322                 ++error;
323             }
324             wpng_info.sample_depth = 8;  /* <==> maxval 255 */
325
326             if (!wpng_info.filter) {
327                 /* make outname from inname */
328                 if ((p = strrchr(inname, '.')) == NULL ||
329                     (p - inname) != (len - 4))
330                 {
331                     strcpy(outname, inname);
332                     strcpy(outname+len, ".png");
333                 } else {
334                     len -= 4;
335                     strncpy(outname, inname, len);
336                     strcpy(outname+len, ".png");
337                 }
338                 /* check if outname already exists; if not, open */
339                 if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) {
340                     fprintf(stderr, PROGNAME ":  output file exists [%s]\n",
341                       outname);
342                     fclose(wpng_info.outfile);
343                     ++error;
344                 } else if (!(wpng_info.outfile = fopen(outname, "wb"))) {
345                     fprintf(stderr, PROGNAME ":  can't open output file [%s]\n",
346                       outname);
347                     ++error;
348                 }
349             }
350         }
351         if (error) {
352             fclose(wpng_info.infile);
353             wpng_info.infile = NULL;
354             if (wpng_info.filter) {
355                 fclose(wpng_info.outfile);
356                 wpng_info.outfile = NULL;
357             }
358         }
359     }
360
361
362     /* if we had any errors, print usage and die horrible death...arrr! */
363
364     if (error) {
365         fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, APPNAME);
366         writepng_version_info();
367         fprintf(stderr, "\n"
368 "Usage:  %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n"
369 "or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n"
370          "    exp \ttransfer-function exponent (``gamma'') of the image in\n"
371          "\t\t  floating-point format (e.g., ``%.5f''); if image looks\n"
372          "\t\t  correct on given display system, image gamma is equal to\n"
373          "\t\t  inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n"
374          "\t\t  (where LUT = lookup-table exponent and CRT = CRT exponent;\n"
375          "\t\t  first varies, second is usually 2.2, all are positive)\n"
376          "    bg  \tdesired background color for alpha-channel images, in\n"
377          "\t\t  7-character hex RGB format (e.g., ``#ff7700'' for orange:\n"
378          "\t\t  same as HTML colors)\n"
379          "    -text\tprompt interactively for text info (tEXt chunks)\n"
380          "    -time\tinclude a tIME chunk (last modification time)\n"
381          "    -interlace\twrite interlaced PNG image\n"
382          "\n"
383 "pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n"
384 "unofficial and unsupported!) PAM (`P8') file.  Currently it is required\n"
385 "to have maxval == 255 (i.e., no scaling).  If pnmfile is specified, it\n"
386 "is converted to the corresponding PNG file with the same base name but a\n"
387 "``.png'' extension; files read from stdin are converted and sent to stdout.\n"
388 "The conversion is progressive (low memory usage) unless interlacing is\n"
389 "requested; in that case the whole image will be buffered in memory and\n"
390 "written in one call.\n"
391          "\n", PROGNAME, PROGNAME, default_gamma);
392         exit(1);
393     }
394
395
396     /* prepare the text buffers for libpng's use; note that even though
397      * PNG's png_text struct includes a length field, we don't have to fill
398      * it out */
399
400     if (text &&
401 #ifndef DOS_OS2_W32
402         (keybd = fdopen(fileno(stderr), "r")) != NULL &&
403 #endif
404         (textbuf = (char *)malloc((5 + 9)*75)) != NULL)
405     {
406         int i, valid, result;
407
408         fprintf(stderr,
409           "Enter text info (no more than 72 characters per line);\n");
410         fprintf(stderr, "to skip a field, hit the <Enter> key.\n");
411         /* note:  just <Enter> leaves len == 1 */
412
413         do {
414             valid = TRUE;
415             p = textbuf + TEXT_TITLE_OFFSET;
416             fprintf(stderr, "  Title: ");
417             fflush(stderr);
418             if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
419                 if (p[len-1] == '\n')
420                     p[--len] = '\0';
421                 wpng_info.title = p;
422                 wpng_info.have_text |= TEXT_TITLE;
423                 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
424                     fprintf(stderr, "    " PROGNAME " warning:  character code"
425                       " %u is %sdiscouraged by the PNG\n    specification "
426                       "[first occurrence was at character position #%d]\n",
427                       (unsigned)p[result], (p[result] == 27)? "strongly " : "",
428                       result+1);
429                     fflush(stderr);
430 #ifdef FORBID_LATIN1_CTRL
431                     wpng_info.have_text &= ~TEXT_TITLE;
432                     valid = FALSE;
433 #else
434                     if (p[result] == 27) {    /* escape character */
435                         wpng_info.have_text &= ~TEXT_TITLE;
436                         valid = FALSE;
437                     }
438 #endif
439                 }
440             }
441         } while (!valid);
442
443         do {
444             valid = TRUE;
445             p = textbuf + TEXT_AUTHOR_OFFSET;
446             fprintf(stderr, "  Author: ");
447             fflush(stderr);
448             if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
449                 if (p[len-1] == '\n')
450                     p[--len] = '\0';
451                 wpng_info.author = p;
452                 wpng_info.have_text |= TEXT_AUTHOR;
453                 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
454                     fprintf(stderr, "    " PROGNAME " warning:  character code"
455                       " %u is %sdiscouraged by the PNG\n    specification "
456                       "[first occurrence was at character position #%d]\n",
457                       (unsigned)p[result], (p[result] == 27)? "strongly " : "",
458                       result+1);
459                     fflush(stderr);
460 #ifdef FORBID_LATIN1_CTRL
461                     wpng_info.have_text &= ~TEXT_AUTHOR;
462                     valid = FALSE;
463 #else
464                     if (p[result] == 27) {    /* escape character */
465                         wpng_info.have_text &= ~TEXT_AUTHOR;
466                         valid = FALSE;
467                     }
468 #endif
469                 }
470             }
471         } while (!valid);
472
473         do {
474             valid = TRUE;
475             p = textbuf + TEXT_DESC_OFFSET;
476             fprintf(stderr, "  Description (up to 9 lines):\n");
477             for (i = 1;  i < 10;  ++i) {
478                 fprintf(stderr, "    [%d] ", i);
479                 fflush(stderr);
480                 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1)
481                     p += len;   /* now points at NULL; char before is newline */
482                 else
483                     break;
484             }
485             if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) {
486                 if (p[-1] == '\n') {
487                     p[-1] = '\0';
488                     --len;
489                 }
490                 wpng_info.desc = textbuf + TEXT_DESC_OFFSET;
491                 wpng_info.have_text |= TEXT_DESC;
492                 p = textbuf + TEXT_DESC_OFFSET;
493                 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
494                     fprintf(stderr, "    " PROGNAME " warning:  character code"
495                       " %u is %sdiscouraged by the PNG\n    specification "
496                       "[first occurrence was at character position #%d]\n",
497                       (unsigned)p[result], (p[result] == 27)? "strongly " : "",
498                       result+1);
499                     fflush(stderr);
500 #ifdef FORBID_LATIN1_CTRL
501                     wpng_info.have_text &= ~TEXT_DESC;
502                     valid = FALSE;
503 #else
504                     if (p[result] == 27) {    /* escape character */
505                         wpng_info.have_text &= ~TEXT_DESC;
506                         valid = FALSE;
507                     }
508 #endif
509                 }
510             }
511         } while (!valid);
512
513         do {
514             valid = TRUE;
515             p = textbuf + TEXT_COPY_OFFSET;
516             fprintf(stderr, "  Copyright: ");
517             fflush(stderr);
518             if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
519                 if (p[len-1] == '\n')
520                     p[--len] = '\0';
521                 wpng_info.copyright = p;
522                 wpng_info.have_text |= TEXT_COPY;
523                 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
524                     fprintf(stderr, "    " PROGNAME " warning:  character code"
525                       " %u is %sdiscouraged by the PNG\n    specification "
526                       "[first occurrence was at character position #%d]\n",
527                       (unsigned)p[result], (p[result] == 27)? "strongly " : "",
528                       result+1);
529                     fflush(stderr);
530 #ifdef FORBID_LATIN1_CTRL
531                     wpng_info.have_text &= ~TEXT_COPY;
532                     valid = FALSE;
533 #else
534                     if (p[result] == 27) {    /* escape character */
535                         wpng_info.have_text &= ~TEXT_COPY;
536                         valid = FALSE;
537                     }
538 #endif
539                 }
540             }
541         } while (!valid);
542
543         do {
544             valid = TRUE;
545             p = textbuf + TEXT_EMAIL_OFFSET;
546             fprintf(stderr, "  E-mail: ");
547             fflush(stderr);
548             if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
549                 if (p[len-1] == '\n')
550                     p[--len] = '\0';
551                 wpng_info.email = p;
552                 wpng_info.have_text |= TEXT_EMAIL;
553                 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
554                     fprintf(stderr, "    " PROGNAME " warning:  character code"
555                       " %u is %sdiscouraged by the PNG\n    specification "
556                       "[first occurrence was at character position #%d]\n",
557                       (unsigned)p[result], (p[result] == 27)? "strongly " : "",
558                       result+1);
559                     fflush(stderr);
560 #ifdef FORBID_LATIN1_CTRL
561                     wpng_info.have_text &= ~TEXT_EMAIL;
562                     valid = FALSE;
563 #else
564                     if (p[result] == 27) {    /* escape character */
565                         wpng_info.have_text &= ~TEXT_EMAIL;
566                         valid = FALSE;
567                     }
568 #endif
569                 }
570             }
571         } while (!valid);
572
573         do {
574             valid = TRUE;
575             p = textbuf + TEXT_URL_OFFSET;
576             fprintf(stderr, "  URL: ");
577             fflush(stderr);
578             if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
579                 if (p[len-1] == '\n')
580                     p[--len] = '\0';
581                 wpng_info.url = p;
582                 wpng_info.have_text |= TEXT_URL;
583                 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
584                     fprintf(stderr, "    " PROGNAME " warning:  character code"
585                       " %u is %sdiscouraged by the PNG\n    specification "
586                       "[first occurrence was at character position #%d]\n",
587                       (unsigned)p[result], (p[result] == 27)? "strongly " : "",
588                       result+1);
589                     fflush(stderr);
590 #ifdef FORBID_LATIN1_CTRL
591                     wpng_info.have_text &= ~TEXT_URL;
592                     valid = FALSE;
593 #else
594                     if (p[result] == 27) {    /* escape character */
595                         wpng_info.have_text &= ~TEXT_URL;
596                         valid = FALSE;
597                     }
598 #endif
599                 }
600             }
601         } while (!valid);
602
603 #ifndef DOS_OS2_W32
604         fclose(keybd);
605 #endif
606
607     } else if (text) {
608         fprintf(stderr, PROGNAME ":  unable to allocate memory for text\n");
609         text = FALSE;
610         wpng_info.have_text = 0;
611     }
612
613
614     /* allocate libpng stuff, initialize transformations, write pre-IDAT data */
615
616     if ((rc = writepng_init(&wpng_info)) != 0) {
617         switch (rc) {
618             case 2:
619                 fprintf(stderr, PROGNAME
620                   ":  libpng initialization problem (longjmp)\n");
621                 break;
622             case 4:
623                 fprintf(stderr, PROGNAME ":  insufficient memory\n");
624                 break;
625             case 11:
626                 fprintf(stderr, PROGNAME
627                   ":  internal logic error (unexpected PNM type)\n");
628                 break;
629             default:
630                 fprintf(stderr, PROGNAME
631                   ":  unknown writepng_init() error\n");
632                 break;
633         }
634         exit(rc);
635     }
636
637
638     /* free textbuf, since it's a completely local variable and all text info
639      * has just been written to the PNG file */
640
641     if (text && textbuf) {
642         free(textbuf);
643         textbuf = NULL;
644     }
645
646
647     /* calculate rowbytes on basis of image type; note that this becomes much
648      * more complicated if we choose to support PBM type, ASCII PNM types, or
649      * 16-bit-per-sample binary data [currently not an official NetPBM type] */
650
651     if (wpng_info.pnmtype == 5)
652         rowbytes = wpng_info.width;
653     else if (wpng_info.pnmtype == 6)
654         rowbytes = wpng_info.width * 3;
655     else /* if (wpng_info.pnmtype == 8) */
656         rowbytes = wpng_info.width * 4;
657
658
659     /* read and write the image, either in its entirety (if writing interlaced
660      * PNG) or row by row (if non-interlaced) */
661
662     fprintf(stderr, "Encoding image data...\n");
663     fflush(stderr);
664
665     if (wpng_info.interlaced) {
666         long i;
667         ulg bytes;
668         ulg image_bytes = rowbytes * wpng_info.height;   /* overflow? */
669
670         wpng_info.image_data = (uch *)malloc(image_bytes);
671         wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
672         if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
673             fprintf(stderr, PROGNAME ":  insufficient memory for image data\n");
674             writepng_cleanup(&wpng_info);
675             wpng_cleanup();
676             exit(5);
677         }
678         for (i = 0;  i < wpng_info.height;  ++i)
679             wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
680         bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
681         if (bytes != image_bytes) {
682             fprintf(stderr, PROGNAME ":  expected %lu bytes, got %lu bytes\n",
683               image_bytes, bytes);
684             fprintf(stderr, "  (continuing anyway)\n");
685         }
686         if (writepng_encode_image(&wpng_info) != 0) {
687             fprintf(stderr, PROGNAME
688               ":  libpng problem (longjmp) while writing image data\n");
689             writepng_cleanup(&wpng_info);
690             wpng_cleanup();
691             exit(2);
692         }
693
694     } else /* not interlaced:  write progressively (row by row) */ {
695         long j;
696         ulg bytes;
697
698         wpng_info.image_data = (uch *)malloc(rowbytes);
699         if (wpng_info.image_data == NULL) {
700             fprintf(stderr, PROGNAME ":  insufficient memory for row data\n");
701             writepng_cleanup(&wpng_info);
702             wpng_cleanup();
703             exit(5);
704         }
705         error = 0;
706         for (j = wpng_info.height;  j > 0L;  --j) {
707             bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
708             if (bytes != rowbytes) {
709                 fprintf(stderr, PROGNAME
710                   ":  expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
711                   bytes, wpng_info.height-j);
712                 ++error;
713                 break;
714             }
715             if (writepng_encode_row(&wpng_info) != 0) {
716                 fprintf(stderr, PROGNAME
717                   ":  libpng problem (longjmp) while writing row %ld\n",
718                   wpng_info.height-j);
719                 ++error;
720                 break;
721             }
722         }
723         if (error) {
724             writepng_cleanup(&wpng_info);
725             wpng_cleanup();
726             exit(2);
727         }
728         if (writepng_encode_finish(&wpng_info) != 0) {
729             fprintf(stderr, PROGNAME ":  error on final libpng call\n");
730             writepng_cleanup(&wpng_info);
731             wpng_cleanup();
732             exit(2);
733         }
734     }
735
736
737     /* OK, we're done (successfully):  clean up all resources and quit */
738
739     fprintf(stderr, "Done.\n");
740     fflush(stderr);
741
742     writepng_cleanup(&wpng_info);
743     wpng_cleanup();
744
745     return 0;
746 }
747
748
749
750
751
752 static int wpng_isvalid_latin1(uch *p, int len)
753 {
754     int i, result = -1;
755
756     for (i = 0;  i < len;  ++i) {
757         if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160)
758             continue;           /* character is completely OK */
759         if (result < 0 || (p[result] != 27 && p[i] == 27))
760             result = i;         /* mark location of first questionable one */
761     }                           /*  or of first escape character (bad) */
762
763     return result;
764 }
765
766
767
768
769
770 static void wpng_cleanup(void)
771 {
772     if (wpng_info.outfile) {
773         fclose(wpng_info.outfile);
774         wpng_info.outfile = NULL;
775     }
776
777     if (wpng_info.infile) {
778         fclose(wpng_info.infile);
779         wpng_info.infile = NULL;
780     }
781
782     if (wpng_info.image_data) {
783         free(wpng_info.image_data);
784         wpng_info.image_data = NULL;
785     }
786
787     if (wpng_info.row_pointers) {
788         free(wpng_info.row_pointers);
789         wpng_info.row_pointers = NULL;
790     }
791 }
792
793
794
795
796 #ifdef DOS_OS2_W32
797
798 static char *dos_kbd_gets(char *buf, int len)
799 {
800     int ch, count=0;
801
802     do {
803         buf[count++] = ch = getche();
804     } while (ch != '\r' && count < len-1);
805
806     buf[count--] = '\0';        /* terminate string */
807     if (buf[count] == '\r')     /* Enter key makes CR, so change to newline */
808         buf[count] = '\n';
809
810     fprintf(stderr, "\n");      /* Enter key does *not* cause a newline */
811     fflush(stderr);
812
813     return buf;
814 }
815
816 #endif /* DOS_OS2_W32 */