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