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