Blame contrib/gregbook/wpng.c

Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   wpng - simple PNG-writing program                                 wpng.c
Packit 0ba690
Packit 0ba690
   This program converts certain NetPBM binary files (grayscale and RGB,
Packit 0ba690
   maxval = 255) to PNG.  Non-interlaced PNGs are written progressively;
Packit 0ba690
   interlaced PNGs are read and written in one memory-intensive blast.
Packit 0ba690
Packit 0ba690
   Thanks to Jean-loup Gailly for providing the necessary trick to read
Packit 0ba690
   interactive text from the keyboard while stdin is redirected.  Thanks
Packit 0ba690
   to Cosmin Truta for Cygwin fixes.
Packit 0ba690
Packit 0ba690
   NOTE:  includes provisional support for PNM type "8" (portable alphamap)
Packit 0ba690
          images, presumed to be a 32-bit interleaved RGBA format; no pro-
Packit 0ba690
          vision for possible interleaved grayscale+alpha (16-bit) format.
Packit 0ba690
          THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT!
Packit 0ba690
Packit 0ba690
   to do:
Packit 0ba690
    - delete output file if quit before calling any writepng routines
Packit 0ba690
    - process backspace with -text option under DOS/Win? (currently get ^H)
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   Changelog:
Packit 0ba690
    - 1.01:  initial public release
Packit 0ba690
    - 1.02:  modified to allow abbreviated options
Packit 0ba690
    - 1.03:  removed extraneous character from usage screen; fixed bug in
Packit 0ba690
              command-line parsing
Packit 0ba690
    - 1.04:  fixed DOS/OS2/Win32 detection, including partial Cygwin fix
Packit 0ba690
              (see http://home.att.net/~perlspinr/diffs/GregBook_cygwin.diff)
Packit 0ba690
    - 2.00:  dual-licensed (added GNU GPL)
Packit 0ba690
Packit 0ba690
        [REPORTED BUG (win32 only):  "contrib/gregbook/wpng.c - cmd line
Packit 0ba690
         dose not work!  In order to do something useful I needed to redirect
Packit 0ba690
         both input and output, with cygwin and with bcc32 as well.  Under
Packit 0ba690
         Linux, the same wpng appears to work fine.  I don't know what is
Packit 0ba690
         the problem."]
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
      Copyright (c) 1998-2007 Greg Roelofs.  All rights reserved.
Packit 0ba690
Packit 0ba690
      This software is provided "as is," without warranty of any kind,
Packit 0ba690
      express or implied.  In no event shall the author or contributors
Packit 0ba690
      be held liable for any damages arising in any way from the use of
Packit 0ba690
      this software.
Packit 0ba690
Packit 0ba690
      The contents of this file are DUAL-LICENSED.  You may modify and/or
Packit 0ba690
      redistribute this software according to the terms of one of the
Packit 0ba690
      following two licenses (at your option):
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 1 ("BSD-like with advertising clause"):
Packit 0ba690
Packit 0ba690
      Permission is granted to anyone to use this software for any purpose,
Packit 0ba690
      including commercial applications, and to alter it and redistribute
Packit 0ba690
      it freely, subject to the following restrictions:
Packit 0ba690
Packit 0ba690
      1. Redistributions of source code must retain the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions.
Packit 0ba690
      2. Redistributions in binary form must reproduce the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions in the documenta-
Packit 0ba690
         tion and/or other materials provided with the distribution.
Packit 0ba690
      3. All advertising materials mentioning features or use of this
Packit 0ba690
         software must display the following acknowledgment:
Packit 0ba690
Packit 0ba690
            This product includes software developed by Greg Roelofs
Packit 0ba690
            and contributors for the book, "PNG: The Definitive Guide,"
Packit 0ba690
            published by O'Reilly and Associates.
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 2 (GNU GPL v2 or later):
Packit 0ba690
Packit 0ba690
      This program is free software; you can redistribute it and/or modify
Packit 0ba690
      it under the terms of the GNU General Public License as published by
Packit 0ba690
      the Free Software Foundation; either version 2 of the License, or
Packit 0ba690
      (at your option) any later version.
Packit 0ba690
Packit 0ba690
      This program is distributed in the hope that it will be useful,
Packit 0ba690
      but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0ba690
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 0ba690
      GNU General Public License for more details.
Packit 0ba690
Packit 0ba690
      You should have received a copy of the GNU General Public License
Packit 0ba690
      along with this program; if not, write to the Free Software Foundation,
Packit 0ba690
      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
#define PROGNAME  "wpng"
Packit 0ba690
#define VERSION   "2.00 of 2 June 2007"
Packit 0ba690
#define APPNAME   "Simple PGM/PPM/PAM to PNG Converter"
Packit 0ba690
Packit 0ba690
#if defined(__MSDOS__) || defined(__OS2__)
Packit 0ba690
#  define DOS_OS2_W32
Packit 0ba690
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
Packit 0ba690
#  ifndef __GNUC__   /* treat Win32 native ports of gcc as Unix environments */
Packit 0ba690
#    define DOS_OS2_W32
Packit 0ba690
#  endif
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
#include <stdio.h>
Packit 0ba690
#include <stdlib.h>
Packit 0ba690
#include <string.h>
Packit 0ba690
#include <setjmp.h>     /* for jmpbuf declaration in writepng.h */
Packit 0ba690
#include <time.h>
Packit 0ba690
Packit 0ba690
#ifdef DOS_OS2_W32
Packit 0ba690
#  include <io.h>       /* for isatty(), setmode() prototypes */
Packit 0ba690
#  include <fcntl.h>    /* O_BINARY for fdopen() without text translation */
Packit 0ba690
#  ifdef __EMX__
Packit 0ba690
#    ifndef getch
Packit 0ba690
#      define getch() _read_kbd(0, 1, 0)    /* need getche() */
Packit 0ba690
#    endif
Packit 0ba690
#  else /* !__EMX__ */
Packit 0ba690
#    ifdef __GO32__
Packit 0ba690
#      include <pc.h>
Packit 0ba690
#      define getch() getkey()  /* GRR:  need getche() */
Packit 0ba690
#    else
Packit 0ba690
#      include <conio.h>        /* for getche() console input */
Packit 0ba690
#    endif
Packit 0ba690
#  endif /* ?__EMX__ */
Packit 0ba690
#  define FGETS(buf,len,stream)  dos_kbd_gets(buf,len)
Packit 0ba690
#else
Packit 0ba690
#  include <unistd.h>           /* for isatty() prototype */
Packit 0ba690
#  define FGETS fgets
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
/* #define DEBUG  :  this enables the Trace() macros */
Packit 0ba690
Packit 0ba690
/* #define FORBID_LATIN1_CTRL  :  this requires the user to re-enter any
Packit 0ba690
   text that includes control characters discouraged by the PNG spec; text
Packit 0ba690
   that includes an escape character (27) must be re-entered regardless */
Packit 0ba690
Packit 0ba690
#include "writepng.h"   /* typedefs, common macros, writepng prototypes */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* local prototypes */
Packit 0ba690
Packit 0ba690
static int  wpng_isvalid_latin1(uch *p, int len);
Packit 0ba690
static void wpng_cleanup(void);
Packit 0ba690
Packit 0ba690
#ifdef DOS_OS2_W32
Packit 0ba690
   static char *dos_kbd_gets(char *buf, int len);
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static mainprog_info wpng_info;   /* lone global */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
int main(int argc, char **argv)
Packit 0ba690
{
Packit 0ba690
#ifndef DOS_OS2_W32
Packit 0ba690
    FILE *keybd;
Packit 0ba690
#endif
Packit 0ba690
#ifdef sgi
Packit 0ba690
    FILE *tmpfile;      /* or we could just use keybd, since no overlap */
Packit 0ba690
    char tmpline[80];
Packit 0ba690
#endif
Packit 0ba690
    char *inname = NULL, outname[256];
Packit 0ba690
    char *p, pnmchar, pnmline[256];
Packit 0ba690
    char *bgstr, *textbuf = NULL;
Packit 0ba690
    ulg rowbytes;
Packit 0ba690
    int rc, len = 0;
Packit 0ba690
    int error = 0;
Packit 0ba690
    int text = FALSE;
Packit 0ba690
    int maxval;
Packit 0ba690
    double LUT_exponent;                /* just the lookup table */
Packit 0ba690
    double CRT_exponent = 2.2;          /* just the monitor */
Packit 0ba690
    double default_display_exponent;    /* whole display system */
Packit 0ba690
    double default_gamma = 0.0;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    wpng_info.infile = NULL;
Packit 0ba690
    wpng_info.outfile = NULL;
Packit 0ba690
    wpng_info.image_data = NULL;
Packit 0ba690
    wpng_info.row_pointers = NULL;
Packit 0ba690
    wpng_info.filter = FALSE;
Packit 0ba690
    wpng_info.interlaced = FALSE;
Packit 0ba690
    wpng_info.have_bg = FALSE;
Packit 0ba690
    wpng_info.have_time = FALSE;
Packit 0ba690
    wpng_info.have_text = 0;
Packit 0ba690
    wpng_info.gamma = 0.0;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* First get the default value for our display-system exponent, i.e.,
Packit 0ba690
     * the product of the CRT exponent and the exponent corresponding to
Packit 0ba690
     * the frame-buffer's lookup table (LUT), if any.  If the PNM image
Packit 0ba690
     * looks correct on the user's display system, its file gamma is the
Packit 0ba690
     * inverse of this value.  (Note that this is not an exhaustive list
Packit 0ba690
     * of LUT values--e.g., OpenStep has a lot of weird ones--but it should
Packit 0ba690
     * cover 99% of the current possibilities.  This section must ensure
Packit 0ba690
     * that default_display_exponent is positive.) */
Packit 0ba690
Packit 0ba690
#if defined(NeXT)
Packit 0ba690
    /* third-party utilities can modify the default LUT exponent */
Packit 0ba690
    LUT_exponent = 1.0 / 2.2;
Packit 0ba690
    /*
Packit 0ba690
    if (some_next_function_that_returns_gamma(&next_gamma))
Packit 0ba690
        LUT_exponent = 1.0 / next_gamma;
Packit 0ba690
     */
Packit 0ba690
#elif defined(sgi)
Packit 0ba690
    LUT_exponent = 1.0 / 1.7;
Packit 0ba690
    /* there doesn't seem to be any documented function to
Packit 0ba690
     * get the "gamma" value, so we do it the hard way */
Packit 0ba690
    tmpfile = fopen("/etc/config/system.glGammaVal", "r");
Packit 0ba690
    if (tmpfile) {
Packit 0ba690
        double sgi_gamma;
Packit 0ba690
Packit 0ba690
        fgets(tmpline, 80, tmpfile);
Packit 0ba690
        fclose(tmpfile);
Packit 0ba690
        sgi_gamma = atof(tmpline);
Packit 0ba690
        if (sgi_gamma > 0.0)
Packit 0ba690
            LUT_exponent = 1.0 / sgi_gamma;
Packit 0ba690
    }
Packit 0ba690
#elif defined(Macintosh)
Packit 0ba690
    LUT_exponent = 1.8 / 2.61;
Packit 0ba690
    /*
Packit 0ba690
    if (some_mac_function_that_returns_gamma(&mac_gamma))
Packit 0ba690
        LUT_exponent = mac_gamma / 2.61;
Packit 0ba690
     */
Packit 0ba690
#else
Packit 0ba690
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
Packit 0ba690
    default_display_exponent = LUT_exponent * CRT_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* If the user has set the SCREEN_GAMMA environment variable as suggested
Packit 0ba690
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
Packit 0ba690
     * use the default value we just calculated.  Either way, the user may
Packit 0ba690
     * override this via a command-line option. */
Packit 0ba690
Packit 0ba690
    if ((p = getenv("SCREEN_GAMMA")) != NULL) {
Packit 0ba690
        double exponent = atof(p);
Packit 0ba690
Packit 0ba690
        if (exponent > 0.0)
Packit 0ba690
            default_gamma = 1.0 / exponent;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (default_gamma == 0.0)
Packit 0ba690
        default_gamma = 1.0 / default_display_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Now parse the command line for options and the PNM filename. */
Packit 0ba690
Packit 0ba690
    while (*++argv && !error) {
Packit 0ba690
        if (!strncmp(*argv, "-i", 2)) {
Packit 0ba690
            wpng_info.interlaced = TRUE;
Packit 0ba690
        } else if (!strncmp(*argv, "-time", 3)) {
Packit 0ba690
            wpng_info.modtime = time(NULL);
Packit 0ba690
            wpng_info.have_time = TRUE;
Packit 0ba690
        } else if (!strncmp(*argv, "-text", 3)) {
Packit 0ba690
            text = TRUE;
Packit 0ba690
        } else if (!strncmp(*argv, "-gamma", 2)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                wpng_info.gamma = atof(*argv);
Packit 0ba690
                if (wpng_info.gamma <= 0.0)
Packit 0ba690
                    ++error;
Packit 0ba690
                else if (wpng_info.gamma > 1.01)
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      " warning:  file gammas are usually less than 1.0\n");
Packit 0ba690
            }
Packit 0ba690
        } else if (!strncmp(*argv, "-bgcolor", 4)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                bgstr = *argv;
Packit 0ba690
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
Packit 0ba690
                    ++error;
Packit 0ba690
                else {
Packit 0ba690
                    unsigned r, g, b;  /* this way quiets compiler warnings */
Packit 0ba690
Packit 0ba690
                    sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
Packit 0ba690
                    wpng_info.bg_red   = (uch)r;
Packit 0ba690
                    wpng_info.bg_green = (uch)g;
Packit 0ba690
                    wpng_info.bg_blue  = (uch)b;
Packit 0ba690
                    wpng_info.have_bg = TRUE;
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } else {
Packit 0ba690
            if (**argv != '-') {
Packit 0ba690
                inname = *argv;
Packit 0ba690
                if (argv[1])   /* shouldn't be any more args after filename */
Packit 0ba690
                    ++error;
Packit 0ba690
            } else
Packit 0ba690
                ++error;   /* not expecting any other options */
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* open the input and output files, or register an error and abort */
Packit 0ba690
Packit 0ba690
    if (!inname) {
Packit 0ba690
        if (isatty(0)) {
Packit 0ba690
            fprintf(stderr, PROGNAME
Packit 0ba690
              ":  must give input filename or provide image data via stdin\n");
Packit 0ba690
            ++error;
Packit 0ba690
        } else {
Packit 0ba690
#ifdef DOS_OS2_W32
Packit 0ba690
            /* some buggy C libraries require BOTH setmode() and fdopen(bin) */
Packit 0ba690
            setmode(fileno(stdin), O_BINARY);
Packit 0ba690
            setmode(fileno(stdout), O_BINARY);
Packit 0ba690
#endif
Packit 0ba690
            if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) {
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  unable to reopen stdin in binary mode\n");
Packit 0ba690
                ++error;
Packit 0ba690
            } else
Packit 0ba690
            if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) {
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  unable to reopen stdout in binary mode\n");
Packit 0ba690
                fclose(wpng_info.infile);
Packit 0ba690
                ++error;
Packit 0ba690
            } else
Packit 0ba690
                wpng_info.filter = TRUE;
Packit 0ba690
        }
Packit 0ba690
    } else if ((len = strlen(inname)) > 250) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  input filename is too long [%d chars]\n",
Packit 0ba690
          len);
Packit 0ba690
        ++error;
Packit 0ba690
    } else if (!(wpng_info.infile = fopen(inname, "rb"))) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  can't open input file [%s]\n", inname);
Packit 0ba690
        ++error;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (!error) {
Packit 0ba690
        fgets(pnmline, 256, wpng_info.infile);
Packit 0ba690
        if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' &&
Packit 0ba690
            pnmchar != '6' && pnmchar != '8'))
Packit 0ba690
        {
Packit 0ba690
            fprintf(stderr, PROGNAME
Packit 0ba690
              ":  input file [%s] is not a binary PGM, PPM or PAM file\n",
Packit 0ba690
              inname);
Packit 0ba690
            ++error;
Packit 0ba690
        } else {
Packit 0ba690
            wpng_info.pnmtype = (int)(pnmchar - '0');
Packit 0ba690
            if (wpng_info.pnmtype != 8)
Packit 0ba690
                wpng_info.have_bg = FALSE;  /* no need for bg if opaque */
Packit 0ba690
            do {
Packit 0ba690
                fgets(pnmline, 256, wpng_info.infile);  /* lose any comments */
Packit 0ba690
            } while (pnmline[0] == '#');
Packit 0ba690
            sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height);
Packit 0ba690
            do {
Packit 0ba690
                fgets(pnmline, 256, wpng_info.infile);  /* more comment lines */
Packit 0ba690
            } while (pnmline[0] == '#');
Packit 0ba690
            sscanf(pnmline, "%d", &maxval);
Packit 0ba690
            if (wpng_info.width <= 0L || wpng_info.height <= 0L ||
Packit 0ba690
                maxval != 255)
Packit 0ba690
            {
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  only positive width/height, maxval == 255 allowed \n");
Packit 0ba690
                ++error;
Packit 0ba690
            }
Packit 0ba690
            wpng_info.sample_depth = 8;  /* <==> maxval 255 */
Packit 0ba690
Packit 0ba690
            if (!wpng_info.filter) {
Packit 0ba690
                /* make outname from inname */
Packit 0ba690
                if ((p = strrchr(inname, '.')) == NULL ||
Packit 0ba690
                    (p - inname) != (len - 4))
Packit 0ba690
                {
Packit 0ba690
                    strcpy(outname, inname);
Packit 0ba690
                    strcpy(outname+len, ".png");
Packit 0ba690
                } else {
Packit 0ba690
                    len -= 4;
Packit 0ba690
                    strncpy(outname, inname, len);
Packit 0ba690
                    strcpy(outname+len, ".png");
Packit 0ba690
                }
Packit 0ba690
                /* check if outname already exists; if not, open */
Packit 0ba690
                if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) {
Packit 0ba690
                    fprintf(stderr, PROGNAME ":  output file exists [%s]\n",
Packit 0ba690
                      outname);
Packit 0ba690
                    fclose(wpng_info.outfile);
Packit 0ba690
                    ++error;
Packit 0ba690
                } else if (!(wpng_info.outfile = fopen(outname, "wb"))) {
Packit 0ba690
                    fprintf(stderr, PROGNAME ":  can't open output file [%s]\n",
Packit 0ba690
                      outname);
Packit 0ba690
                    ++error;
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
        if (error) {
Packit 0ba690
            fclose(wpng_info.infile);
Packit 0ba690
            wpng_info.infile = NULL;
Packit 0ba690
            if (wpng_info.filter) {
Packit 0ba690
                fclose(wpng_info.outfile);
Packit 0ba690
                wpng_info.outfile = NULL;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* if we had any errors, print usage and die horrible death...arrr! */
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, APPNAME);
Packit 0ba690
        writepng_version_info();
Packit 0ba690
        fprintf(stderr, "\n"
Packit 0ba690
"Usage:  %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n"
Packit 0ba690
"or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n"
Packit 0ba690
         "    exp \ttransfer-function exponent (``gamma'') of the image in\n"
Packit 0ba690
         "\t\t  floating-point format (e.g., ``%.5f''); if image looks\n"
Packit 0ba690
         "\t\t  correct on given display system, image gamma is equal to\n"
Packit 0ba690
         "\t\t  inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n"
Packit 0ba690
         "\t\t  (where LUT = lookup-table exponent and CRT = CRT exponent;\n"
Packit 0ba690
         "\t\t  first varies, second is usually 2.2, all are positive)\n"
Packit 0ba690
         "    bg  \tdesired background color for alpha-channel images, in\n"
Packit 0ba690
         "\t\t  7-character hex RGB format (e.g., ``#ff7700'' for orange:\n"
Packit 0ba690
         "\t\t  same as HTML colors)\n"
Packit 0ba690
         "    -text\tprompt interactively for text info (tEXt chunks)\n"
Packit 0ba690
         "    -time\tinclude a tIME chunk (last modification time)\n"
Packit 0ba690
         "    -interlace\twrite interlaced PNG image\n"
Packit 0ba690
         "\n"
Packit 0ba690
"pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n"
Packit 0ba690
"unofficial and unsupported!) PAM (`P8') file.  Currently it is required\n"
Packit 0ba690
"to have maxval == 255 (i.e., no scaling).  If pnmfile is specified, it\n"
Packit 0ba690
"is converted to the corresponding PNG file with the same base name but a\n"
Packit 0ba690
"``.png'' extension; files read from stdin are converted and sent to stdout.\n"
Packit 0ba690
"The conversion is progressive (low memory usage) unless interlacing is\n"
Packit 0ba690
"requested; in that case the whole image will be buffered in memory and\n"
Packit 0ba690
"written in one call.\n"
Packit 0ba690
         "\n", PROGNAME, PROGNAME, default_gamma);
Packit 0ba690
        exit(1);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* prepare the text buffers for libpng's use; note that even though
Packit 0ba690
     * PNG's png_text struct includes a length field, we don't have to fill
Packit 0ba690
     * it out */
Packit 0ba690
Packit 0ba690
    if (text &&
Packit 0ba690
#ifndef DOS_OS2_W32
Packit 0ba690
        (keybd = fdopen(fileno(stderr), "r")) != NULL &&
Packit 0ba690
#endif
Packit 0ba690
        (textbuf = (char *)malloc((5 + 9)*75)) != NULL)
Packit 0ba690
    {
Packit 0ba690
        int i, valid, result;
Packit 0ba690
Packit 0ba690
        fprintf(stderr,
Packit 0ba690
          "Enter text info (no more than 72 characters per line);\n");
Packit 0ba690
        fprintf(stderr, "to skip a field, hit the <Enter> key.\n");
Packit 0ba690
        /* note:  just <Enter> leaves len == 1 */
Packit 0ba690
Packit 0ba690
        do {
Packit 0ba690
            valid = TRUE;
Packit 0ba690
            p = textbuf + TEXT_TITLE_OFFSET;
Packit 0ba690
            fprintf(stderr, "  Title: ");
Packit 0ba690
            fflush(stderr);
Packit 0ba690
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
Packit 0ba690
                if (p[len-1] == '\n')
Packit 0ba690
                    p[--len] = '\0';
Packit 0ba690
                wpng_info.title = p;
Packit 0ba690
                wpng_info.have_text |= TEXT_TITLE;
Packit 0ba690
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
Packit 0ba690
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
Packit 0ba690
                      " %u is %sdiscouraged by the PNG\n    specification "
Packit 0ba690
                      "[first occurrence was at character position #%d]\n",
Packit 0ba690
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
Packit 0ba690
                      result+1);
Packit 0ba690
                    fflush(stderr);
Packit 0ba690
#ifdef FORBID_LATIN1_CTRL
Packit 0ba690
                    wpng_info.have_text &= ~TEXT_TITLE;
Packit 0ba690
                    valid = FALSE;
Packit 0ba690
#else
Packit 0ba690
                    if (p[result] == 27) {    /* escape character */
Packit 0ba690
                        wpng_info.have_text &= ~TEXT_TITLE;
Packit 0ba690
                        valid = FALSE;
Packit 0ba690
                    }
Packit 0ba690
#endif
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } while (!valid);
Packit 0ba690
Packit 0ba690
        do {
Packit 0ba690
            valid = TRUE;
Packit 0ba690
            p = textbuf + TEXT_AUTHOR_OFFSET;
Packit 0ba690
            fprintf(stderr, "  Author: ");
Packit 0ba690
            fflush(stderr);
Packit 0ba690
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
Packit 0ba690
                if (p[len-1] == '\n')
Packit 0ba690
                    p[--len] = '\0';
Packit 0ba690
                wpng_info.author = p;
Packit 0ba690
                wpng_info.have_text |= TEXT_AUTHOR;
Packit 0ba690
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
Packit 0ba690
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
Packit 0ba690
                      " %u is %sdiscouraged by the PNG\n    specification "
Packit 0ba690
                      "[first occurrence was at character position #%d]\n",
Packit 0ba690
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
Packit 0ba690
                      result+1);
Packit 0ba690
                    fflush(stderr);
Packit 0ba690
#ifdef FORBID_LATIN1_CTRL
Packit 0ba690
                    wpng_info.have_text &= ~TEXT_AUTHOR;
Packit 0ba690
                    valid = FALSE;
Packit 0ba690
#else
Packit 0ba690
                    if (p[result] == 27) {    /* escape character */
Packit 0ba690
                        wpng_info.have_text &= ~TEXT_AUTHOR;
Packit 0ba690
                        valid = FALSE;
Packit 0ba690
                    }
Packit 0ba690
#endif
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } while (!valid);
Packit 0ba690
Packit 0ba690
        do {
Packit 0ba690
            valid = TRUE;
Packit 0ba690
            p = textbuf + TEXT_DESC_OFFSET;
Packit 0ba690
            fprintf(stderr, "  Description (up to 9 lines):\n");
Packit 0ba690
            for (i = 1;  i < 10;  ++i) {
Packit 0ba690
                fprintf(stderr, "    [%d] ", i);
Packit 0ba690
                fflush(stderr);
Packit 0ba690
                if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1)
Packit 0ba690
                    p += len;   /* now points at NULL; char before is newline */
Packit 0ba690
                else
Packit 0ba690
                    break;
Packit 0ba690
            }
Packit 0ba690
            if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) {
Packit 0ba690
                if (p[-1] == '\n') {
Packit 0ba690
                    p[-1] = '\0';
Packit 0ba690
                    --len;
Packit 0ba690
                }
Packit 0ba690
                wpng_info.desc = textbuf + TEXT_DESC_OFFSET;
Packit 0ba690
                wpng_info.have_text |= TEXT_DESC;
Packit 0ba690
                p = textbuf + TEXT_DESC_OFFSET;
Packit 0ba690
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
Packit 0ba690
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
Packit 0ba690
                      " %u is %sdiscouraged by the PNG\n    specification "
Packit 0ba690
                      "[first occurrence was at character position #%d]\n",
Packit 0ba690
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
Packit 0ba690
                      result+1);
Packit 0ba690
                    fflush(stderr);
Packit 0ba690
#ifdef FORBID_LATIN1_CTRL
Packit 0ba690
                    wpng_info.have_text &= ~TEXT_DESC;
Packit 0ba690
                    valid = FALSE;
Packit 0ba690
#else
Packit 0ba690
                    if (p[result] == 27) {    /* escape character */
Packit 0ba690
                        wpng_info.have_text &= ~TEXT_DESC;
Packit 0ba690
                        valid = FALSE;
Packit 0ba690
                    }
Packit 0ba690
#endif
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } while (!valid);
Packit 0ba690
Packit 0ba690
        do {
Packit 0ba690
            valid = TRUE;
Packit 0ba690
            p = textbuf + TEXT_COPY_OFFSET;
Packit 0ba690
            fprintf(stderr, "  Copyright: ");
Packit 0ba690
            fflush(stderr);
Packit 0ba690
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
Packit 0ba690
                if (p[len-1] == '\n')
Packit 0ba690
                    p[--len] = '\0';
Packit 0ba690
                wpng_info.copyright = p;
Packit 0ba690
                wpng_info.have_text |= TEXT_COPY;
Packit 0ba690
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
Packit 0ba690
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
Packit 0ba690
                      " %u is %sdiscouraged by the PNG\n    specification "
Packit 0ba690
                      "[first occurrence was at character position #%d]\n",
Packit 0ba690
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
Packit 0ba690
                      result+1);
Packit 0ba690
                    fflush(stderr);
Packit 0ba690
#ifdef FORBID_LATIN1_CTRL
Packit 0ba690
                    wpng_info.have_text &= ~TEXT_COPY;
Packit 0ba690
                    valid = FALSE;
Packit 0ba690
#else
Packit 0ba690
                    if (p[result] == 27) {    /* escape character */
Packit 0ba690
                        wpng_info.have_text &= ~TEXT_COPY;
Packit 0ba690
                        valid = FALSE;
Packit 0ba690
                    }
Packit 0ba690
#endif
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } while (!valid);
Packit 0ba690
Packit 0ba690
        do {
Packit 0ba690
            valid = TRUE;
Packit 0ba690
            p = textbuf + TEXT_EMAIL_OFFSET;
Packit 0ba690
            fprintf(stderr, "  E-mail: ");
Packit 0ba690
            fflush(stderr);
Packit 0ba690
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
Packit 0ba690
                if (p[len-1] == '\n')
Packit 0ba690
                    p[--len] = '\0';
Packit 0ba690
                wpng_info.email = p;
Packit 0ba690
                wpng_info.have_text |= TEXT_EMAIL;
Packit 0ba690
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
Packit 0ba690
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
Packit 0ba690
                      " %u is %sdiscouraged by the PNG\n    specification "
Packit 0ba690
                      "[first occurrence was at character position #%d]\n",
Packit 0ba690
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
Packit 0ba690
                      result+1);
Packit 0ba690
                    fflush(stderr);
Packit 0ba690
#ifdef FORBID_LATIN1_CTRL
Packit 0ba690
                    wpng_info.have_text &= ~TEXT_EMAIL;
Packit 0ba690
                    valid = FALSE;
Packit 0ba690
#else
Packit 0ba690
                    if (p[result] == 27) {    /* escape character */
Packit 0ba690
                        wpng_info.have_text &= ~TEXT_EMAIL;
Packit 0ba690
                        valid = FALSE;
Packit 0ba690
                    }
Packit 0ba690
#endif
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } while (!valid);
Packit 0ba690
Packit 0ba690
        do {
Packit 0ba690
            valid = TRUE;
Packit 0ba690
            p = textbuf + TEXT_URL_OFFSET;
Packit 0ba690
            fprintf(stderr, "  URL: ");
Packit 0ba690
            fflush(stderr);
Packit 0ba690
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
Packit 0ba690
                if (p[len-1] == '\n')
Packit 0ba690
                    p[--len] = '\0';
Packit 0ba690
                wpng_info.url = p;
Packit 0ba690
                wpng_info.have_text |= TEXT_URL;
Packit 0ba690
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
Packit 0ba690
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
Packit 0ba690
                      " %u is %sdiscouraged by the PNG\n    specification "
Packit 0ba690
                      "[first occurrence was at character position #%d]\n",
Packit 0ba690
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
Packit 0ba690
                      result+1);
Packit 0ba690
                    fflush(stderr);
Packit 0ba690
#ifdef FORBID_LATIN1_CTRL
Packit 0ba690
                    wpng_info.have_text &= ~TEXT_URL;
Packit 0ba690
                    valid = FALSE;
Packit 0ba690
#else
Packit 0ba690
                    if (p[result] == 27) {    /* escape character */
Packit 0ba690
                        wpng_info.have_text &= ~TEXT_URL;
Packit 0ba690
                        valid = FALSE;
Packit 0ba690
                    }
Packit 0ba690
#endif
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
        } while (!valid);
Packit 0ba690
Packit 0ba690
#ifndef DOS_OS2_W32
Packit 0ba690
        fclose(keybd);
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
    } else if (text) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  unable to allocate memory for text\n");
Packit 0ba690
        text = FALSE;
Packit 0ba690
        wpng_info.have_text = 0;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* allocate libpng stuff, initialize transformations, write pre-IDAT data */
Packit 0ba690
Packit 0ba690
    if ((rc = writepng_init(&wpng_info)) != 0) {
Packit 0ba690
        switch (rc) {
Packit 0ba690
            case 2:
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  libpng initialization problem (longjmp)\n");
Packit 0ba690
                break;
Packit 0ba690
            case 4:
Packit 0ba690
                fprintf(stderr, PROGNAME ":  insufficient memory\n");
Packit 0ba690
                break;
Packit 0ba690
            case 11:
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  internal logic error (unexpected PNM type)\n");
Packit 0ba690
                break;
Packit 0ba690
            default:
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  unknown writepng_init() error\n");
Packit 0ba690
                break;
Packit 0ba690
        }
Packit 0ba690
        exit(rc);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* free textbuf, since it's a completely local variable and all text info
Packit 0ba690
     * has just been written to the PNG file */
Packit 0ba690
Packit 0ba690
    if (text && textbuf) {
Packit 0ba690
        free(textbuf);
Packit 0ba690
        textbuf = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* calculate rowbytes on basis of image type; note that this becomes much
Packit 0ba690
     * more complicated if we choose to support PBM type, ASCII PNM types, or
Packit 0ba690
     * 16-bit-per-sample binary data [currently not an official NetPBM type] */
Packit 0ba690
Packit 0ba690
    if (wpng_info.pnmtype == 5)
Packit 0ba690
        rowbytes = wpng_info.width;
Packit 0ba690
    else if (wpng_info.pnmtype == 6)
Packit 0ba690
        rowbytes = wpng_info.width * 3;
Packit 0ba690
    else /* if (wpng_info.pnmtype == 8) */
Packit 0ba690
        rowbytes = wpng_info.width * 4;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* read and write the image, either in its entirety (if writing interlaced
Packit 0ba690
     * PNG) or row by row (if non-interlaced) */
Packit 0ba690
Packit 0ba690
    fprintf(stderr, "Encoding image data...\n");
Packit 0ba690
    fflush(stderr);
Packit 0ba690
Packit 0ba690
    if (wpng_info.interlaced) {
Packit 0ba690
        long i;
Packit 0ba690
        ulg bytes;
Packit 0ba690
        ulg image_bytes = rowbytes * wpng_info.height;   /* overflow? */
Packit 0ba690
Packit 0ba690
        wpng_info.image_data = (uch *)malloc(image_bytes);
Packit 0ba690
        wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
Packit 0ba690
        if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
Packit 0ba690
            fprintf(stderr, PROGNAME ":  insufficient memory for image data\n");
Packit 0ba690
            writepng_cleanup(&wpng_info);
Packit 0ba690
            wpng_cleanup();
Packit 0ba690
            exit(5);
Packit 0ba690
        }
Packit 0ba690
        for (i = 0;  i < wpng_info.height;  ++i)
Packit 0ba690
            wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
Packit 0ba690
        bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
Packit 0ba690
        if (bytes != image_bytes) {
Packit 0ba690
            fprintf(stderr, PROGNAME ":  expected %lu bytes, got %lu bytes\n",
Packit 0ba690
              image_bytes, bytes);
Packit 0ba690
            fprintf(stderr, "  (continuing anyway)\n");
Packit 0ba690
        }
Packit 0ba690
        if (writepng_encode_image(&wpng_info) != 0) {
Packit 0ba690
            fprintf(stderr, PROGNAME
Packit 0ba690
              ":  libpng problem (longjmp) while writing image data\n");
Packit 0ba690
            writepng_cleanup(&wpng_info);
Packit 0ba690
            wpng_cleanup();
Packit 0ba690
            exit(2);
Packit 0ba690
        }
Packit 0ba690
Packit 0ba690
    } else /* not interlaced:  write progressively (row by row) */ {
Packit 0ba690
        long j;
Packit 0ba690
        ulg bytes;
Packit 0ba690
Packit 0ba690
        wpng_info.image_data = (uch *)malloc(rowbytes);
Packit 0ba690
        if (wpng_info.image_data == NULL) {
Packit 0ba690
            fprintf(stderr, PROGNAME ":  insufficient memory for row data\n");
Packit 0ba690
            writepng_cleanup(&wpng_info);
Packit 0ba690
            wpng_cleanup();
Packit 0ba690
            exit(5);
Packit 0ba690
        }
Packit 0ba690
        error = 0;
Packit 0ba690
        for (j = wpng_info.height;  j > 0L;  --j) {
Packit 0ba690
            bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
Packit 0ba690
            if (bytes != rowbytes) {
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
Packit 0ba690
                  bytes, wpng_info.height-j);
Packit 0ba690
                ++error;
Packit 0ba690
                break;
Packit 0ba690
            }
Packit 0ba690
            if (writepng_encode_row(&wpng_info) != 0) {
Packit 0ba690
                fprintf(stderr, PROGNAME
Packit 0ba690
                  ":  libpng problem (longjmp) while writing row %ld\n",
Packit 0ba690
                  wpng_info.height-j);
Packit 0ba690
                ++error;
Packit 0ba690
                break;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
        if (error) {
Packit 0ba690
            writepng_cleanup(&wpng_info);
Packit 0ba690
            wpng_cleanup();
Packit 0ba690
            exit(2);
Packit 0ba690
        }
Packit 0ba690
        if (writepng_encode_finish(&wpng_info) != 0) {
Packit 0ba690
            fprintf(stderr, PROGNAME ":  error on final libpng call\n");
Packit 0ba690
            writepng_cleanup(&wpng_info);
Packit 0ba690
            wpng_cleanup();
Packit 0ba690
            exit(2);
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* OK, we're done (successfully):  clean up all resources and quit */
Packit 0ba690
Packit 0ba690
    fprintf(stderr, "Done.\n");
Packit 0ba690
    fflush(stderr);
Packit 0ba690
Packit 0ba690
    writepng_cleanup(&wpng_info);
Packit 0ba690
    wpng_cleanup();
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int wpng_isvalid_latin1(uch *p, int len)
Packit 0ba690
{
Packit 0ba690
    int i, result = -1;
Packit 0ba690
Packit 0ba690
    for (i = 0;  i < len;  ++i) {
Packit 0ba690
        if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160)
Packit 0ba690
            continue;           /* character is completely OK */
Packit 0ba690
        if (result < 0 || (p[result] != 27 && p[i] == 27))
Packit 0ba690
            result = i;         /* mark location of first questionable one */
Packit 0ba690
    }                           /*  or of first escape character (bad) */
Packit 0ba690
Packit 0ba690
    return result;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void wpng_cleanup(void)
Packit 0ba690
{
Packit 0ba690
    if (wpng_info.outfile) {
Packit 0ba690
        fclose(wpng_info.outfile);
Packit 0ba690
        wpng_info.outfile = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (wpng_info.infile) {
Packit 0ba690
        fclose(wpng_info.infile);
Packit 0ba690
        wpng_info.infile = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (wpng_info.image_data) {
Packit 0ba690
        free(wpng_info.image_data);
Packit 0ba690
        wpng_info.image_data = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (wpng_info.row_pointers) {
Packit 0ba690
        free(wpng_info.row_pointers);
Packit 0ba690
        wpng_info.row_pointers = NULL;
Packit 0ba690
    }
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
#ifdef DOS_OS2_W32
Packit 0ba690
Packit 0ba690
static char *dos_kbd_gets(char *buf, int len)
Packit 0ba690
{
Packit 0ba690
    int ch, count=0;
Packit 0ba690
Packit 0ba690
    do {
Packit 0ba690
        buf[count++] = ch = getche();
Packit 0ba690
    } while (ch != '\r' && count < len-1);
Packit 0ba690
Packit 0ba690
    buf[count--] = '\0';        /* terminate string */
Packit 0ba690
    if (buf[count] == '\r')     /* Enter key makes CR, so change to newline */
Packit 0ba690
        buf[count] = '\n';
Packit 0ba690
Packit 0ba690
    fprintf(stderr, "\n");      /* Enter key does *not* cause a newline */
Packit 0ba690
    fflush(stderr);
Packit 0ba690
Packit 0ba690
    return buf;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
#endif /* DOS_OS2_W32 */