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
141static int  wpng_isvalid_latin1(uch *p, int len);
142static 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
150static mainprog_info wpng_info;   /* lone global */
151
152
153
154int 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
789static 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
807static 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
835static 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