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