diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
index e393931..e1f2d18 100644
--- a/converter/other/pnmtops.c
+++ b/converter/other/pnmtops.c
@@ -42,6 +42,8 @@
#include <unistd.h>
#include <assert.h>
#include <string.h>
+#include <errno.h>
+#include <signal.h>
#ifndef NOFLATE
#include <zlib.h>
#endif
@@ -52,6 +54,37 @@
#include "shhopt.h"
#include "nstring.h"
+
+
+static void
+setSignals() {
+/*----------------------------------------------------------------------------
+ Set up the process-global signal-related state.
+
+ Note that we can't rely on defaults, because much of this is inherited
+ from the process that forked and exec'ed this program.
+-----------------------------------------------------------------------------*/
+ /* See waitForChildren() for why we do this to SIGCHLD */
+
+ struct sigaction sigchldAction;
+ int rc;
+ sigset_t emptySet;
+
+ sigemptyset(&emptySet);
+
+ sigchldAction.sa_handler = SIG_DFL;
+ sigchldAction.sa_mask = emptySet;
+ sigchldAction.sa_flags = SA_NOCLDSTOP;
+
+ rc = sigaction(SIGCHLD, &sigchldAction, NULL);
+
+ if (rc != 0)
+ pm_error("sigaction() to set up signal environment failed, "
+ "errno = %d (%s)", errno, strerror(errno));
+}
+
+
+
struct cmdlineInfo {
/* All the information the user supplied in the command line,
in a form easy for the program to use.
@@ -256,21 +289,17 @@ parseCommandLine(int argc, const char ** argv,
validateCompDimension(width, 72, "-width value");
validateCompDimension(height, 72, "-height value");
- overflow2(width, 72);
cmdlineP->width = width * 72;
- overflow2(height, 72);
cmdlineP->height = height * 72;
if (imagewidthSpec) {
validateCompDimension(imagewidth, 72, "-imagewidth value");
- overflow2(imagewidth, 72);
cmdlineP->imagewidth = imagewidth * 72;
}
else
cmdlineP->imagewidth = 0;
if (imageheightSpec) {
- validateCompDimension(imageheight, 72, "-imageheight value");
- overflow2(imageheight, 72);
+ validateCompDimension(imagewidth, 72, "-imageheight value");
cmdlineP->imageheight = imageheight * 72;
}
else
@@ -339,6 +368,33 @@ basebasename(const char * const filespec) {
+static void
+writeFile(const unsigned char * const buffer,
+ size_t const writeCt,
+ const char * const name,
+ FILE * const ofP) {
+
+ size_t writtenCt;
+
+ writtenCt = fwrite(buffer, 1, writeCt, ofP);
+
+ if (writtenCt != writeCt)
+ pm_error("Error writing to %s output file", name);
+}
+
+
+
+static void
+writeFileChar(const char * const buffer,
+ size_t const writeCt,
+ const char * const name,
+ FILE * const ofP) {
+
+ writeFile((const unsigned char *)buffer, writeCt, name, ofP);
+}
+
+
+
#define MAX_FILTER_CT 10
/* The maximum number of filters this code is capable of applying */
@@ -498,18 +554,12 @@ flateFilter(FILE * const ifP,
*/
do {
unsigned int have;
- size_t bytesWritten;
strm.avail_out = chunkSz;
strm.next_out = out;
deflate(&strm, flush);
have = chunkSz - strm.avail_out;
- bytesWritten = fwrite(out, 1, have, ofP);
- if (ferror(ofP) || bytesWritten != have) {
- deflateEnd(&strm);
- pm_error("Error writing to internal pipe during "
- "flate compression.");
- }
+ writeFile(out, have, "flate filter", ofP);
} while (strm.avail_out == 0);
assert(strm.avail_in == 0); /* all input is used */
@@ -554,7 +604,7 @@ rlePutBuffer (unsigned int const repeat,
fputc(repeatitem, fP);
} else {
fputc(count - 1, fP);
- fwrite(itembuf, 1, count, fP);
+ writeFile(itembuf, count, "rlePutBuffer", fP);
}
}
@@ -677,23 +727,24 @@ asciiHexFilter(FILE * const ifP,
unsigned char inbuff[40], outbuff[81];
for (eof = false; !eof; ) {
- size_t bytesRead;
+ size_t readCt;
- bytesRead = fread(inbuff, 1, 40, ifP);
+ readCt = fread(inbuff, 1, 40, ifP);
- if (bytesRead == 0)
+ if (readCt == 0)
eof = true;
else {
unsigned int i;
- for (i = 0; i < bytesRead; ++i) {
+ for (i = 0; i < readCt; ++i) {
int const item = inbuff[i];
outbuff[i*2] = hexits[item >> 4];
outbuff[i*2+1] = hexits[item & 15];
}
}
- outbuff[bytesRead * 2] = '\n';
- fwrite(outbuff, 1, bytesRead*2 + 1, ofP);
+ outbuff[readCt * 2] = '\n';
+
+ writeFile(outbuff, readCt * 2 + 1, "asciiHex filter", ofP);
}
fclose(ifP);
@@ -731,7 +782,8 @@ ascii85Filter(FILE * const ifP,
++count;
if (value == 0 && count == 4) {
- putchar('z'); /* Ascii85 encoding z exception */
+ writeFileChar("z", 1, "ASCII 85 filter", ofP);
+ /* Ascii85 encoding z exception */
++outcount;
count = 0;
} else if (count == 4) {
@@ -741,13 +793,14 @@ ascii85Filter(FILE * const ifP,
outbuff[1] = value % 85 + 33;
outbuff[0] = value / 85 + 33;
- fwrite(outbuff, 1, count + 1, ofP);
+ writeFileChar(outbuff, count + 1, "ASCII 85 filter", ofP);
+
count = value = 0;
outcount += 5;
}
if (outcount > 75) {
- putchar('\n');
+ writeFileChar("\n", 1, "ASCII 85 filter", ofP);
outcount = 0;
}
}
@@ -763,7 +816,7 @@ ascii85Filter(FILE * const ifP,
outbuff[0] = value / 85 + 33;
outbuff[count + 1] = '\n';
- fwrite(outbuff, 1, count + 2, ofP);
+ writeFileChar(outbuff, count + 2, "ASCII 85 filter", ofP);
}
fclose(ifP);
@@ -873,13 +926,22 @@ addFilter(const char * const description,
OutputEncoder * const oeP,
FILE ** const feedFilePP,
pid_t * const pidList) {
+/*----------------------------------------------------------------------------
+ Add a filter to the front of the chain.
- pid_t pid;
+ Spawn a process to do the filtering, by running function 'filter'.
+
+ *feedFilePP is the present head of the chain. We make the new filter
+ process write its output to that and get its input from a new pipe.
+ We update *feedFilePP to the sending end of the new pipe.
+ Add to the list pidList[] the PID of the process we spawn.
+-----------------------------------------------------------------------------*/
FILE * const oldFeedFileP = *feedFilePP;
FILE * newFeedFileP;
-
+ pid_t pid;
+
spawnFilter(oldFeedFileP, filter, oeP, &newFeedFileP, &pid);
if (verbose)
@@ -887,7 +949,7 @@ addFilter(const char * const description,
description, (unsigned)pid);
fclose(oldFeedFileP); /* Child keeps this open now */
-
+
addToPidList(pidList, pid);
*feedFilePP = newFeedFileP;
@@ -949,6 +1011,15 @@ waitForChildren(const pid_t * const pidList) {
Wait for all child processes with PIDs in pidList[] to exit.
In pidList[], end-of-list is marked with a special zero value.
-----------------------------------------------------------------------------*/
+ /* There's an odd behavior in Unix such that if you have set the
+ action for SIGCHLD to ignore the signal (even though ignoring the
+ signal is the default), the process' children do not become
+ zombies. Consequently, waitpid() always fails with ECHILD - but
+ nonetheless waits for the child to exit.
+
+ We expect the process not to have the action for SIGCHLD set that
+ way.
+ */
unsigned int i;
for (i = 0; pidList[i]; ++i) {
@@ -960,7 +1031,8 @@ waitForChildren(const pid_t * const pidList) {
rc = waitpid(pidList[i], &status, 0);
if (rc == -1)
- pm_error ("waitpid() for child %u failed", i);
+ pm_error ("waitpid() for child %u failed, errno=%d (%s)",
+ i, errno, strerror(errno));
else if (status != EXIT_SUCCESS)
pm_error ("Child process %u terminated abnormally", i);
}
@@ -1724,7 +1796,7 @@ convertRowPbm(struct pam * const pamP,
bitrow[colChars-1] <<= padRight; /* right edge */
}
- fwrite(bitrow, 1, colChars, fP);
+ writeFile(bitrow, colChars, "PBM reader", fP);
}
@@ -1867,6 +1939,21 @@ convertRaster(struct pam * const inpamP,
+/* FILE MANAGEMENT: File management is pretty hairy here. A filter, which
+ runs in its own process, needs to be able to cause its output file to
+ close because it might be an internal pipe and the next stage needs to
+ know output is done. So the forking process must close its copy of the
+ file descriptor. BUT: if the output of the filter is not an internal
+ pipe but this program's output, then we don't want it closed when the
+ filter terminates because we'll need it to be open for the next image
+ the program converts (with a whole new chain of filters).
+
+ To prevent the progam output file from getting closed, we pass a
+ duplicate of it to spawnFilters() and keep the original open.
+*/
+
+
+
static void
convertPage(FILE * const ifP,
int const turnflag,
@@ -1954,7 +2041,7 @@ convertPage(FILE * const ifP,
fflush(stdout);
filterChainOfP = fdopen(dup(fileno(stdout)), "w");
- /* spawnFilters() closes this. See FILE MANAGEMENT above */
+ /* spawnFilters() closes this. See FILE MANAGEMENT above */
spawnFilters(filterChainOfP, &oe, &feedFileP, filterPidList);
@@ -1979,6 +2066,8 @@ main(int argc, const char * argv[]) {
pm_proginit(&argc, argv);
+ setSignals();
+
parseCommandLine(argc, argv, &cmdline);
verbose = cmdline.verbose;
@@ -1994,6 +2083,13 @@ main(int argc, const char * argv[]) {
name = strdup("noname");
else
name = basebasename(cmdline.inputFileName);
+
+ /* This program manages file descriptors in a way that assumes
+ that new files will get file descriptor numbers less than 10,
+ so we close superfluous files now to make sure that's true.
+ */
+ closeAllBut(fileno(ifP), fileno(stdout), fileno(stderr));
+
{
int eof; /* There are no more images in the input file */
unsigned int imageSeq;