1 /* libpgm1.c - pgm utility library part 1
2 **
3 ** Copyright (C) 1989 by Jef Poskanzer.
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11 */
12 
13 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
14    offset stuff.
15 */
16 #define _FILE_OFFSET_BITS 64
17 #define _LARGE_FILES
18 
19 #include <string.h>
20 #include <stdio.h>
21 #include <errno.h>
22 
23 #include "netpbm/mallocvar.h"
24 #include "netpbm/nstring.h"
25 
26 #include "pgm.h"
27 #include "libpgm.h"
28 #include "pbm.h"
29 #include "libpbm.h"
30 #include "pam.h"
31 #include "libpam.h"
32 #include "fileio.h"
33 
34 
35 gray *
pgm_allocrow(unsigned int const cols)36 pgm_allocrow(unsigned int const cols) {
37 
38     gray * grayrow;
39 
40     MALLOCARRAY(grayrow, cols);
41 
42     if (grayrow == NULL)
43         pm_error("Unable to allocate space for a %u-column gray row", cols);
44 
45     return grayrow;
46 }
47 
48 
49 
50 void
pgm_init(int * const argcP,char ** const argv)51 pgm_init(int *   const argcP,
52          char ** const argv) {
53 
54     pbm_init(argcP, argv);
55 }
56 
57 
58 
59 void
pgm_nextimage(FILE * const file,int * const eofP)60 pgm_nextimage(FILE * const file,
61               int *  const eofP) {
62     pm_nextimage(file, eofP);
63 }
64 
65 
66 
67 void
pgm_readpgminitrest(FILE * const fileP,int * const colsP,int * const rowsP,gray * const maxvalP)68 pgm_readpgminitrest(FILE * const fileP,
69                     int *  const colsP,
70                     int *  const rowsP,
71                     gray * const maxvalP) {
72 
73     gray maxval;
74 
75     /* Read size. */
76     *colsP = (int)pm_getuint(fileP);
77     *rowsP = (int)pm_getuint(fileP);
78 
79     /* Read maxval. */
80     maxval = pm_getuint(fileP);
81     if (maxval > PGM_OVERALLMAXVAL)
82         pm_error("maxval of input image (%u) is too large.  "
83                  "The maximum allowed by PGM is %u.",
84                  maxval, PGM_OVERALLMAXVAL);
85     if (maxval == 0)
86         pm_error("maxval of input image is zero.");
87 
88     *maxvalP = maxval;
89 }
90 
91 
92 
93 static void
validateComputableSize(unsigned int const cols,unsigned int const rows)94 validateComputableSize(unsigned int const cols,
95                        unsigned int const rows) {
96 /*----------------------------------------------------------------------------
97    Validate that the dimensions of the image are such that it can be
98    processed in typical ways on this machine without worrying about
99    overflows.  Note that in C, arithmetic is always modulus
100    arithmetic, so if your values are too big, the result is not what
101    you expect.  That failed expectation can be disastrous if you use
102    it to allocate memory.
103 
104    It is very normal to allocate space for a pixel row, so we make sure
105    the size of a pixel row, in bytes, can be represented by an 'int'.
106 
107    A common operation is adding 1 or 2 to the highest row or
108    column number in the image, so we make sure that's possible.
109 -----------------------------------------------------------------------------*/
110     if (cols > INT_MAX / (sizeof(gray)) || cols > INT_MAX - 2)
111         pm_error("image width (%u) too large to be processed", cols);
112     if (rows > INT_MAX - 2)
113         pm_error("image height (%u) too large to be processed", rows);
114 }
115 
116 
117 
118 void
pgm_readpgminit(FILE * const fileP,int * const colsP,int * const rowsP,gray * const maxvalP,int * const formatP)119 pgm_readpgminit(FILE * const fileP,
120                 int *  const colsP,
121                 int *  const rowsP,
122                 gray * const maxvalP,
123                 int *  const formatP) {
124 
125     int realFormat;
126 
127     /* Check magic number. */
128     realFormat = pm_readmagicnumber(fileP);
129     switch (PAM_FORMAT_TYPE(realFormat)) {
130     case PBM_TYPE:
131         *formatP = realFormat;
132         pbm_readpbminitrest(fileP, colsP, rowsP);
133 
134         /* Mathematically, it makes the most sense for the maxval of a PBM
135            file seen as a PGM to be 1.  But we tried this for a while and
136            found that it causes unexpected results and frequent need for a
137            Pnmdepth stage to convert the maxval to 255.  You see, when you
138            transform a PGM file in a way that causes interpolated gray shades,
139            there's no in-between value to use when maxval is 1.  It's really
140            hard even to discover that your lack of Pnmdepth is your problem.
141            So we pick 255, which is the most common PGM maxval, and the highest
142            resolution you can get without increasing the size of the PGM
143            image.
144 
145            So this means some programs that are capable of exploiting the
146            bi-level nature of a PBM file must be PNM programs instead of PGM
147            programs.
148         */
149 
150         *maxvalP = PGM_MAXMAXVAL;
151         break;
152 
153     case PGM_TYPE:
154         *formatP = realFormat;
155         pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP);
156         break;
157 
158     case PPM_TYPE:
159         pm_error("Input file is a PPM, which this program cannot process.  "
160                  "You may want to convert it to PGM with 'ppmtopgm'");
161 
162     case PAM_TYPE:
163         pnm_readpaminitrestaspnm(fileP, colsP, rowsP, maxvalP, formatP);
164 
165         if (PAM_FORMAT_TYPE(*formatP) != PGM_TYPE)
166             pm_error("Format of PAM input is not consistent with PGM");
167 
168         break;
169 
170     default:
171         pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
172                  realFormat);
173     }
174     validateComputableSize(*colsP, *rowsP);
175 }
176 
177 
178 
179 static void
validateRpgmRow(gray * const grayrow,unsigned int const cols,gray const maxval,const char ** const errorP)180 validateRpgmRow(gray *         const grayrow,
181                 unsigned int   const cols,
182                 gray           const maxval,
183                 const char **  const errorP) {
184 /*----------------------------------------------------------------------------
185   Check for sample values above maxval in input.
186 
187   Note: a program that wants to deal with invalid sample values itself can
188   simply make sure it uses a sufficiently high maxval on the read function
189   call, so this validation never fails.
190 -----------------------------------------------------------------------------*/
191     if (maxval == 255 || maxval == 65535) {
192         /* There's no way a sample can be invalid, so we don't need to look at
193            the samples individually.
194         */
195         *errorP = NULL;
196     } else {
197         unsigned int col;
198         for (col = 0; col < cols; ++col) {
199             if (grayrow[col] > maxval) {
200                 pm_asprintf(errorP,
201                             "gray value %u is greater than maxval (%u)",
202                             grayrow[col], maxval);
203                 return;
204             }
205         }
206         *errorP = NULL;
207     }
208 }
209 
210 
211 
212 static void
readRpgmRow(FILE * const fileP,gray * const grayrow,int const cols,gray const maxval,int const format)213 readRpgmRow(FILE * const fileP,
214             gray * const grayrow,
215             int    const cols,
216             gray   const maxval,
217             int    const format) {
218 
219     unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
220     int          const bytesPerRow    = cols * bytesPerSample;
221 
222     unsigned char * rowBuffer;
223     const char * error;
224 
225     MALLOCARRAY(rowBuffer, bytesPerRow);
226     if (rowBuffer == NULL)
227         pm_asprintf(&error, "Unable to allocate memory for row buffer "
228                     "for %u columns", cols);
229     else {
230         size_t rc;
231         rc = fread(rowBuffer, 1, bytesPerRow, fileP);
232         if (rc == 0)
233             pm_asprintf(&error, "Error reading row.  fread() errno=%d (%s)",
234                         errno, strerror(errno));
235         else if (rc != bytesPerRow)
236             pm_asprintf(&error, "Error reading row.  Short read of %u bytes "
237                         "instead of %u", (unsigned)rc, bytesPerRow);
238         else {
239             if (maxval < 256) {
240                 unsigned int col;
241                 for (col = 0; col < cols; ++col)
242                     grayrow[col] = (gray)rowBuffer[col];
243             } else {
244                 unsigned int bufferCursor;
245                 unsigned int col;
246 
247                 bufferCursor = 0;  /* Start at beginning of rowBuffer[] */
248 
249                 for (col = 0; col < cols; ++col) {
250                     gray g;
251 
252                     g = rowBuffer[bufferCursor++] << 8;
253                     g |= rowBuffer[bufferCursor++];
254 
255                     grayrow[col] = g;
256                 }
257             }
258             validateRpgmRow(grayrow, cols, maxval, &error);
259         }
260         free(rowBuffer);
261     }
262     if (error) {
263         pm_errormsg("%s", error);
264         pm_strfree(error);
265         pm_longjmp();
266     }
267 }
268 
269 
270 
271 static void
readPbmRow(FILE * const fileP,gray * const grayrow,int const cols,gray const maxval,int const format)272 readPbmRow(FILE * const fileP,
273            gray * const grayrow,
274            int    const cols,
275            gray   const maxval,
276            int    const format) {
277 
278     jmp_buf jmpbuf;
279     jmp_buf * origJmpbufP;
280     bit * bitrow;
281 
282     bitrow = pbm_allocrow_packed(cols);
283     if (setjmp(jmpbuf) != 0) {
284         pbm_freerow(bitrow);
285         pm_setjmpbuf(origJmpbufP);
286         pm_longjmp();
287     } else {
288         unsigned int col;
289 
290         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
291 
292         pbm_readpbmrow_packed(fileP, bitrow, cols, format);
293 
294         for (col = 0; col < cols; ++col) {
295             grayrow[col] =
296                 ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
297                 maxval : 0
298                 ;
299         }
300         pm_setjmpbuf(origJmpbufP);
301     }
302     pbm_freerow(bitrow);
303 }
304 
305 
306 
307 void
pgm_readpgmrow(FILE * const fileP,gray * const grayrow,int const cols,gray const maxval,int const format)308 pgm_readpgmrow(FILE * const fileP,
309                gray * const grayrow,
310                int    const cols,
311                gray   const maxval,
312                int    const format) {
313 
314     switch (format) {
315     case PGM_FORMAT: {
316         unsigned int col;
317         for (col = 0; col < cols; ++col) {
318             grayrow[col] = pm_getuint(fileP);
319             if (grayrow[col] > maxval)
320                 pm_error("value out of bounds (%u > %u)",
321                          grayrow[col], maxval);
322         }
323     }
324     break;
325 
326     case RPGM_FORMAT:
327         readRpgmRow(fileP, grayrow, cols, maxval, format);
328         break;
329 
330     case PBM_FORMAT:
331     case RPBM_FORMAT:
332         readPbmRow(fileP, grayrow, cols, maxval, format);
333         break;
334 
335     default:
336         pm_error("can't happen");
337     }
338 }
339 
340 
341 
342 gray **
pgm_readpgm(FILE * const fileP,int * const colsP,int * const rowsP,gray * const maxvalP)343 pgm_readpgm(FILE * const fileP,
344             int *  const colsP,
345             int *  const rowsP,
346             gray * const maxvalP) {
347 
348     gray ** grays;
349     int rows, cols;
350     gray maxval;
351     int format;
352     jmp_buf jmpbuf;
353     jmp_buf * origJmpbufP;
354 
355     pgm_readpgminit(fileP, &cols, &rows, &maxval, &format);
356 
357     grays = pgm_allocarray(cols, rows);
358 
359     if (setjmp(jmpbuf) != 0) {
360         pgm_freearray(grays, rows);
361         pm_setjmpbuf(origJmpbufP);
362         pm_longjmp();
363     } else {
364         unsigned int row;
365 
366         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
367 
368         for (row = 0; row < rows; ++row)
369             pgm_readpgmrow(fileP, grays[row], cols, maxval, format);
370 
371         pm_setjmpbuf(origJmpbufP);
372     }
373     *colsP = cols;
374     *rowsP = rows;
375     *maxvalP = maxval;
376     return grays;
377 }
378 
379 
380 
381 void
pgm_check(FILE * const file,enum pm_check_type const checkType,int const format,int const cols,int const rows,gray const maxval,enum pm_check_code * const retvalP)382 pgm_check(FILE *               const file,
383           enum pm_check_type   const checkType,
384           int                  const format,
385           int                  const cols,
386           int                  const rows,
387           gray                 const maxval,
388           enum pm_check_code * const retvalP) {
389 
390     if (rows < 0)
391         pm_error("Invalid number of rows passed to pgm_check(): %d", rows);
392     if (cols < 0)
393         pm_error("Invalid number of columns passed to pgm_check(): %d", cols);
394 
395     if (checkType != PM_CHECK_BASIC) {
396         if (retvalP)
397             *retvalP = PM_CHECK_UNKNOWN_TYPE;
398     } else if (PGM_FORMAT_TYPE(format) == PBM_TYPE) {
399         pbm_check(file, checkType, format, cols, rows, retvalP);
400     } else if (format != RPGM_FORMAT) {
401         if (retvalP)
402             *retvalP = PM_CHECK_UNCHECKABLE;
403     } else {
404         pm_filepos const bytesPerRow    = cols * (maxval > 255 ? 2 : 1);
405         pm_filepos const needRasterSize = rows * bytesPerRow;
406 
407         pm_check(file, checkType, needRasterSize, retvalP);
408     }
409 }
410