1 /**************************************************************************
2                                  pmfileio.c
3 ***************************************************************************
4   This file contains fundamental file I/O stuff for libnetpbm.
5   These are external functions, unlike 'fileio.c', but are not
6   particular to any Netpbm format.
7 **************************************************************************/
8 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
9 #define _SVID_SOURCE
10     /* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500
11        does it in other libc's).  pm_config.h defines TMPDIR as P_tmpdir
12        in some environments.
13     */
14 #define _BSD_SOURCE    /* Make sure strdup is defined */
15 #define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko, strdup are defined */
16 #define _LARGEFILE_SOURCE 1  /* Make sure ftello, fseeko are defined */
17 #define _LARGEFILE64_SOURCE 1
18 #define _FILE_OFFSET_BITS 64
19     /* This means ftello() is really ftello64() and returns a 64 bit file
20        position.  Unless the C library doesn't have ftello64(), in which
21        case ftello() is still just ftello().
22 
23        Likewise for all the other C library file functions.
24 
25        And off_t and fpos_t are 64 bit types instead of 32.  Consequently,
26        pm_filepos_t might be 64 bits instead of 32.
27     */
28 #define _LARGE_FILES
29     /* This does for AIX what _FILE_OFFSET_BITS=64 does for GNU */
30 #define _LARGE_FILE_API
31     /* This makes the the x64() functions available on AIX */
32 
33 #include "netpbm/pm_config.h"
34 #include <unistd.h>
35 #include <assert.h>
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <errno.h>
41 #if HAVE_IO_H
42   #include <io.h>  /* For mktemp */
43 #endif
44 
45 #include "netpbm/pm_c_util.h"
46 #include "netpbm/mallocvar.h"
47 #include "netpbm/nstring.h"
48 
49 #include "pm.h"
50 
51 
52 
53 /* File open/close that handles "-" as stdin/stdout and checks errors. */
54 
55 FILE *
pm_openr(const char * const name)56 pm_openr(const char * const name) {
57     FILE * f;
58 
59     if (streq(name, "-"))
60         f = stdin;
61     else {
62         f = fopen(name, "rb");
63         if (f == NULL)
64             pm_error("Unable to open file '%s' for reading.  "
65                      "fopen() returns errno %d (%s)",
66                      name, errno, strerror(errno));
67     }
68     return f;
69 }
70 
71 
72 
73 FILE *
pm_openw(const char * const name)74 pm_openw(const char * const name) {
75     FILE * f;
76 
77     if (streq(name, "-"))
78         f = stdout;
79     else {
80         f = fopen(name, "wb");
81         if (f == NULL)
82             pm_error("Unable to open file '%s' for writing.  "
83                      "fopen() returns errno %d (%s)",
84                      name, errno, strerror(errno));
85     }
86     return f;
87 }
88 
89 
90 
91 static const char *
tmpDir(void)92 tmpDir(void) {
93 /*----------------------------------------------------------------------------
94    Return the name of the directory in which we should create temporary
95    files.
96 
97    The name is a constant in static storage.
98 -----------------------------------------------------------------------------*/
99     const char * tmpdir;
100         /* running approximation of the result */
101 
102     tmpdir = getenv("TMPDIR");   /* Unix convention */
103 
104     if (!tmpdir || strlen(tmpdir) == 0)
105         tmpdir = getenv("TMP");  /* Windows convention */
106 
107     if (!tmpdir || strlen(tmpdir) == 0)
108         tmpdir = getenv("TEMP"); /* Windows convention */
109 
110     if (!tmpdir || strlen(tmpdir) == 0)
111         tmpdir = TMPDIR;
112 
113     return tmpdir;
114 }
115 
116 
117 
118 static int
tempFileOpenFlags(void)119 tempFileOpenFlags(void) {
120 /*----------------------------------------------------------------------------
121   Open flags (argument to open()) suitable for a new temporary file
122 -----------------------------------------------------------------------------*/
123     int retval;
124 
125     retval = 0
126         | O_CREAT
127         | O_RDWR
128 #if !MSVCRT
129         | O_EXCL
130 #endif
131 #if MSVCRT
132         | O_BINARY
133 #endif
134         ;
135 
136     return retval;
137 }
138 
139 
140 
141 
142 static int
mkstempx(char * const filenameBuffer)143 mkstempx(char * const filenameBuffer) {
144 /*----------------------------------------------------------------------------
145   This is meant to be equivalent to POSIX mkstemp().
146 
147   On some old systems, mktemp() is a security hazard that allows a hacker
148   to read or write our temporary file or cause us to read or write some
149   unintended file.  On other systems, mkstemp() does not exist.
150 
151   A Windows/mingw environment is one which doesn't have mkstemp()
152   (2006.06.15).
153 
154   We assume that if a system doesn't have mkstemp() that its mktemp()
155   is safe, or that the total situation is such that the problems of
156   mktemp() are not a problem for the user.
157 -----------------------------------------------------------------------------*/
158     int retval;
159     int fd;
160     unsigned int attempts;
161     bool gotFile;
162     bool error;
163 
164     for (attempts = 0, gotFile = FALSE, error = FALSE;
165          !gotFile && !error && attempts < 100;
166          ++attempts) {
167 
168         char * rc;
169         rc = mktemp(filenameBuffer);
170 
171         if (rc == NULL)
172             error = TRUE;
173         else {
174             int rc;
175 
176             rc = open(filenameBuffer, tempFileOpenFlags(),
177                       PM_S_IWUSR | PM_S_IRUSR);
178 
179             if (rc >= 0) {
180                 fd = rc;
181                 gotFile = TRUE;
182             } else {
183                 if (errno == EEXIST) {
184                     /* We'll just have to keep trying */
185                 } else
186                     error = TRUE;
187             }
188         }
189     }
190     if (gotFile)
191         retval = fd;
192     else
193         retval = -1;
194 
195     return retval;
196 }
197 
198 
199 
200 static int
mkstemp2(char * const filenameBuffer)201 mkstemp2(char * const filenameBuffer) {
202 
203 #if HAVE_MKSTEMP
204     if (0)
205         mkstempx(NULL);  /* defeat compiler unused function warning */
206     return mkstemp(filenameBuffer);
207 #else
208     return mkstempx(filenameBuffer);
209 #endif
210 }
211 
212 
213 
214 static void
makeTmpfileWithTemplate(const char * const filenameTemplate,int * const fdP,const char ** const filenameP,const char ** const errorP)215 makeTmpfileWithTemplate(const char *  const filenameTemplate,
216                         int *         const fdP,
217                         const char ** const filenameP,
218                         const char ** const errorP) {
219 
220     char * filenameBuffer;  /* malloc'ed */
221 
222     filenameBuffer = strdup(filenameTemplate);
223 
224     if (filenameBuffer == NULL)
225         pm_asprintf(errorP, "Unable to allocate storage for temporary "
226                     "file name");
227     else {
228         int rc;
229 
230         rc = mkstemp2(filenameBuffer);
231 
232         if (rc < 0)
233             pm_asprintf(errorP,
234                         "Unable to create temporary file according to name "
235                         "pattern '%s'.  mkstemp() failed with errno %d (%s)",
236                         filenameTemplate, errno, strerror(errno));
237         else {
238             *fdP = rc;
239             *filenameP = filenameBuffer;
240             *errorP = NULL;
241         }
242         if (*errorP)
243             pm_strfree(filenameBuffer);
244     }
245 }
246 
247 
248 
249 void
pm_make_tmpfile_fd(int * const fdP,const char ** const filenameP)250 pm_make_tmpfile_fd(int *         const fdP,
251                    const char ** const filenameP) {
252 
253     const char * filenameTemplate;
254     const char * tmpdir;
255     const char * dirseparator;
256     const char * error;
257 
258     tmpdir = tmpDir();
259 
260     if (tmpdir[strlen(tmpdir) - 1] == '/')
261         dirseparator = "";
262     else
263         dirseparator = "/";
264 
265     pm_asprintf(&filenameTemplate, "%s%s%s%s",
266                 tmpdir, dirseparator, pm_progname, "_XXXXXX");
267 
268     if (filenameTemplate == pm_strsol)
269         pm_asprintf(&error,
270                     "Unable to allocate storage for temporary file name");
271     else {
272         makeTmpfileWithTemplate(filenameTemplate, fdP, filenameP, &error);
273 
274         pm_strfree(filenameTemplate);
275     }
276     if (error) {
277         pm_errormsg("%s", error);
278         pm_strfree(error);
279         pm_longjmp();
280     }
281 }
282 
283 
284 
285 void
pm_make_tmpfile(FILE ** const filePP,const char ** const filenameP)286 pm_make_tmpfile(FILE **       const filePP,
287                 const char ** const filenameP) {
288 
289     int fd;
290 
291     pm_make_tmpfile_fd(&fd, filenameP);
292 
293     *filePP = fdopen(fd, "w+b");
294 
295     if (*filePP == NULL) {
296         close(fd);
297         unlink(*filenameP);
298         pm_strfree(*filenameP);
299 
300         pm_error("Unable to create temporary file.  "
301                  "fdopen() failed with errno %d (%s)",
302                  errno, strerror(errno));
303     }
304 }
305 
306 
307 
308 bool const canUnlinkOpen =
309 #if CAN_UNLINK_OPEN
310     1
311 #else
312     0
313 #endif
314     ;
315 
316 
317 
318 typedef struct UnlinkListEntry {
319 /*----------------------------------------------------------------------------
320    This is an entry in the linked list of files to close and unlink as the
321    program exits.
322 -----------------------------------------------------------------------------*/
323     struct UnlinkListEntry * next;
324     int                      fd;
325     char                     fileName[1];  /* Actually variable length */
326 } UnlinkListEntry;
327 
328 static UnlinkListEntry * unlinkListP;
329 
330 
331 
332 static void
unlinkTempFiles(void)333 unlinkTempFiles(void) {
334 /*----------------------------------------------------------------------------
335   Close and unlink (so presumably delete) the files in the list
336   *unlinkListP.
337 
338   This is an atexit function.
339 -----------------------------------------------------------------------------*/
340     while (unlinkListP) {
341         UnlinkListEntry * const firstEntryP = unlinkListP;
342 
343         unlinkListP = unlinkListP->next;
344 
345         close(firstEntryP->fd);
346         unlink(firstEntryP->fileName);
347 
348         free(firstEntryP);
349     }
350 }
351 
352 
353 
354 static UnlinkListEntry *
newUnlinkListEntry(const char * const fileName,int const fd)355 newUnlinkListEntry(const char * const fileName,
356                    int          const fd) {
357 
358     UnlinkListEntry * const unlinkListEntryP =
359         malloc(sizeof(*unlinkListEntryP) + strlen(fileName) + 1);
360 
361     if (unlinkListEntryP) {
362         strcpy(unlinkListEntryP->fileName, fileName);
363         unlinkListEntryP->fd   = fd;
364         unlinkListEntryP->next = NULL;
365     }
366     return unlinkListEntryP;
367 }
368 
369 
370 
371 static void
addUnlinkListEntry(const char * const fileName,int const fd)372 addUnlinkListEntry(const char * const fileName,
373                    int          const fd) {
374 
375     UnlinkListEntry * const unlinkListEntryP =
376         newUnlinkListEntry(fileName, fd);
377 
378     if (unlinkListEntryP) {
379         unlinkListEntryP->next = unlinkListP;
380         unlinkListP = unlinkListEntryP;
381     }
382 }
383 
384 
385 
386 static void
scheduleUnlinkAtExit(const char * const fileName,int const fd)387 scheduleUnlinkAtExit(const char * const fileName,
388                      int          const fd) {
389 /*----------------------------------------------------------------------------
390    Set things up to have the file unlinked as the program exits.
391 
392    This is messy and probably doesn't work in all situations; it is a hack
393    to get Unix code essentially working on Windows, without messing up the
394    code too badly for Unix.
395 -----------------------------------------------------------------------------*/
396     static bool unlinkListEstablished = false;
397 
398     if (!unlinkListEstablished) {
399         atexit(unlinkTempFiles);
400         unlinkListP = NULL;
401         unlinkListEstablished = true;
402     }
403 
404     addUnlinkListEntry(fileName, fd);
405 }
406 
407 
408 
409 static void
arrangeUnlink(const char * const fileName,int const fd)410 arrangeUnlink(const char * const fileName,
411               int          const fd) {
412 
413     if (canUnlinkOpen)
414         unlink(fileName);
415     else
416         scheduleUnlinkAtExit(fileName, fd);
417 }
418 
419 
420 
421 FILE *
pm_tmpfile(void)422 pm_tmpfile(void) {
423 
424     FILE * fileP;
425     const char * tmpfileNm;
426 
427     pm_make_tmpfile(&fileP, &tmpfileNm);
428 
429     arrangeUnlink(tmpfileNm, fileno(fileP));
430 
431     pm_strfree(tmpfileNm);
432 
433     return fileP;
434 }
435 
436 
437 
438 int
pm_tmpfile_fd(void)439 pm_tmpfile_fd(void) {
440 
441     int fd;
442     const char * tmpfileNm;
443 
444     pm_make_tmpfile_fd(&fd, &tmpfileNm);
445 
446     arrangeUnlink(tmpfileNm, fd);
447 
448     pm_strfree(tmpfileNm);
449 
450     return fd;
451 }
452 
453 
454 
455 FILE *
pm_openr_seekable(const char name[])456 pm_openr_seekable(const char name[]) {
457 /*----------------------------------------------------------------------------
458   Open the file named by name[] such that it is seekable (i.e. it can be
459   rewound and read in multiple passes with fseek()).
460 
461   If the file is actually seekable, this reduces to the same as
462   pm_openr().  If not, we copy the named file to a temporary file
463   and return that file's stream descriptor.
464 
465   We use a file that the operating system recognizes as temporary, so
466   it picks the filename and deletes the file when Caller closes it.
467 -----------------------------------------------------------------------------*/
468     int stat_rc;
469     int seekable;  /* logical: file is seekable */
470     struct stat statbuf;
471     FILE * original_file;
472     FILE * seekable_file;
473 
474     original_file = pm_openr((char *) name);
475 
476     /* I would use fseek() to determine if the file is seekable and
477        be a little more general than checking the type of file, but I
478        don't have reliable information on how to do that.  I have seen
479        streams be partially seekable -- you can, for example seek to
480        0 if the file is positioned at 0 but you can't actually back up
481        to 0.  I have seen documentation that says the errno for an
482        unseekable stream is EBADF and in practice seen ESPIPE.
483 
484        On the other hand, regular files are always seekable and even if
485        some other file is, it doesn't hurt much to assume it isn't.
486     */
487 
488     stat_rc = fstat(fileno(original_file), &statbuf);
489     if (stat_rc == 0 && S_ISREG(statbuf.st_mode))
490         seekable = TRUE;
491     else
492         seekable = FALSE;
493 
494     if (seekable) {
495         seekable_file = original_file;
496     } else {
497         seekable_file = pm_tmpfile();
498 
499         /* Copy the input into the temporary seekable file */
500         while (!feof(original_file) && !ferror(original_file)
501                && !ferror(seekable_file)) {
502             char buffer[4096];
503             int bytes_read;
504             bytes_read = fread(buffer, 1, sizeof(buffer), original_file);
505             fwrite(buffer, 1, bytes_read, seekable_file);
506         }
507         if (ferror(original_file))
508             pm_error("Error reading input file into temporary file.  "
509                      "Errno = %s (%d)", strerror(errno), errno);
510         if (ferror(seekable_file))
511             pm_error("Error writing input into temporary file.  "
512                      "Errno = %s (%d)", strerror(errno), errno);
513         pm_close(original_file);
514         {
515             int seek_rc;
516             seek_rc = fseek(seekable_file, 0, SEEK_SET);
517             if (seek_rc != 0)
518                 pm_error("fseek() failed to rewind temporary file.  "
519                          "Errno = %s (%d)", strerror(errno), errno);
520         }
521     }
522     return seekable_file;
523 }
524 
525 
526 
527 void
pm_close(FILE * const f)528 pm_close(FILE * const f) {
529     fflush(f);
530     if (ferror(f))
531         pm_message("A file read or write error occurred at some point");
532     if (f != stdin)
533         if (fclose(f) != 0)
534             pm_error("close of file failed with errno %d (%s)",
535                      errno, strerror(errno));
536 }
537 
538 
539 
540 /* The pnmtopng package uses pm_closer() and pm_closew() instead of
541    pm_close(), apparently because the 1999 Pbmplus package has them.
542    I don't know what the difference is supposed to be.
543 */
544 
545 void
pm_closer(FILE * const f)546 pm_closer(FILE * const f) {
547     pm_close(f);
548 }
549 
550 
551 
552 void
pm_closew(FILE * const f)553 pm_closew(FILE * const f) {
554     pm_close(f);
555 }
556 
557 
558 
559 /* Endian I/O.
560 
561    Before Netpbm 10.27 (March 2005), these would return failure on EOF
562    or I/O failure.  For backward compatibility, they still have the return
563    code, but it is always zero and the routines abort the program in case
564    of EOF or I/O failure.  A program that wants to handle failure differently
565    must use lower level (C library) interfaces.  But that level of detail
566    is uncharacteristic of a Netpbm program; the ease of programming that
567    comes with not checking a return code is more Netpbm.
568 
569    It is also for historical reasons that these return signed values,
570    when clearly unsigned would make more sense.
571 */
572 
573 
574 
575 static void
abortWithReadError(FILE * const ifP)576 abortWithReadError(FILE * const ifP) {
577 
578     if (feof(ifP))
579         pm_error("Unexpected end of input file");
580     else
581         pm_error("Error (not EOF) reading file.");
582 }
583 
584 
585 
586 static unsigned char
getcNofail(FILE * const ifP)587 getcNofail(FILE * const ifP) {
588 
589     int c;
590 
591     c = getc(ifP);
592 
593     if (c == EOF)
594         abortWithReadError(ifP);
595 
596     return (unsigned char)c;
597 }
598 
599 
600 
601 void
pm_readchar(FILE * const ifP,char * const cP)602 pm_readchar(FILE * const ifP,
603             char * const cP) {
604 
605     *cP = (char)getcNofail(ifP);
606 }
607 
608 
609 
610 void
pm_writechar(FILE * const ofP,char const c)611 pm_writechar(FILE * const ofP,
612              char   const c) {
613 
614     putc(c, ofP);
615 }
616 
617 
618 
619 int
pm_readbigshort(FILE * const ifP,short * const sP)620 pm_readbigshort(FILE *  const ifP,
621                 short * const sP) {
622 
623     unsigned short s;
624 
625     s  = getcNofail(ifP) << 8;
626     s |= getcNofail(ifP) << 0;
627 
628     *sP = s;
629 
630     return 0;
631 }
632 
633 
634 
635 int
pm_writebigshort(FILE * const ofP,short const s)636 pm_writebigshort(FILE * const ofP,
637                  short  const s) {
638 
639     putc((s >> 8) & 0xff, ofP);
640     putc(s & 0xff, ofP);
641 
642     return 0;
643 }
644 
645 
646 
647 int
pm_readbiglong(FILE * const ifP,long * const lP)648 pm_readbiglong(FILE * const ifP,
649                long * const lP) {
650 
651     unsigned long l;
652 
653     l  = getcNofail(ifP) << 24;
654     l |= getcNofail(ifP) << 16;
655     l |= getcNofail(ifP) <<  8;
656     l |= getcNofail(ifP) <<  0;
657 
658     *lP = l;
659 
660     return 0;
661 }
662 
663 
664 
665 int
pm_readbiglong2(FILE * const ifP,int32_t * const lP)666 pm_readbiglong2(FILE *    const ifP,
667                 int32_t * const lP) {
668     int rc;
669     long l;
670 
671     rc = pm_readbiglong(ifP, &l);
672 
673     assert((int32_t)l == l);
674 
675     *lP = (int32_t)l;
676 
677     return rc;
678 }
679 
680 
681 
682 int
pm_writebiglong(FILE * const ofP,long const l)683 pm_writebiglong(FILE * const ofP,
684                 long   const l) {
685 
686     putc((l >> 24) & 0xff, ofP);
687     putc((l >> 16) & 0xff, ofP);
688     putc((l >>  8) & 0xff, ofP);
689     putc((l >>  0) & 0xff, ofP);
690 
691     return 0;
692 }
693 
694 
695 
696 int
pm_readlittleshort(FILE * const ifP,short * const sP)697 pm_readlittleshort(FILE *  const ifP,
698                    short * const sP) {
699     unsigned short s;
700 
701     s  = getcNofail(ifP) << 0;
702     s |= getcNofail(ifP) << 8;
703 
704     *sP = s;
705 
706     return 0;
707 }
708 
709 
710 
711 int
pm_writelittleshort(FILE * const ofP,short const s)712 pm_writelittleshort(FILE * const ofP,
713                     short  const s) {
714 
715     putc((s >> 0) & 0xff, ofP);
716     putc((s >> 8) & 0xff, ofP);
717 
718     return 0;
719 }
720 
721 
722 
723 int
pm_readlittlelong(FILE * const ifP,long * const lP)724 pm_readlittlelong(FILE * const ifP,
725                   long * const lP) {
726     unsigned long l;
727 
728     l  = getcNofail(ifP) <<  0;
729     l |= getcNofail(ifP) <<  8;
730     l |= getcNofail(ifP) << 16;
731     l |= getcNofail(ifP) << 24;
732 
733     *lP = l;
734 
735     return 0;
736 }
737 
738 
739 
740 int
pm_readlittlelong2(FILE * const ifP,int32_t * const lP)741 pm_readlittlelong2(FILE *    const ifP,
742                    int32_t * const lP) {
743     int rc;
744     long l;
745 
746     rc = pm_readlittlelong(ifP, &l);
747 
748     assert((int32_t)l == l);
749 
750     *lP = (int32_t)l;
751 
752     return rc;
753 }
754 
755 
756 
757 int
pm_writelittlelong(FILE * const ofP,long const l)758 pm_writelittlelong(FILE * const ofP,
759                    long   const l) {
760 
761     putc((l >>  0) & 0xff, ofP);
762     putc((l >>  8) & 0xff, ofP);
763     putc((l >> 16) & 0xff, ofP);
764     putc((l >> 24) & 0xff, ofP);
765 
766     return 0;
767 }
768 
769 
770 
771 int
pm_readmagicnumber(FILE * const ifP)772 pm_readmagicnumber(FILE * const ifP) {
773 
774     int ich1, ich2;
775 
776     ich1 = getc(ifP);
777 
778     if (ich1 == EOF)
779         pm_error("Error reading first byte of what is expected to be "
780                  "a Netpbm magic number.  "
781                  "Most often, this means your input file is empty");
782 
783     ich2 = getc(ifP);
784 
785     if (ich2 == EOF)
786         pm_error("Error reading second byte of what is expected to be "
787                  "a Netpbm magic number (the first byte was successfully "
788                  "read as 0x%02x)", ich1);
789 
790     return ich1 * 256 + ich2;
791 }
792 
793 
794 
795 /* Read a file of unknown size to a buffer. Return the number of bytes
796    read. Allocate more memory as we need it. The calling routine has
797    to free() the buffer.
798 
799    Oliver Trepte, oliver@fysik4.kth.se, 930613
800 */
801 
802 #define PM_BUF_SIZE 16384      /* First try this size of the buffer, then
803                                   double this until we reach PM_MAX_BUF_INC */
804 #define PM_MAX_BUF_INC 65536   /* Don't allocate more memory in larger blocks
805                                   than this. */
806 
807 char *
pm_read_unknown_size(FILE * const file,long * const nread)808 pm_read_unknown_size(FILE * const file,
809                      long * const nread) {
810     long nalloc;
811     char * buf;
812     bool eof;
813 
814     *nread = 0;
815     nalloc = PM_BUF_SIZE;
816     MALLOCARRAY(buf, nalloc);
817 
818     if (!buf)
819         pm_error("Failed to allocate %lu bytes for read buffer",
820                  (unsigned long) nalloc);
821 
822     eof = FALSE;  /* initial value */
823 
824     while(!eof) {
825         int val;
826 
827         if (*nread >= nalloc) { /* We need a larger buffer */
828             if (nalloc > PM_MAX_BUF_INC)
829                 nalloc += PM_MAX_BUF_INC;
830             else
831                 nalloc += nalloc;
832             REALLOCARRAY(buf, nalloc);
833             if (!buf)
834                 pm_error("Failed to allocate %lu bytes for read buffer",
835                          (unsigned long) nalloc);
836         }
837 
838         val = getc(file);
839         if (val == EOF)
840             eof = TRUE;
841         else
842             buf[(*nread)++] = val;
843     }
844     return buf;
845 }
846 
847 
848 
849 void
pm_getline(FILE * const ifP,char ** const bufferP,size_t * const bufferSzP,int * const eofP,size_t * const lineLenP)850 pm_getline(FILE *   const ifP,
851            char **  const bufferP,
852            size_t * const bufferSzP,
853            int *    const eofP,
854            size_t * const lineLenP) {
855 /*----------------------------------------------------------------------------
856    This is like POSIX 'getline'.
857 
858    But we don't include the newline in the returned line.
859 -----------------------------------------------------------------------------*/
860     char * buffer;
861     size_t bufferSz;
862     bool gotLine;
863     bool eof;
864     size_t nReadSoFar;
865 
866     buffer   = *bufferP;    /* initial value */
867     bufferSz = *bufferSzP;  /* initial value */
868 
869     for (nReadSoFar = 0, gotLine = false, eof = false;
870          !gotLine && !eof; ) {
871 
872         int rc;
873 
874         rc = fgetc(ifP);
875 
876         if (rc == EOF) {
877             if (ferror(ifP))
878                 pm_error("Error reading input file.  fgets() failed with "
879                          "errno %d (%s)", errno, strerror(errno));
880 
881             if (nReadSoFar == 0) {
882                 /* Didn't get anything before EOF, so return EOF */
883                 eof = true;
884             } else {
885                 /* End of file ends the line */
886                 gotLine = true;
887             }
888         } else {
889             char const c = (char)rc;
890 
891             if (c == '\n') {
892                 /* Newline ends the line, and is not part of it */
893                 gotLine = true;
894             } else {
895                 if (nReadSoFar + 2 > bufferSz) {
896                     /* + 2 = 1 for 'c', one for terminating NUL */
897                     bufferSz += 128;
898                     REALLOCARRAY(buffer, bufferSz);
899                     if (!buffer) {
900                         pm_error("Failed to allocate %lu bytes for buffer "
901                                  "to assemble a line of input",
902                                  (unsigned long) bufferSz);
903                     }
904                 }
905                 buffer[nReadSoFar++] = c;
906             }
907         }
908     }
909 
910     if (gotLine) {
911         bufferSz = nReadSoFar + 1;
912         REALLOCARRAY(buffer, bufferSz);
913         if (!buffer) {
914             pm_error("Failed to allocate %lu bytes for buffer "
915                      "to assemble a line of input",
916                      (unsigned long) bufferSz);
917         }
918         buffer[nReadSoFar] = '\0';
919     }
920 
921     *eofP      = eof;
922     *bufferP   = buffer;
923     *bufferSzP = bufferSz;
924     *lineLenP  = nReadSoFar;
925 }
926 
927 
928 
929 union cheat {
930     uint32_t l;
931     short s;
932     unsigned char c[4];
933 };
934 
935 
936 
937 short
pm_bs_short(short const s)938 pm_bs_short(short const s) {
939     union cheat u;
940     unsigned char t;
941 
942     u.s = s;
943     t = u.c[0];
944     u.c[0] = u.c[1];
945     u.c[1] = t;
946     return u.s;
947 }
948 
949 
950 
951 long
pm_bs_long(long const l)952 pm_bs_long(long const l) {
953     union cheat u;
954     unsigned char t;
955 
956     u.l = l;
957     t = u.c[0];
958     u.c[0] = u.c[3];
959     u.c[3] = t;
960     t = u.c[1];
961     u.c[1] = u.c[2];
962     u.c[2] = t;
963     return u.l;
964 }
965 
966 
967 
968 void
pm_tell2(FILE * const fileP,void * const fileposP,unsigned int const fileposSize)969 pm_tell2(FILE *       const fileP,
970          void *       const fileposP,
971          unsigned int const fileposSize) {
972 /*----------------------------------------------------------------------------
973    Return the current file position as *filePosP, which is a buffer
974    'fileposSize' bytes long.  Abort the program if error, including if
975    *fileP isn't a file that has a position.
976 -----------------------------------------------------------------------------*/
977     /* Note: FTELLO() is either ftello() or ftell(), depending on the
978        capabilities of the underlying C library.  It is defined in
979        pm_config.h.  ftello(), in turn, may be either ftell() or
980        ftello64(), as implemented by the C library.
981     */
982     pm_filepos const filepos = FTELLO(fileP);
983     if (filepos < 0)
984         pm_error("ftello() to get current file position failed.  "
985                  "Errno = %s (%d)\n", strerror(errno), errno);
986 
987     if (fileposSize == sizeof(pm_filepos)) {
988         pm_filepos * const fileposP_filepos = fileposP;
989         *fileposP_filepos = filepos;
990     } else if (fileposSize == sizeof(long)) {
991         if (sizeof(pm_filepos) > sizeof(long) &&
992             filepos >= (pm_filepos) 1 << (sizeof(long)*8))
993             pm_error("File size is too large to represent in the %u bytes "
994                      "that were provided to pm_tell2()", fileposSize);
995         else {
996             long * const fileposP_long = fileposP;
997             *fileposP_long = (long)filepos;
998         }
999     } else
1000         pm_error("File position size passed to pm_tell() is invalid: %u.  "
1001                  "Valid sizes are %u and %u",
1002                  fileposSize, (unsigned int)sizeof(pm_filepos),
1003                  (unsigned int) sizeof(long));
1004 }
1005 
1006 
1007 
1008 unsigned int
pm_tell(FILE * const fileP)1009 pm_tell(FILE * const fileP) {
1010 
1011     long filepos;
1012 
1013     pm_tell2(fileP, &filepos, sizeof(filepos));
1014 
1015     return filepos;
1016 }
1017 
1018 
1019 
1020 void
pm_seek2(FILE * const fileP,const pm_filepos * const fileposP,unsigned int const fileposSize)1021 pm_seek2(FILE *             const fileP,
1022          const pm_filepos * const fileposP,
1023          unsigned int       const fileposSize) {
1024 /*----------------------------------------------------------------------------
1025    Position file *fileP to position *fileposP.  Abort if error, including
1026    if *fileP isn't a seekable file.
1027 -----------------------------------------------------------------------------*/
1028     if (fileposSize == sizeof(pm_filepos))
1029         /* Note: FSEEKO() is either fseeko() or fseek(), depending on the
1030            capabilities of the underlying C library.  It is defined in
1031            pm_config.h.  fseeko(), in turn, may be either fseek() or
1032            fseeko64(), as implemented by the C library.
1033         */
1034         FSEEKO(fileP, *fileposP, SEEK_SET);
1035     else if (fileposSize == sizeof(long)) {
1036         long const fileposLong = *(long *)fileposP;
1037         fseek(fileP, fileposLong, SEEK_SET);
1038     } else
1039         pm_error("File position size passed to pm_seek() is invalid: %u.  "
1040                  "Valid sizes are %u and %u",
1041                  fileposSize, (unsigned int)sizeof(pm_filepos),
1042                  (unsigned int) sizeof(long));
1043 }
1044 
1045 
1046 
1047 void
pm_seek(FILE * const fileP,unsigned long filepos)1048 pm_seek(FILE * const fileP, unsigned long filepos) {
1049 /*----------------------------------------------------------------------------
1050 -----------------------------------------------------------------------------*/
1051 
1052     pm_filepos fileposBuff;
1053 
1054     fileposBuff = filepos;
1055 
1056     pm_seek2(fileP, &fileposBuff, sizeof(fileposBuff));
1057 }
1058 
1059 
1060 
1061 void
pm_nextimage(FILE * const file,int * const eofP)1062 pm_nextimage(FILE * const file, int * const eofP) {
1063 /*----------------------------------------------------------------------------
1064    Position the file 'file' to the next image in the stream, assuming it is
1065    now positioned just after the current image.  I.e. read off any white
1066    space at the end of the current image's raster.  Note that the raw formats
1067    don't permit such white space, but this routine tolerates it anyway,
1068    because the plain formats do permit white space after the raster.
1069 
1070    Iff there is no next image, return *eofP == TRUE.
1071 
1072    Note that in practice, we will not normally see white space here in
1073    a plain PPM or plain PGM stream because the routine to read a
1074    sample from the image reads one character of white space after the
1075    sample in order to know where the sample ends.  There is not
1076    normally more than one character of white space (a newline) after
1077    the last sample in the raster.  But plain PBM is another story.  No white
1078    space is required between samples of a plain PBM image.  But the raster
1079    normally ends with a newline nonetheless.  Since the sample reading code
1080    will not have read that newline, it is there for us to read now.
1081 -----------------------------------------------------------------------------*/
1082     bool eof;
1083     bool nonWhitespaceFound;
1084 
1085     eof = FALSE;
1086     nonWhitespaceFound = FALSE;
1087 
1088     while (!eof && !nonWhitespaceFound) {
1089         int c;
1090         c = getc(file);
1091         if (c == EOF) {
1092             if (feof(file))
1093                 eof = TRUE;
1094             else
1095                 pm_error("File error on getc() to position to image");
1096         } else {
1097             if (!isspace(c)) {
1098                 int rc;
1099 
1100                 nonWhitespaceFound = TRUE;
1101 
1102                 /* Have to put the non-whitespace character back in
1103                    the stream -- it's part of the next image.
1104                 */
1105                 rc = ungetc(c, file);
1106                 if (rc == EOF)
1107                     pm_error("File error doing ungetc() "
1108                              "to position to image.");
1109             }
1110         }
1111     }
1112     *eofP = eof;
1113 }
1114 
1115 
1116 
1117 void
pm_check(FILE * const file,enum pm_check_type const check_type,pm_filepos const need_raster_size,enum pm_check_code * const retval_p)1118 pm_check(FILE *               const file,
1119          enum pm_check_type   const check_type,
1120          pm_filepos           const need_raster_size,
1121          enum pm_check_code * const retval_p) {
1122 /*----------------------------------------------------------------------------
1123    This is not defined for use outside of libnetpbm.
1124 -----------------------------------------------------------------------------*/
1125     struct stat statbuf;
1126     pm_filepos curpos;  /* Current position of file; -1 if none */
1127     int rc;
1128 
1129     /* Note: FTELLO() is either ftello() or ftell(), depending on the
1130        capabilities of the underlying C library.  It is defined in
1131        pm_config.h.  ftello(), in turn, may be either ftell() or
1132        ftello64(), as implemented by the C library.
1133     */
1134     curpos = FTELLO(file);
1135     if (curpos >= 0) {
1136         /* This type of file has a current position */
1137 
1138         rc = fstat(fileno(file), &statbuf);
1139         if (rc != 0)
1140             pm_error("fstat() failed to get size of file, though ftello() "
1141                      "successfully identified\n"
1142                      "the current position.  Errno=%s (%d)",
1143                      strerror(errno), errno);
1144         else if (!S_ISREG(statbuf.st_mode)) {
1145             /* Not a regular file; we can't know its size */
1146             if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
1147         } else {
1148             pm_filepos const have_raster_size = statbuf.st_size - curpos;
1149 
1150             if (have_raster_size < need_raster_size)
1151                 pm_error("File has invalid format.  The raster should "
1152                          "contain %u bytes, but\n"
1153                          "the file ends after only %u bytes.",
1154                          (unsigned int) need_raster_size,
1155                          (unsigned int) have_raster_size);
1156             else if (have_raster_size > need_raster_size) {
1157                 if (retval_p) *retval_p = PM_CHECK_TOO_LONG;
1158             } else {
1159                 if (retval_p) *retval_p = PM_CHECK_OK;
1160             }
1161         }
1162     } else
1163         if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
1164 }
1165 
1166 
1167 
1168 void
pm_drain(FILE * const fileP,unsigned int const limit,unsigned int * const bytesReadP)1169 pm_drain(FILE *         const fileP,
1170          unsigned int   const limit,
1171          unsigned int * const bytesReadP) {
1172 /*----------------------------------------------------------------------------
1173   Read bytes from *fileP until EOF and return as *bytesReadP how many there
1174   were.
1175 
1176   But don't read any more than 'limit'.
1177 
1178   This is a good thing to call after reading an input file to be sure you
1179   didn't leave some input behind, which could mean you didn't properly
1180   interpret the file.
1181 -----------------------------------------------------------------------------*/
1182     unsigned int bytesRead;
1183     bool eof;
1184 
1185     for (bytesRead = 0, eof = false; !eof && bytesRead < limit;) {
1186 
1187         int rc;
1188 
1189         rc = fgetc(fileP);
1190 
1191         eof = (rc == EOF);
1192         if (!eof)
1193             ++bytesRead;
1194     }
1195     *bytesReadP = bytesRead;
1196 }
1197