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