all repos — mgba @ 9de8f084ba55460b02d300c1dd8b8e6c56f691d5

mGBA Game Boy Advance Emulator

src/third-party/libpng/contrib/gregbook/wpng.c (view raw)

  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
142static int  wpng_isvalid_latin1(uch *p, int len);
143static 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
151static mainprog_info wpng_info;   /* lone global */
152
153
154
155int 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
801static 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
819static 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
847static 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 */