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 #include #include +#include +#include #ifndef NOFLATE #include #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;