|
Packit |
78deda |
/**************************************************************************
|
|
Packit |
78deda |
pmfileio.c
|
|
Packit |
78deda |
***************************************************************************
|
|
Packit |
78deda |
This file contains fundamental file I/O stuff for libnetpbm.
|
|
Packit |
78deda |
These are external functions, unlike 'fileio.c', but are not
|
|
Packit |
78deda |
particular to any Netpbm format.
|
|
Packit |
78deda |
**************************************************************************/
|
|
Packit |
78deda |
#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
|
|
Packit |
78deda |
#define _SVID_SOURCE
|
|
Packit |
78deda |
/* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500
|
|
Packit |
78deda |
does it in other libc's). pm_config.h defines TMPDIR as P_tmpdir
|
|
Packit |
78deda |
in some environments.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
#define _BSD_SOURCE /* Make sure strdup is defined */
|
|
Packit |
78deda |
#define _XOPEN_SOURCE 500 /* Make sure ftello, fseeko, strdup are defined */
|
|
Packit |
78deda |
#define _LARGEFILE_SOURCE 1 /* Make sure ftello, fseeko are defined */
|
|
Packit |
78deda |
#define _LARGEFILE64_SOURCE 1
|
|
Packit |
78deda |
#define _FILE_OFFSET_BITS 64
|
|
Packit |
78deda |
/* This means ftello() is really ftello64() and returns a 64 bit file
|
|
Packit |
78deda |
position. Unless the C library doesn't have ftello64(), in which
|
|
Packit |
78deda |
case ftello() is still just ftello().
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Likewise for all the other C library file functions.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
And off_t and fpos_t are 64 bit types instead of 32. Consequently,
|
|
Packit |
78deda |
pm_filepos_t might be 64 bits instead of 32.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
#define _LARGE_FILES
|
|
Packit |
78deda |
/* This does for AIX what _FILE_OFFSET_BITS=64 does for GNU */
|
|
Packit |
78deda |
#define _LARGE_FILE_API
|
|
Packit |
78deda |
/* This makes the the x64() functions available on AIX */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#include "netpbm/pm_config.h"
|
|
Packit |
78deda |
#include <unistd.h>
|
|
Packit |
78deda |
#include <assert.h>
|
|
Packit |
78deda |
#include <stdio.h>
|
|
Packit |
78deda |
#include <fcntl.h>
|
|
Packit |
78deda |
#include <stdarg.h>
|
|
Packit |
78deda |
#include <string.h>
|
|
Packit |
78deda |
#include <errno.h>
|
|
Packit |
78deda |
#if HAVE_IO_H
|
|
Packit |
78deda |
#include <io.h> /* For mktemp */
|
|
Packit |
78deda |
#endif
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#include "netpbm/pm_c_util.h"
|
|
Packit |
78deda |
#include "netpbm/mallocvar.h"
|
|
Packit |
78deda |
#include "netpbm/nstring.h"
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#include "pm.h"
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* File open/close that handles "-" as stdin/stdout and checks errors. */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
FILE *
|
|
Packit |
78deda |
pm_openr(const char * const name) {
|
|
Packit |
78deda |
FILE * f;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (streq(name, "-"))
|
|
Packit |
78deda |
f = stdin;
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
f = fopen(name, "rb");
|
|
Packit |
78deda |
if (f == NULL)
|
|
Packit |
78deda |
pm_error("Unable to open file '%s' for reading. "
|
|
Packit |
78deda |
"fopen() returns errno %d (%s)",
|
|
Packit |
78deda |
name, errno, strerror(errno));
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
return f;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
FILE *
|
|
Packit |
78deda |
pm_openw(const char * const name) {
|
|
Packit |
78deda |
FILE * f;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (streq(name, "-"))
|
|
Packit |
78deda |
f = stdout;
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
f = fopen(name, "wb");
|
|
Packit |
78deda |
if (f == NULL)
|
|
Packit |
78deda |
pm_error("Unable to open file '%s' for writing. "
|
|
Packit |
78deda |
"fopen() returns errno %d (%s)",
|
|
Packit |
78deda |
name, errno, strerror(errno));
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
return f;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static const char *
|
|
Packit |
78deda |
tmpDir(void) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Return the name of the directory in which we should create temporary
|
|
Packit |
78deda |
files.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
The name is a constant in static storage.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
const char * tmpdir;
|
|
Packit |
78deda |
/* running approximation of the result */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
tmpdir = getenv("TMPDIR"); /* Unix convention */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!tmpdir || strlen(tmpdir) == 0)
|
|
Packit |
78deda |
tmpdir = getenv("TMP"); /* Windows convention */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!tmpdir || strlen(tmpdir) == 0)
|
|
Packit |
78deda |
tmpdir = getenv("TEMP"); /* Windows convention */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!tmpdir || strlen(tmpdir) == 0)
|
|
Packit |
78deda |
tmpdir = TMPDIR;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return tmpdir;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static int
|
|
Packit |
78deda |
tempFileOpenFlags(void) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Open flags (argument to open()) suitable for a new temporary file
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
int retval;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
retval = 0
|
|
Packit |
78deda |
| O_CREAT
|
|
Packit |
78deda |
| O_RDWR
|
|
Packit |
78deda |
#if !MSVCRT
|
|
Packit |
78deda |
| O_EXCL
|
|
Packit |
78deda |
#endif
|
|
Packit |
78deda |
#if MSVCRT
|
|
Packit |
78deda |
| O_BINARY
|
|
Packit |
78deda |
#endif
|
|
Packit |
78deda |
;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return retval;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static int
|
|
Packit |
78deda |
mkstempx(char * const filenameBuffer) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
This is meant to be equivalent to POSIX mkstemp().
|
|
Packit |
78deda |
|
|
Packit |
78deda |
On some old systems, mktemp() is a security hazard that allows a hacker
|
|
Packit |
78deda |
to read or write our temporary file or cause us to read or write some
|
|
Packit |
78deda |
unintended file. On other systems, mkstemp() does not exist.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
A Windows/mingw environment is one which doesn't have mkstemp()
|
|
Packit |
78deda |
(2006.06.15).
|
|
Packit |
78deda |
|
|
Packit |
78deda |
We assume that if a system doesn't have mkstemp() that its mktemp()
|
|
Packit |
78deda |
is safe, or that the total situation is such that the problems of
|
|
Packit |
78deda |
mktemp() are not a problem for the user.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
int retval;
|
|
Packit |
78deda |
int fd;
|
|
Packit |
78deda |
unsigned int attempts;
|
|
Packit |
78deda |
bool gotFile;
|
|
Packit |
78deda |
bool error;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (attempts = 0, gotFile = FALSE, error = FALSE;
|
|
Packit |
78deda |
!gotFile && !error && attempts < 100;
|
|
Packit |
78deda |
++attempts) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
char * rc;
|
|
Packit |
78deda |
rc = mktemp(filenameBuffer);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (rc == NULL)
|
|
Packit |
78deda |
error = TRUE;
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
rc = open(filenameBuffer, tempFileOpenFlags(),
|
|
Packit |
78deda |
PM_S_IWUSR | PM_S_IRUSR);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (rc >= 0) {
|
|
Packit |
78deda |
fd = rc;
|
|
Packit |
78deda |
gotFile = TRUE;
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
if (errno == EEXIST) {
|
|
Packit |
78deda |
/* We'll just have to keep trying */
|
|
Packit |
78deda |
} else
|
|
Packit |
78deda |
error = TRUE;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (gotFile)
|
|
Packit |
78deda |
retval = fd;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
retval = -1;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return retval;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static int
|
|
Packit |
78deda |
mkstemp2(char * const filenameBuffer) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#if HAVE_MKSTEMP
|
|
Packit |
78deda |
if (0)
|
|
Packit |
78deda |
mkstempx(NULL); /* defeat compiler unused function warning */
|
|
Packit |
78deda |
return mkstemp(filenameBuffer);
|
|
Packit |
78deda |
#else
|
|
Packit |
78deda |
return mkstempx(filenameBuffer);
|
|
Packit |
78deda |
#endif
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
makeTmpfileWithTemplate(const char * const filenameTemplate,
|
|
Packit |
78deda |
int * const fdP,
|
|
Packit |
78deda |
const char ** const filenameP,
|
|
Packit |
78deda |
const char ** const errorP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
char * filenameBuffer; /* malloc'ed */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
filenameBuffer = strdup(filenameTemplate);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (filenameBuffer == NULL)
|
|
Packit |
78deda |
pm_asprintf(errorP, "Unable to allocate storage for temporary "
|
|
Packit |
78deda |
"file name");
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
rc = mkstemp2(filenameBuffer);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (rc < 0)
|
|
Packit |
78deda |
pm_asprintf(errorP,
|
|
Packit |
78deda |
"Unable to create temporary file according to name "
|
|
Packit |
78deda |
"pattern '%s'. mkstemp() failed with errno %d (%s)",
|
|
Packit |
78deda |
filenameTemplate, errno, strerror(errno));
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
*fdP = rc;
|
|
Packit |
78deda |
*filenameP = filenameBuffer;
|
|
Packit |
78deda |
*errorP = NULL;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (*errorP)
|
|
Packit |
78deda |
pm_strfree(filenameBuffer);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_make_tmpfile_fd(int * const fdP,
|
|
Packit |
78deda |
const char ** const filenameP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
const char * filenameTemplate;
|
|
Packit |
78deda |
const char * tmpdir;
|
|
Packit |
78deda |
const char * dirseparator;
|
|
Packit |
78deda |
const char * error;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
tmpdir = tmpDir();
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (tmpdir[strlen(tmpdir) - 1] == '/')
|
|
Packit |
78deda |
dirseparator = "";
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
dirseparator = "/";
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_asprintf(&filenameTemplate, "%s%s%s%s",
|
|
Packit |
78deda |
tmpdir, dirseparator, pm_progname, "_XXXXXX");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (filenameTemplate == pm_strsol)
|
|
Packit |
78deda |
pm_asprintf(&error,
|
|
Packit |
78deda |
"Unable to allocate storage for temporary file name");
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
makeTmpfileWithTemplate(filenameTemplate, fdP, filenameP, &error);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_strfree(filenameTemplate);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (error) {
|
|
Packit |
78deda |
pm_errormsg("%s", error);
|
|
Packit |
78deda |
pm_strfree(error);
|
|
Packit |
78deda |
pm_longjmp();
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_make_tmpfile(FILE ** const filePP,
|
|
Packit |
78deda |
const char ** const filenameP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int fd;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_make_tmpfile_fd(&fd, filenameP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*filePP = fdopen(fd, "w+b");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (*filePP == NULL) {
|
|
Packit |
78deda |
close(fd);
|
|
Packit |
78deda |
unlink(*filenameP);
|
|
Packit |
78deda |
pm_strfree(*filenameP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_error("Unable to create temporary file. "
|
|
Packit |
78deda |
"fdopen() failed with errno %d (%s)",
|
|
Packit |
78deda |
errno, strerror(errno));
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
bool const canUnlinkOpen =
|
|
Packit |
78deda |
#if CAN_UNLINK_OPEN
|
|
Packit |
78deda |
1
|
|
Packit |
78deda |
#else
|
|
Packit |
78deda |
0
|
|
Packit |
78deda |
#endif
|
|
Packit |
78deda |
;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
typedef struct UnlinkListEntry {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
This is an entry in the linked list of files to close and unlink as the
|
|
Packit |
78deda |
program exits.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
struct UnlinkListEntry * next;
|
|
Packit |
78deda |
int fd;
|
|
Packit |
78deda |
char fileName[1]; /* Actually variable length */
|
|
Packit |
78deda |
} UnlinkListEntry;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static UnlinkListEntry * unlinkListP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
unlinkTempFiles(void) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Close and unlink (so presumably delete) the files in the list
|
|
Packit |
78deda |
*unlinkListP.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
This is an atexit function.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
while (unlinkListP) {
|
|
Packit |
78deda |
UnlinkListEntry * const firstEntryP = unlinkListP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unlinkListP = unlinkListP->next;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
close(firstEntryP->fd);
|
|
Packit |
78deda |
unlink(firstEntryP->fileName);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
free(firstEntryP);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static UnlinkListEntry *
|
|
Packit |
78deda |
newUnlinkListEntry(const char * const fileName,
|
|
Packit |
78deda |
int const fd) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
UnlinkListEntry * const unlinkListEntryP =
|
|
Packit |
78deda |
malloc(sizeof(*unlinkListEntryP) + strlen(fileName) + 1);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (unlinkListEntryP) {
|
|
Packit |
78deda |
strcpy(unlinkListEntryP->fileName, fileName);
|
|
Packit |
78deda |
unlinkListEntryP->fd = fd;
|
|
Packit |
78deda |
unlinkListEntryP->next = NULL;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
return unlinkListEntryP;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
addUnlinkListEntry(const char * const fileName,
|
|
Packit |
78deda |
int const fd) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
UnlinkListEntry * const unlinkListEntryP =
|
|
Packit |
78deda |
newUnlinkListEntry(fileName, fd);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (unlinkListEntryP) {
|
|
Packit |
78deda |
unlinkListEntryP->next = unlinkListP;
|
|
Packit |
78deda |
unlinkListP = unlinkListEntryP;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
scheduleUnlinkAtExit(const char * const fileName,
|
|
Packit |
78deda |
int const fd) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Set things up to have the file unlinked as the program exits.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
This is messy and probably doesn't work in all situations; it is a hack
|
|
Packit |
78deda |
to get Unix code essentially working on Windows, without messing up the
|
|
Packit |
78deda |
code too badly for Unix.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
static bool unlinkListEstablished = false;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!unlinkListEstablished) {
|
|
Packit |
78deda |
atexit(unlinkTempFiles);
|
|
Packit |
78deda |
unlinkListP = NULL;
|
|
Packit |
78deda |
unlinkListEstablished = true;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
addUnlinkListEntry(fileName, fd);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
arrangeUnlink(const char * const fileName,
|
|
Packit |
78deda |
int const fd) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (canUnlinkOpen)
|
|
Packit |
78deda |
unlink(fileName);
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
scheduleUnlinkAtExit(fileName, fd);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
FILE *
|
|
Packit |
78deda |
pm_tmpfile(void) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
FILE * fileP;
|
|
Packit |
78deda |
const char * tmpfileNm;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_make_tmpfile(&fileP, &tmpfileNm);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
arrangeUnlink(tmpfileNm, fileno(fileP));
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_strfree(tmpfileNm);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return fileP;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_tmpfile_fd(void) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int fd;
|
|
Packit |
78deda |
const char * tmpfileNm;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_make_tmpfile_fd(&fd, &tmpfileNm);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
arrangeUnlink(tmpfileNm, fd);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_strfree(tmpfileNm);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return fd;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
FILE *
|
|
Packit |
78deda |
pm_openr_seekable(const char name[]) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Open the file named by name[] such that it is seekable (i.e. it can be
|
|
Packit |
78deda |
rewound and read in multiple passes with fseek()).
|
|
Packit |
78deda |
|
|
Packit |
78deda |
If the file is actually seekable, this reduces to the same as
|
|
Packit |
78deda |
pm_openr(). If not, we copy the named file to a temporary file
|
|
Packit |
78deda |
and return that file's stream descriptor.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
We use a file that the operating system recognizes as temporary, so
|
|
Packit |
78deda |
it picks the filename and deletes the file when Caller closes it.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
int stat_rc;
|
|
Packit |
78deda |
int seekable; /* logical: file is seekable */
|
|
Packit |
78deda |
struct stat statbuf;
|
|
Packit |
78deda |
FILE * original_file;
|
|
Packit |
78deda |
FILE * seekable_file;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
original_file = pm_openr((char *) name);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* I would use fseek() to determine if the file is seekable and
|
|
Packit |
78deda |
be a little more general than checking the type of file, but I
|
|
Packit |
78deda |
don't have reliable information on how to do that. I have seen
|
|
Packit |
78deda |
streams be partially seekable -- you can, for example seek to
|
|
Packit |
78deda |
0 if the file is positioned at 0 but you can't actually back up
|
|
Packit |
78deda |
to 0. I have seen documentation that says the errno for an
|
|
Packit |
78deda |
unseekable stream is EBADF and in practice seen ESPIPE.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
On the other hand, regular files are always seekable and even if
|
|
Packit |
78deda |
some other file is, it doesn't hurt much to assume it isn't.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
stat_rc = fstat(fileno(original_file), &statbuf);
|
|
Packit |
78deda |
if (stat_rc == 0 && S_ISREG(statbuf.st_mode))
|
|
Packit |
78deda |
seekable = TRUE;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
seekable = FALSE;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (seekable) {
|
|
Packit |
78deda |
seekable_file = original_file;
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
seekable_file = pm_tmpfile();
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Copy the input into the temporary seekable file */
|
|
Packit |
78deda |
while (!feof(original_file) && !ferror(original_file)
|
|
Packit |
78deda |
&& !ferror(seekable_file)) {
|
|
Packit |
78deda |
char buffer[4096];
|
|
Packit |
78deda |
int bytes_read;
|
|
Packit |
78deda |
bytes_read = fread(buffer, 1, sizeof(buffer), original_file);
|
|
Packit |
78deda |
fwrite(buffer, 1, bytes_read, seekable_file);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (ferror(original_file))
|
|
Packit |
78deda |
pm_error("Error reading input file into temporary file. "
|
|
Packit |
78deda |
"Errno = %s (%d)", strerror(errno), errno);
|
|
Packit |
78deda |
if (ferror(seekable_file))
|
|
Packit |
78deda |
pm_error("Error writing input into temporary file. "
|
|
Packit |
78deda |
"Errno = %s (%d)", strerror(errno), errno);
|
|
Packit |
78deda |
pm_close(original_file);
|
|
Packit |
78deda |
{
|
|
Packit |
78deda |
int seek_rc;
|
|
Packit |
78deda |
seek_rc = fseek(seekable_file, 0, SEEK_SET);
|
|
Packit |
78deda |
if (seek_rc != 0)
|
|
Packit |
78deda |
pm_error("fseek() failed to rewind temporary file. "
|
|
Packit |
78deda |
"Errno = %s (%d)", strerror(errno), errno);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
return seekable_file;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_close(FILE * const f) {
|
|
Packit |
78deda |
fflush(f);
|
|
Packit |
78deda |
if (ferror(f))
|
|
Packit |
78deda |
pm_message("A file read or write error occurred at some point");
|
|
Packit |
78deda |
if (f != stdin)
|
|
Packit |
78deda |
if (fclose(f) != 0)
|
|
Packit |
78deda |
pm_error("close of file failed with errno %d (%s)",
|
|
Packit |
78deda |
errno, strerror(errno));
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* The pnmtopng package uses pm_closer() and pm_closew() instead of
|
|
Packit |
78deda |
pm_close(), apparently because the 1999 Pbmplus package has them.
|
|
Packit |
78deda |
I don't know what the difference is supposed to be.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_closer(FILE * const f) {
|
|
Packit |
78deda |
pm_close(f);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_closew(FILE * const f) {
|
|
Packit |
78deda |
pm_close(f);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Endian I/O.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Before Netpbm 10.27 (March 2005), these would return failure on EOF
|
|
Packit |
78deda |
or I/O failure. For backward compatibility, they still have the return
|
|
Packit |
78deda |
code, but it is always zero and the routines abort the program in case
|
|
Packit |
78deda |
of EOF or I/O failure. A program that wants to handle failure differently
|
|
Packit |
78deda |
must use lower level (C library) interfaces. But that level of detail
|
|
Packit |
78deda |
is uncharacteristic of a Netpbm program; the ease of programming that
|
|
Packit |
78deda |
comes with not checking a return code is more Netpbm.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
It is also for historical reasons that these return signed values,
|
|
Packit |
78deda |
when clearly unsigned would make more sense.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
abortWithReadError(FILE * const ifP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (feof(ifP))
|
|
Packit |
78deda |
pm_error("Unexpected end of input file");
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
pm_error("Error (not EOF) reading file.");
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static unsigned char
|
|
Packit |
78deda |
getcNofail(FILE * const ifP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int c;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
c = getc(ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (c == EOF)
|
|
Packit |
78deda |
abortWithReadError(ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return (unsigned char)c;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_readchar(FILE * const ifP,
|
|
Packit |
78deda |
char * const cP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*cP = (char)getcNofail(ifP);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_writechar(FILE * const ofP,
|
|
Packit |
78deda |
char const c) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
putc(c, ofP);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_readbigshort(FILE * const ifP,
|
|
Packit |
78deda |
short * const sP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned short s;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
s = getcNofail(ifP) << 8;
|
|
Packit |
78deda |
s |= getcNofail(ifP) << 0;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*sP = s;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_writebigshort(FILE * const ofP,
|
|
Packit |
78deda |
short const s) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
putc((s >> 8) & 0xff, ofP);
|
|
Packit |
78deda |
putc(s & 0xff, ofP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_readbiglong(FILE * const ifP,
|
|
Packit |
78deda |
long * const lP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned long l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
l = getcNofail(ifP) << 24;
|
|
Packit |
78deda |
l |= getcNofail(ifP) << 16;
|
|
Packit |
78deda |
l |= getcNofail(ifP) << 8;
|
|
Packit |
78deda |
l |= getcNofail(ifP) << 0;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*lP = l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_readbiglong2(FILE * const ifP,
|
|
Packit |
78deda |
int32_t * const lP) {
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
long l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
rc = pm_readbiglong(ifP, &l);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
assert((int32_t)l == l);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*lP = (int32_t)l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return rc;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_writebiglong(FILE * const ofP,
|
|
Packit |
78deda |
long const l) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
putc((l >> 24) & 0xff, ofP);
|
|
Packit |
78deda |
putc((l >> 16) & 0xff, ofP);
|
|
Packit |
78deda |
putc((l >> 8) & 0xff, ofP);
|
|
Packit |
78deda |
putc((l >> 0) & 0xff, ofP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_readlittleshort(FILE * const ifP,
|
|
Packit |
78deda |
short * const sP) {
|
|
Packit |
78deda |
unsigned short s;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
s = getcNofail(ifP) << 0;
|
|
Packit |
78deda |
s |= getcNofail(ifP) << 8;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*sP = s;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_writelittleshort(FILE * const ofP,
|
|
Packit |
78deda |
short const s) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
putc((s >> 0) & 0xff, ofP);
|
|
Packit |
78deda |
putc((s >> 8) & 0xff, ofP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_readlittlelong(FILE * const ifP,
|
|
Packit |
78deda |
long * const lP) {
|
|
Packit |
78deda |
unsigned long l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
l = getcNofail(ifP) << 0;
|
|
Packit |
78deda |
l |= getcNofail(ifP) << 8;
|
|
Packit |
78deda |
l |= getcNofail(ifP) << 16;
|
|
Packit |
78deda |
l |= getcNofail(ifP) << 24;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*lP = l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_readlittlelong2(FILE * const ifP,
|
|
Packit |
78deda |
int32_t * const lP) {
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
long l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
rc = pm_readlittlelong(ifP, &l);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
assert((int32_t)l == l);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*lP = (int32_t)l;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return rc;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_writelittlelong(FILE * const ofP,
|
|
Packit |
78deda |
long const l) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
putc((l >> 0) & 0xff, ofP);
|
|
Packit |
78deda |
putc((l >> 8) & 0xff, ofP);
|
|
Packit |
78deda |
putc((l >> 16) & 0xff, ofP);
|
|
Packit |
78deda |
putc((l >> 24) & 0xff, ofP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
pm_readmagicnumber(FILE * const ifP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int ich1, ich2;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ich1 = getc(ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (ich1 == EOF)
|
|
Packit |
78deda |
pm_error("Error reading first byte of what is expected to be "
|
|
Packit |
78deda |
"a Netpbm magic number. "
|
|
Packit |
78deda |
"Most often, this means your input file is empty");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ich2 = getc(ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (ich2 == EOF)
|
|
Packit |
78deda |
pm_error("Error reading second byte of what is expected to be "
|
|
Packit |
78deda |
"a Netpbm magic number (the first byte was successfully "
|
|
Packit |
78deda |
"read as 0x%02x)", ich1);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return ich1 * 256 + ich2;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Read a file of unknown size to a buffer. Return the number of bytes
|
|
Packit |
78deda |
read. Allocate more memory as we need it. The calling routine has
|
|
Packit |
78deda |
to free() the buffer.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Oliver Trepte, oliver@fysik4.kth.se, 930613
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#define PM_BUF_SIZE 16384 /* First try this size of the buffer, then
|
|
Packit |
78deda |
double this until we reach PM_MAX_BUF_INC */
|
|
Packit |
78deda |
#define PM_MAX_BUF_INC 65536 /* Don't allocate more memory in larger blocks
|
|
Packit |
78deda |
than this. */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
char *
|
|
Packit |
78deda |
pm_read_unknown_size(FILE * const file,
|
|
Packit |
78deda |
long * const nread) {
|
|
Packit |
78deda |
long nalloc;
|
|
Packit |
78deda |
char * buf;
|
|
Packit |
78deda |
bool eof;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*nread = 0;
|
|
Packit |
78deda |
nalloc = PM_BUF_SIZE;
|
|
Packit |
78deda |
MALLOCARRAY(buf, nalloc);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
eof = FALSE; /* initial value */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
while(!eof) {
|
|
Packit |
78deda |
int val;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (*nread >= nalloc) { /* We need a larger buffer */
|
|
Packit |
78deda |
if (nalloc > PM_MAX_BUF_INC)
|
|
Packit |
78deda |
nalloc += PM_MAX_BUF_INC;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
nalloc += nalloc;
|
|
Packit |
78deda |
REALLOCARRAY_NOFAIL(buf, nalloc);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
val = getc(file);
|
|
Packit |
78deda |
if (val == EOF)
|
|
Packit |
78deda |
eof = TRUE;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
buf[(*nread)++] = val;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
return buf;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
union cheat {
|
|
Packit |
78deda |
uint32_t l;
|
|
Packit |
78deda |
short s;
|
|
Packit |
78deda |
unsigned char c[4];
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
short
|
|
Packit |
78deda |
pm_bs_short(short const s) {
|
|
Packit |
78deda |
union cheat u;
|
|
Packit |
78deda |
unsigned char t;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
u.s = s;
|
|
Packit |
78deda |
t = u.c[0];
|
|
Packit |
78deda |
u.c[0] = u.c[1];
|
|
Packit |
78deda |
u.c[1] = t;
|
|
Packit |
78deda |
return u.s;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
long
|
|
Packit |
78deda |
pm_bs_long(long const l) {
|
|
Packit |
78deda |
union cheat u;
|
|
Packit |
78deda |
unsigned char t;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
u.l = l;
|
|
Packit |
78deda |
t = u.c[0];
|
|
Packit |
78deda |
u.c[0] = u.c[3];
|
|
Packit |
78deda |
u.c[3] = t;
|
|
Packit |
78deda |
t = u.c[1];
|
|
Packit |
78deda |
u.c[1] = u.c[2];
|
|
Packit |
78deda |
u.c[2] = t;
|
|
Packit |
78deda |
return u.l;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_tell2(FILE * const fileP,
|
|
Packit |
78deda |
void * const fileposP,
|
|
Packit |
78deda |
unsigned int const fileposSize) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Return the current file position as *filePosP, which is a buffer
|
|
Packit |
78deda |
'fileposSize' bytes long. Abort the program if error, including if
|
|
Packit |
78deda |
*fileP isn't a file that has a position.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
/* Note: FTELLO() is either ftello() or ftell(), depending on the
|
|
Packit |
78deda |
capabilities of the underlying C library. It is defined in
|
|
Packit |
78deda |
pm_config.h. ftello(), in turn, may be either ftell() or
|
|
Packit |
78deda |
ftello64(), as implemented by the C library.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
pm_filepos const filepos = FTELLO(fileP);
|
|
Packit |
78deda |
if (filepos < 0)
|
|
Packit |
78deda |
pm_error("ftello() to get current file position failed. "
|
|
Packit |
78deda |
"Errno = %s (%d)\n", strerror(errno), errno);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (fileposSize == sizeof(pm_filepos)) {
|
|
Packit |
78deda |
pm_filepos * const fileposP_filepos = fileposP;
|
|
Packit |
78deda |
*fileposP_filepos = filepos;
|
|
Packit |
78deda |
} else if (fileposSize == sizeof(long)) {
|
|
Packit |
78deda |
if (sizeof(pm_filepos) > sizeof(long) &&
|
|
Packit |
78deda |
filepos >= (pm_filepos) 1 << (sizeof(long)*8))
|
|
Packit |
78deda |
pm_error("File size is too large to represent in the %u bytes "
|
|
Packit |
78deda |
"that were provided to pm_tell2()", fileposSize);
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
long * const fileposP_long = fileposP;
|
|
Packit |
78deda |
*fileposP_long = (long)filepos;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
} else
|
|
Packit |
78deda |
pm_error("File position size passed to pm_tell() is invalid: %u. "
|
|
Packit |
78deda |
"Valid sizes are %u and %u",
|
|
Packit |
78deda |
fileposSize, (unsigned int)sizeof(pm_filepos),
|
|
Packit |
78deda |
(unsigned int) sizeof(long));
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int
|
|
Packit |
78deda |
pm_tell(FILE * const fileP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
long filepos;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_tell2(fileP, &filepos, sizeof(filepos));
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return filepos;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_seek2(FILE * const fileP,
|
|
Packit |
78deda |
const pm_filepos * const fileposP,
|
|
Packit |
78deda |
unsigned int const fileposSize) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Position file *fileP to position *fileposP. Abort if error, including
|
|
Packit |
78deda |
if *fileP isn't a seekable file.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
if (fileposSize == sizeof(pm_filepos))
|
|
Packit |
78deda |
/* Note: FSEEKO() is either fseeko() or fseek(), depending on the
|
|
Packit |
78deda |
capabilities of the underlying C library. It is defined in
|
|
Packit |
78deda |
pm_config.h. fseeko(), in turn, may be either fseek() or
|
|
Packit |
78deda |
fseeko64(), as implemented by the C library.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
FSEEKO(fileP, *fileposP, SEEK_SET);
|
|
Packit |
78deda |
else if (fileposSize == sizeof(long)) {
|
|
Packit |
78deda |
long const fileposLong = *(long *)fileposP;
|
|
Packit |
78deda |
fseek(fileP, fileposLong, SEEK_SET);
|
|
Packit |
78deda |
} else
|
|
Packit |
78deda |
pm_error("File position size passed to pm_seek() is invalid: %u. "
|
|
Packit |
78deda |
"Valid sizes are %u and %u",
|
|
Packit |
78deda |
fileposSize, (unsigned int)sizeof(pm_filepos),
|
|
Packit |
78deda |
(unsigned int) sizeof(long));
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_seek(FILE * const fileP, unsigned long filepos) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_filepos fileposBuff;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
fileposBuff = filepos;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_seek2(fileP, &fileposBuff, sizeof(fileposBuff));
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_nextimage(FILE * const file, int * const eofP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Position the file 'file' to the next image in the stream, assuming it is
|
|
Packit |
78deda |
now positioned just after the current image. I.e. read off any white
|
|
Packit |
78deda |
space at the end of the current image's raster. Note that the raw formats
|
|
Packit |
78deda |
don't permit such white space, but this routine tolerates it anyway,
|
|
Packit |
78deda |
because the plain formats do permit white space after the raster.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Iff there is no next image, return *eofP == TRUE.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Note that in practice, we will not normally see white space here in
|
|
Packit |
78deda |
a plain PPM or plain PGM stream because the routine to read a
|
|
Packit |
78deda |
sample from the image reads one character of white space after the
|
|
Packit |
78deda |
sample in order to know where the sample ends. There is not
|
|
Packit |
78deda |
normally more than one character of white space (a newline) after
|
|
Packit |
78deda |
the last sample in the raster. But plain PBM is another story. No white
|
|
Packit |
78deda |
space is required between samples of a plain PBM image. But the raster
|
|
Packit |
78deda |
normally ends with a newline nonetheless. Since the sample reading code
|
|
Packit |
78deda |
will not have read that newline, it is there for us to read now.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
bool eof;
|
|
Packit |
78deda |
bool nonWhitespaceFound;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
eof = FALSE;
|
|
Packit |
78deda |
nonWhitespaceFound = FALSE;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
while (!eof && !nonWhitespaceFound) {
|
|
Packit |
78deda |
int c;
|
|
Packit |
78deda |
c = getc(file);
|
|
Packit |
78deda |
if (c == EOF) {
|
|
Packit |
78deda |
if (feof(file))
|
|
Packit |
78deda |
eof = TRUE;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
pm_error("File error on getc() to position to image");
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
if (!isspace(c)) {
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
nonWhitespaceFound = TRUE;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Have to put the non-whitespace character back in
|
|
Packit |
78deda |
the stream -- it's part of the next image.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
rc = ungetc(c, file);
|
|
Packit |
78deda |
if (rc == EOF)
|
|
Packit |
78deda |
pm_error("File error doing ungetc() "
|
|
Packit |
78deda |
"to position to image.");
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
*eofP = eof;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_check(FILE * const file,
|
|
Packit |
78deda |
enum pm_check_type const check_type,
|
|
Packit |
78deda |
pm_filepos const need_raster_size,
|
|
Packit |
78deda |
enum pm_check_code * const retval_p) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
This is not defined for use outside of libnetpbm.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
struct stat statbuf;
|
|
Packit |
78deda |
pm_filepos curpos; /* Current position of file; -1 if none */
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Note: FTELLO() is either ftello() or ftell(), depending on the
|
|
Packit |
78deda |
capabilities of the underlying C library. It is defined in
|
|
Packit |
78deda |
pm_config.h. ftello(), in turn, may be either ftell() or
|
|
Packit |
78deda |
ftello64(), as implemented by the C library.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
curpos = FTELLO(file);
|
|
Packit |
78deda |
if (curpos >= 0) {
|
|
Packit |
78deda |
/* This type of file has a current position */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
rc = fstat(fileno(file), &statbuf);
|
|
Packit |
78deda |
if (rc != 0)
|
|
Packit |
78deda |
pm_error("fstat() failed to get size of file, though ftello() "
|
|
Packit |
78deda |
"successfully identified\n"
|
|
Packit |
78deda |
"the current position. Errno=%s (%d)",
|
|
Packit |
78deda |
strerror(errno), errno);
|
|
Packit |
78deda |
else if (!S_ISREG(statbuf.st_mode)) {
|
|
Packit |
78deda |
/* Not a regular file; we can't know its size */
|
|
Packit |
78deda |
if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
pm_filepos const have_raster_size = statbuf.st_size - curpos;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (have_raster_size < need_raster_size)
|
|
Packit |
78deda |
pm_error("File has invalid format. The raster should "
|
|
Packit |
78deda |
"contain %u bytes, but\n"
|
|
Packit |
78deda |
"the file ends after only %u bytes.",
|
|
Packit |
78deda |
(unsigned int) need_raster_size,
|
|
Packit |
78deda |
(unsigned int) have_raster_size);
|
|
Packit |
78deda |
else if (have_raster_size > need_raster_size) {
|
|
Packit |
78deda |
if (retval_p) *retval_p = PM_CHECK_TOO_LONG;
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
if (retval_p) *retval_p = PM_CHECK_OK;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
} else
|
|
Packit |
78deda |
if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
void
|
|
Packit |
78deda |
pm_drain(FILE * const fileP,
|
|
Packit |
78deda |
unsigned int const limit,
|
|
Packit |
78deda |
unsigned int * const bytesReadP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read bytes from *fileP until EOF and return as *bytesReadP how many there
|
|
Packit |
78deda |
were.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
But don't read any more than 'limit'.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
This is a good thing to call after reading an input file to be sure you
|
|
Packit |
78deda |
didn't leave some input behind, which could mean you didn't properly
|
|
Packit |
78deda |
interpret the file.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
unsigned int bytesRead;
|
|
Packit |
78deda |
bool eof;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (bytesRead = 0, eof = false; !eof && bytesRead < limit;) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
rc = fgetc(fileP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
eof = (rc == EOF);
|
|
Packit |
78deda |
if (!eof)
|
|
Packit |
78deda |
++bytesRead;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
*bytesReadP = bytesRead;
|
|
Packit |
78deda |
}
|