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