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