about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/Makefile42
-rw-r--r--editor/pamcut.c5
-rw-r--r--editor/pamenlarge.test2
-rw-r--r--editor/pnmcrop.c246
-rw-r--r--editor/pnmcut.c427
-rw-r--r--editor/pnminvert.test2
-rw-r--r--editor/pnmscale.c748
-rw-r--r--editor/specialty/Makefile55
-rw-r--r--editor/specialty/pamdeinterlace.c (renamed from editor/pamdeinterlace.c)0
-rw-r--r--editor/specialty/pammixinterlace.c (renamed from editor/pammixinterlace.c)0
-rw-r--r--editor/specialty/pamoil.c (renamed from editor/pamoil.c)0
-rw-r--r--editor/specialty/pampop9.c (renamed from editor/pampop9.c)0
-rw-r--r--editor/specialty/pbmlife.c (renamed from editor/pbmlife.c)0
-rw-r--r--editor/specialty/pgmabel.c (renamed from editor/pgmabel.c)0
-rw-r--r--editor/specialty/pgmbentley.c (renamed from editor/pgmbentley.c)0
-rw-r--r--editor/specialty/pgmmorphconv.c (renamed from editor/pgmmorphconv.c)0
-rw-r--r--editor/specialty/pnmindex.c (renamed from editor/pnmindex.c)0
-rw-r--r--editor/specialty/ppm3d.c (renamed from editor/ppm3d.c)0
-rw-r--r--editor/specialty/ppmglobe.c (renamed from editor/ppmglobe.c)0
-rw-r--r--editor/specialty/ppmntsc.c (renamed from editor/ppmntsc.c)0
-rw-r--r--editor/specialty/ppmrelief.c (renamed from editor/ppmrelief.c)0
-rw-r--r--editor/specialty/ppmshift.c (renamed from editor/ppmshift.c)0
-rw-r--r--editor/specialty/ppmspread.c (renamed from editor/ppmspread.c)0
-rw-r--r--editor/specialty/ppmtv.c (renamed from editor/ppmtv.c)0
24 files changed, 224 insertions, 1303 deletions
diff --git a/editor/Makefile b/editor/Makefile
index 842cf31a..784a75a4 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -7,6 +7,8 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
+SUBDIRS = specialty
+
 # We tend to separate out the build targets so that we don't have
 # any more dependencies for a given target than it really needs.
 # That way, if there is a problem with a dependency, we can still
@@ -15,24 +17,23 @@ include $(BUILDDIR)/config.mk
 # build.
 
 PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
-	       pamdeinterlace pamdice pamditherbw pamedge \
+	       pamdice pamditherbw pamedge \
 	       pamenlarge \
-	       pamflip pamfunc pammasksharpen pammixinterlace \
-	       pamoil pamperspective pampop9 \
+	       pamflip pamfunc pammasksharpen \
+	       pamperspective \
 	       pamscale pamstretch pamthreshold pamundice \
-	       pbmclean pbmlife pbmmask pbmpscale pbmreduce \
-	       pgmabel pgmbentley pgmdeshadow pgmenhance \
-	       pgmmedian pgmmorphconv \
-	       pnmalias pnmcat pnmcomp pnmconvol pnmcrop pnmcut \
+	       pbmclean pbmmask pbmpscale pbmreduce \
+	       pgmdeshadow pgmenhance \
+	       pgmmedian \
+	       pnmalias pnmcat pnmcomp pnmconvol pnmcrop \
 	       pnmgamma \
-	       pnmhisteq pnmindex pnminvert pnmmontage \
+	       pnmhisteq pnminvert pnmmontage \
 	       pnmnlfilt pnmnorm pnmpad pnmpaste \
 	       pnmremap pnmrotate \
-	       pnmscale pnmscalefixed pnmshear pnmsmooth pnmstitch pnmtile \
-	       ppm3d ppmbrighten ppmchange ppmcolormask \
+	       pnmscalefixed pnmshear pnmsmooth pnmstitch pnmtile \
+	       ppmbrighten ppmchange ppmcolormask \
 	       ppmdim ppmdist ppmdither ppmdraw \
-	       ppmflash ppmglobe ppmlabel ppmmix \
-	       ppmntsc ppmrelief ppmshift ppmspread ppmtv
+	       ppmflash ppmlabel ppmmix \
 
 # We don't include programs that have special library dependencies in the
 # merge scheme, because we don't want those dependencies to prevent us
@@ -51,7 +52,7 @@ OBJECTS = $(BINARIES:%=%.o)
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 
 .PHONY: all
-all: $(BINARIES)
+all: $(BINARIES) $(SUBDIRS:%=%/all)
 
 include $(SRCDIR)/common.mk
 
@@ -60,15 +61,10 @@ install.bin: install.bin.local
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
-# backward compatibility: program used to be named pnmnoraw
 # backward compatibility: program used to be pnminterp
 	cd $(PKGDIR)/bin ; \
 	rm -f pnminterp; \
 	$(SYMLINK) pamstretch$(EXE) pnminterp
-# pamoil replaced pgmoil in June 2001.
-	cd $(PKGDIR)/bin ; \
-	rm -f pgmoil ; \
-	$(SYMLINK) pamoil$(EXE) pgmoil
 # In March 2002, pnmnorm replaced ppmnorm and pgmnorm
 	cd $(PKGDIR)/bin ; \
 	rm -f ppmnorm ; \
@@ -84,3 +80,13 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	rm -f pnmenlarge ; \
 	$(SYMLINK) pamenlarge$(EXE) pnmenlarge
+# In March 2009, pamcut replaced pnmcut (but pamcut is much older -- pnmcut
+# was obsoleted by pamcut long before this).
+	cd $(PKGDIR)/bin ; \
+	rm -f pnmcut ; \
+	$(SYMLINK) pamcut$(EXE) pnmcut
+# In March 2009, pamscale replaced pnmscale (but pamscale is much older --
+# pnmscale was obsoleted by pamscale long before this).
+	cd $(PKGDIR)/bin ; \
+	rm -f pnmscale ; \
+	$(SYMLINK) pamscale$(EXE) pnmscale
diff --git a/editor/pamcut.c b/editor/pamcut.c
index ce63da7b..d2d6170c 100644
--- a/editor/pamcut.c
+++ b/editor/pamcut.c
@@ -563,7 +563,7 @@ extractRowsPBM(const struct pam * const inpamP,
     int             row;
     unsigned int    totalWidth;
 
-    assert(0 <= leftcol && leftcol <= rightcol && rightcol < inpamP->width);
+    assert(leftcol <= rightcol);
     assert(toprow <= bottomrow);
 
     if (leftcol > 0) {
@@ -658,8 +658,7 @@ cutOneImage(FILE *             const ifP,
 
     pnm_writepaminit(&outpam);
 
-    if (PNM_FORMAT_TYPE(outpam.format) == PBM_TYPE &&
-        leftcol >= 0 && rightcol < inpam.width)
+    if (PNM_FORMAT_TYPE(outpam.format) == PBM_TYPE)
         extractRowsPBM(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow);
     else
         extractRowsGen(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow);
diff --git a/editor/pamenlarge.test b/editor/pamenlarge.test
index a2221d4d..7584af01 100644
--- a/editor/pamenlarge.test
+++ b/editor/pamenlarge.test
@@ -1,6 +1,6 @@
 echo Test 1.  Should print 3424505894 913236
 ./pamenlarge 3 ../testimg.ppm | cksum
-echo Test 2.  Should print 2940246561 304422
+echo Test 2.  Should print 4152147096 304422
 ppmtopgm ../testimg.ppm | ./pamenlarge 3 | cksum
 echo Test 3.  Should print 3342398172 297
 ./pamenlarge 3 ../testgrid.pbm | cksum
diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c
index e9dcbe4a..c2e07805 100644
--- a/editor/pnmcrop.c
+++ b/editor/pnmcrop.c
@@ -40,7 +40,17 @@ static const char * const edgeName[] = {
     "bottom"
 };
 
+typedef struct {
+    unsigned int size[4];
+} borderSet;
 
+typedef enum {
+    /* A position in a PNM image file stream */
+    FILEPOS_BEG,
+        /* Immediately before the raster */
+    FILEPOS_END
+        /* Immediately after the raster */
+} imageFilePos;
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -155,6 +165,11 @@ typedef struct {
 } cropOp;
 
 
+typedef struct {
+    cropOp op[4];
+} cropSet;
+
+
 
 static xel
 background3Corners(FILE * const ifP,
@@ -223,8 +238,7 @@ computeBackground(FILE *         const ifP,
                   int            const rows,
                   xelval         const maxval,
                   int            const format,
-                  enum bg_choice const backgroundChoice,
-                  bool           const verbose) {
+                  enum bg_choice const backgroundChoice) {
 /*----------------------------------------------------------------------------
    Determine what color is the background color of the image in file
    *ifP, which is described by 'cols', 'rows', 'maxval', and 'format'.
@@ -254,11 +268,6 @@ computeBackground(FILE *         const ifP,
         break;
     }
 
-    if (verbose) {
-        pixel const backgroundPixel = pnm_xeltopixel(background, format);
-        pm_message("Background color is %s", 
-                   ppm_colorname(&backgroundPixel, maxval, TRUE /*hexok*/));
-    }
     return(background);
 }
 
@@ -271,9 +280,8 @@ findBordersInImage(FILE *         const ifP,
                    xelval         const maxval,
                    int            const format,
                    xel            const backgroundColor,
-                   bool           const verbose, 
                    bool *         const hasBordersP,
-                   unsigned int * const borderSize) {
+                   borderSet *    const borderSizeP) {
 /*----------------------------------------------------------------------------
    Find the left, right, top, and bottom borders in the image 'ifP'.
    Return their sizes in pixels as borderSize[n].
@@ -335,38 +343,58 @@ findBordersInImage(FILE *         const ifP,
     else {
         *hasBordersP = TRUE;
         assert(right <= cols); assert(bottom <= rows);
-        borderSize[LEFT]   = left - 0;
-        borderSize[RIGHT]  = cols - right;
-        borderSize[TOP]    = top - 0;
-        borderSize[BOTTOM] = rows - bottom;
+        borderSizeP->size[LEFT]   = left - 0;
+        borderSizeP->size[RIGHT]  = cols - right;
+        borderSizeP->size[TOP]    = top - 0;
+        borderSizeP->size[BOTTOM] = rows - bottom;
     }
 }
 
 
 
 static void
-findBordersInFile(unsigned int   const icols,
-                  unsigned int   const irows,
-                  FILE *         const borderFileP, 
-                  xel            const backgroundColor,
-                  bool           const verbose, 
-                  bool *         const hasBordersP,
-                  unsigned int * const borderSize) {
-
-    int bcols;  /* cols and rows in the borderfile */
-    int brows;
-    xelval maxval;
-    int format;
-    
-    pnm_readpnminit(borderFileP, &bcols, &brows, &maxval, &format);
+analyzeImage(FILE *         const ifP,
+             unsigned int   const cols,
+             unsigned int   const rows,
+             xelval         const maxval,
+             int            const format,
+             enum bg_choice const backgroundReq,
+             imageFilePos   const newFilePos,
+             xel *          const backgroundColorP,
+             bool *         const hasBordersP,
+             borderSet *    const borderSizeP) {
+/*----------------------------------------------------------------------------
+   Analyze the PNM image on file stream *ifP to determine its borders
+   and the color of those borders (the assumed background color).
 
-    if (bcols != icols || brows != irows)
-      pm_error("Image file and border file differ in size: %ux%u %ux%u",
-           icols, irows, bcols, brows);
-    
-    findBordersInImage(borderFileP, bcols, brows, maxval, format, 
-                       backgroundColor, verbose, hasBordersP, borderSize);
-} 
+   Return as *backgroundColorP the background color.
+
+   Return as *borderSizeP the set of border sizes (one for each of the
+   four edges).  But iff there are no borders, don't return anything as
+   *borderSizeP and return *hasBordersP == false.
+
+   Expect *ifP to be positioned right after the header and seekable.
+   Return with it positioned either before or after the raster, as
+   requested by 'newFilePos'.
+-----------------------------------------------------------------------------*/
+    pm_filepos rasterpos;
+    xel background;
+
+    pm_tell2(ifP, &rasterpos, sizeof(rasterpos));
+
+    background = computeBackground(ifP, cols, rows, maxval, format,
+                                   backgroundReq);
+
+    pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
+
+    findBordersInImage(ifP, cols, rows, maxval, format, 
+                       background, hasBordersP, borderSizeP);
+
+    if (newFilePos == FILEPOS_BEG)
+        pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
+
+    *backgroundColorP = background;
+}
 
 
 
@@ -379,21 +407,22 @@ ending(unsigned int const n) {
 
 
 static void
-reportCroppingParameters(cropOp const crop[]) {
+reportCroppingParameters(cropSet const crop) {
 
     unsigned int i;
 
     for (i = 0; i < 4; ++i) {
-        if (crop[i].removeSize == 0 && crop[i].padSize == 0)
+        if (crop.op[i].removeSize == 0 && crop.op[i].padSize == 0)
             pm_message("Not cropping %s edge", edgeName[i]);
         else {
-            if (crop[i].padSize > 0)
+            if (crop.op[i].padSize > 0)
                 pm_message("Adding %u pixel%s to the %s border",
-                           crop[i].padSize, ending(crop[i].padSize),
+                           crop.op[i].padSize, ending(crop.op[i].padSize),
                            edgeName[i]);
-            if (crop[i].removeSize > 0)
+            if (crop.op[i].removeSize > 0)
                 pm_message("Cropping %u pixel%s from the %s border",
-                           crop[i].removeSize, ending(crop[i].removeSize),
+                           crop.op[i].removeSize,
+                           ending(crop.op[i].removeSize),
                            edgeName[i]);
         }
     }
@@ -467,7 +496,7 @@ writeCroppedNonPbm(FILE *       const ifP,
                    unsigned int const rows,
                    xelval       const maxval,
                    int          const format,
-                   cropOp       const crop[],
+                   cropSet      const crop,
                    xel          const backgroundColor,
                    FILE *       const ofP) {
 
@@ -511,22 +540,23 @@ writeCroppedNonPbm(FILE *       const ifP,
     */
 
     unsigned int const foregroundCols =
-        cols - crop[LEFT].removeSize - crop[RIGHT].removeSize;
+        cols - crop.op[LEFT].removeSize - crop.op[RIGHT].removeSize;
     unsigned int const outputCols     = 
-        foregroundCols + crop[LEFT].padSize + crop[RIGHT].padSize;
+        foregroundCols + crop.op[LEFT].padSize + crop.op[RIGHT].padSize;
     unsigned int const foregroundRows =
-        rows - crop[TOP].removeSize - crop[BOTTOM].removeSize;
+        rows - crop.op[TOP].removeSize - crop.op[BOTTOM].removeSize;
     unsigned int const outputRows     =
-        foregroundRows + crop[TOP].padSize + crop[BOTTOM].padSize;
+        foregroundRows + crop.op[TOP].padSize + crop.op[BOTTOM].padSize;
 
     unsigned int const foregroundLeft  =
-        MAX(crop[LEFT].removeSize, crop[LEFT].padSize);
+        MAX(crop.op[LEFT].removeSize, crop.op[LEFT].padSize);
         /* Index into xelrow[] of leftmost pixel of foreground */
     unsigned int const foregroundRight = foregroundLeft + foregroundCols;
         /* Index into xelrow[] just past rightmost pixel of foreground */
 
     unsigned int const allocCols =
-        foregroundRight + MAX(crop[RIGHT].removeSize, crop[RIGHT].padSize);
+        foregroundRight + MAX(crop.op[RIGHT].removeSize,
+                              crop.op[RIGHT].padSize);
 
     xel * xelrow;
     unsigned int i;
@@ -535,33 +565,35 @@ writeCroppedNonPbm(FILE *       const ifP,
 
     xelrow = pnm_allocrow(allocCols);
 
-    readOffBorderNonPbm(crop[TOP].removeSize, ifP, cols, maxval, format);
+    readOffBorderNonPbm(crop.op[TOP].removeSize, ifP, cols, maxval, format);
 
-    outputNewBorderNonPbm(crop[TOP].padSize, outputCols, backgroundColor,
+    outputNewBorderNonPbm(crop.op[TOP].padSize, outputCols, backgroundColor,
                           ofP, maxval, format);
 
     /* Set left border pixels */
-    fillRow(&xelrow[foregroundLeft - crop[LEFT].padSize], crop[LEFT].padSize,
+    fillRow(&xelrow[foregroundLeft - crop.op[LEFT].padSize],
+            crop.op[LEFT].padSize,
             backgroundColor);
 
     /* Set right border pixels */
-    fillRow(&xelrow[foregroundRight], crop[RIGHT].padSize, backgroundColor);
+    fillRow(&xelrow[foregroundRight], crop.op[RIGHT].padSize, backgroundColor);
 
     /* Read and output foreground rows */
     for (i = 0; i < foregroundRows; ++i) {
  
         /* Read foreground pixels */
-        pnm_readpnmrow(ifP, &(xelrow[foregroundLeft - crop[LEFT].removeSize]),
+        pnm_readpnmrow(ifP,
+                       &(xelrow[foregroundLeft - crop.op[LEFT].removeSize]),
                        cols, maxval, format);
         
         pnm_writepnmrow(ofP,
-                        &(xelrow[foregroundLeft - crop[LEFT].padSize]),
+                        &(xelrow[foregroundLeft - crop.op[LEFT].padSize]),
                         outputCols, maxval, format, 0);
     }
 
-    readOffBorderNonPbm(crop[BOTTOM].removeSize, ifP, cols, maxval, format);
+    readOffBorderNonPbm(crop.op[BOTTOM].removeSize, ifP, cols, maxval, format);
     
-    outputNewBorderNonPbm(crop[BOTTOM].padSize, outputCols,
+    outputNewBorderNonPbm(crop.op[BOTTOM].padSize, outputCols,
                           backgroundColor,
                           ofP, maxval, format);
 
@@ -642,7 +674,7 @@ writeCroppedPBM(FILE *       const ifP,
                 unsigned int const cols,
                 unsigned int const rows,
                 int          const format,
-                cropOp       const crop[],
+                cropSet      const crop,
                 xel          const backgroundColor,
                 FILE *       const ofP) {
     
@@ -651,26 +683,28 @@ writeCroppedPBM(FILE *       const ifP,
     */
 
     unsigned int const foregroundCols =
-        cols - crop[LEFT].removeSize - crop[RIGHT].removeSize;
+        cols - crop.op[LEFT].removeSize - crop.op[RIGHT].removeSize;
     unsigned int const outputCols     = 
-        foregroundCols + crop[LEFT].padSize + crop[RIGHT].padSize;
+        foregroundCols + crop.op[LEFT].padSize + crop.op[RIGHT].padSize;
     unsigned int const foregroundRows =
-        rows - crop[TOP].removeSize - crop[BOTTOM].removeSize;
+        rows - crop.op[TOP].removeSize - crop.op[BOTTOM].removeSize;
     unsigned int const outputRows     =
-        foregroundRows + crop[TOP].padSize + crop[BOTTOM].padSize;
+        foregroundRows + crop.op[TOP].padSize + crop.op[BOTTOM].padSize;
 
     unsigned int const foregroundLeft  =
-        MAX(crop[LEFT].removeSize, crop[LEFT].padSize);
+        MAX(crop.op[LEFT].removeSize, crop.op[LEFT].padSize);
     unsigned int const foregroundRight = foregroundLeft + foregroundCols;
 
     unsigned int const allocCols =
-        foregroundRight + MAX(crop[RIGHT].removeSize, crop[RIGHT].padSize);
+        foregroundRight + 
+        MAX(crop.op[RIGHT].removeSize, crop.op[RIGHT].padSize);
 
     unsigned int const backgroundBlackWhite =
         PNM_EQUAL(backgroundColor, pnm_whitexel(1, PBM_TYPE)) ? 0: 1;
 
-    unsigned int const readOffset    = foregroundLeft - crop[LEFT].removeSize;
-    unsigned int const writeOffset   = foregroundLeft - crop[LEFT].padSize;
+    unsigned int const readOffset    =
+        foregroundLeft - crop.op[LEFT].removeSize;
+    unsigned int const writeOffset   = foregroundLeft - crop.op[LEFT].padSize;
     unsigned int const lastWriteChar = writeOffset/8 + (outputCols-1)/8;
     unsigned char * bitrow;
     unsigned int i;
@@ -679,9 +713,9 @@ writeCroppedPBM(FILE *       const ifP,
 
     bitrow = pbm_allocrow_packed(allocCols);
 
-    readOffBorderPbm(crop[TOP].removeSize, ifP, cols, format);
+    readOffBorderPbm(crop.op[TOP].removeSize, ifP, cols, format);
 
-    outputNewBorderPbm(crop[TOP].padSize, outputCols, backgroundBlackWhite,
+    outputNewBorderPbm(crop.op[TOP].padSize, outputCols, backgroundBlackWhite,
                        ofP);
 
     /* Prepare padding: left and/or right */
@@ -699,13 +733,14 @@ writeCroppedPBM(FILE *       const ifP,
            distorted by pbm_writepbmrow_bitoffset() 
            (No need to mend any left-side padding)
         */
-        if (crop[RIGHT].padSize > 0)    
+        if (crop.op[RIGHT].padSize > 0)    
             bitrow[lastWriteChar] = backgroundBlackWhite * 0xff;
     }
 
-    readOffBorderPbm(crop[BOTTOM].removeSize, ifP, cols, format);
+    readOffBorderPbm(crop.op[BOTTOM].removeSize, ifP, cols, format);
 
-    outputNewBorderPbm(crop[BOTTOM].padSize, outputCols, backgroundBlackWhite,
+    outputNewBorderPbm(crop.op[BOTTOM].padSize, outputCols,
+                       backgroundBlackWhite,
                        ofP);
 
     pbm_freerow_packed(bitrow);
@@ -715,23 +750,25 @@ writeCroppedPBM(FILE *       const ifP,
 
 static void
 determineCrops(struct cmdlineInfo const cmdline,
-               unsigned int       const oldBorderSize[],
-               cropOp *           const cropArray) {
+               borderSet *        const oldBorderSizeP,
+               cropSet *          const cropP) {
 
     edgeLocation i;
 
     for (i = 0; i < 4; ++i) {
         if (cmdline.wantCrop[i]) {
-            if (oldBorderSize[i] > cmdline.margin) {
-                cropArray[i].removeSize = oldBorderSize[i] - cmdline.margin;
-                cropArray[i].padSize    = 0;
+            if (oldBorderSizeP->size[i] > cmdline.margin) {
+                cropP->op[i].removeSize =
+                    oldBorderSizeP->size[i] - cmdline.margin;
+                cropP->op[i].padSize    = 0;
             } else {
-                cropArray[i].removeSize = 0;
-                cropArray[i].padSize    = cmdline.margin - oldBorderSize[i];
+                cropP->op[i].removeSize = 0;
+                cropP->op[i].padSize    =
+                    cmdline.margin - oldBorderSizeP->size[i];
             }
         } else {
-            cropArray[i].removeSize = 0;
-            cropArray[i].padSize    = 0;
+            cropP->op[i].removeSize = 0;
+            cropP->op[i].padSize    = 0;
         }
     }
 }
@@ -741,15 +778,15 @@ determineCrops(struct cmdlineInfo const cmdline,
 static void
 validateComputableSize(unsigned int const cols,
                        unsigned int const rows,
-                       cropOp       const crop[]) {
+                       cropSet      const crop) {
 
     double const newcols =
         (double)cols +
-        (double)crop[LEFT].padSize + (double)crop[RIGHT].padSize;
+        (double)crop.op[LEFT].padSize + (double)crop.op[RIGHT].padSize;
 
     double const newrows =
         (double)rows +
-        (double)crop[TOP].padSize + (double)crop[BOTTOM].padSize;
+        (double)crop.op[TOP].padSize + (double)crop.op[BOTTOM].padSize;
 
     if (newcols > INT_MAX)
        pm_error("Output width too large: %.0f.", newcols);
@@ -771,50 +808,48 @@ cropOneImage(struct cmdlineInfo const cmdline,
    (the file that tells us where the existing borders are in the input
    image).  Leave *ifP and *bdfP positioned after the image.
 
-   *ifP is seekable; *bdfP may not be.
+   Both files are seekable.
 -----------------------------------------------------------------------------*/
-    xelval maxval;
-    int format;
-    int rows, cols;   /* dimensions of input image */
+    xelval maxval, bmaxval;
+    int format, bformat;
+    int rows, cols, brows, bcols;
     bool hasBorders;
-    unsigned int oldBorder[4];
+    borderSet oldBorder;
         /* The sizes of the borders in the input image */
-    cropOp crop[4];
+    cropSet crop;
         /* The crops we have to do on each side */
-    
     xel background;
-    pm_filepos rasterpos;
 
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
 
-    pm_tell2(ifP, &rasterpos, sizeof(rasterpos));
-
-    background = computeBackground(ifP, cols, rows, maxval, format,
-                                   cmdline.background, cmdline.verbose);
+    if (bdfP)
+        pnm_readpnminit(bdfP, &bcols, &brows, &bmaxval, &bformat);
 
-    if (cmdline.borderfile) {
-        findBordersInFile(cols, rows, bdfP, background,
-                          cmdline.verbose, &hasBorders, oldBorder);
-    } else {
-        pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
+    if (bdfP)
+        analyzeImage(bdfP, bcols, brows, bmaxval, bformat, cmdline.background,
+                     FILEPOS_END,
+                     &background, &hasBorders, &oldBorder);
+    else
+        analyzeImage(ifP, cols, rows, maxval, format, cmdline.background,
+                     FILEPOS_BEG,
+                     &background, &hasBorders, &oldBorder);
 
-        findBordersInImage(ifP, cols, rows, maxval, format, 
-                           background, cmdline.verbose, &hasBorders,
-                           oldBorder);
+    if (cmdline.verbose) {
+        pixel const backgroundPixel = pnm_xeltopixel(background, format);
+        pm_message("Background color is %s", 
+                   ppm_colorname(&backgroundPixel, maxval, TRUE /*hexok*/));
     }
     if (!hasBorders)
         pm_error("The image is entirely background; "
                  "there is nothing to crop.");
 
-    determineCrops(cmdline, oldBorder, crop);
+    determineCrops(cmdline, &oldBorder, &crop);
 
     validateComputableSize(cols, rows, crop);
 
     if (cmdline.verbose) 
         reportCroppingParameters(crop);
 
-    pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
-
     if (PNM_FORMAT_TYPE(format) == PBM_TYPE)
         writeCroppedPBM(ifP, cols, rows, format, crop, background, ofP);
     else
@@ -832,7 +867,8 @@ main(int argc, const char *argv[]) {
         /* The program's regular input file.  Could be a seekable copy of
            it in a temporary file.
         */
-    FILE * bdfP; /* The borderfile; NULL if none.  No seeks.  */
+    FILE * bdfP;
+        /* The border file.  NULL if none. */
     bool eof;    /* no more images in input stream */
     bool beof;   /* no more images in borderfile stream */
 
diff --git a/editor/pnmcut.c b/editor/pnmcut.c
deleted file mode 100644
index a21fcffb..00000000
--- a/editor/pnmcut.c
+++ /dev/null
@@ -1,427 +0,0 @@
- /* pnmcut.c - cut a rectangle out of a portable anymap
-**
-** Copyright (C) 1989 by Jef Poskanzer.
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
-*/
-
-#include <limits.h>
-#include "pnm.h"
-#include "shhopt.h"
-
-#define UNSPEC INT_MAX
-    /* UNSPEC is the value we use for an argument that is not specified
-       by the user.  Theoretically, the user could specify this value,
-       but we hope not.
-       */
-
-struct cmdline_info {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *input_filespec;  /* Filespecs of input files */
-
-    /* The following describe the rectangle the user wants to cut out. 
-       the value UNSPEC for any of them indicates that value was not
-       specified.  A negative value means relative to the far edge.
-       'width' and 'height' are not negative.  These specifications 
-       do not necessarily describe a valid rectangle; they are just
-       what the user said.
-       */
-    int left;
-    int right;
-    int top;
-    int bottom;
-    int width;
-    int height;
-    int pad;
-
-    int verbose;
-};
-
-
-
-static xel black_xel;  /* A black xel */
-
-
-static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
-        /* Instructions to OptParseOptions2 on how to parse our options.
-         */
-    optStruct2 opt;
-
-    unsigned int option_def_index;
-
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "left",       OPT_INT,    &cmdline_p->left,           0);
-    OPTENTRY(0,   "right",      OPT_INT,    &cmdline_p->right,          0);
-    OPTENTRY(0,   "top",        OPT_INT,    &cmdline_p->top,            0);
-    OPTENTRY(0,   "bottom",     OPT_INT,    &cmdline_p->bottom,         0);
-    OPTENTRY(0,   "width",      OPT_INT,    &cmdline_p->width,          0);
-    OPTENTRY(0,   "height",     OPT_INT,    &cmdline_p->height,         0);
-    OPTENTRY(0,   "pad",        OPT_FLAG,   &cmdline_p->pad,            0);
-    OPTENTRY(0,   "verbose",    OPT_FLAG,   &cmdline_p->verbose,        0);
-
-    /* Set the defaults */
-    cmdline_p->left = UNSPEC;
-    cmdline_p->right = UNSPEC;
-    cmdline_p->top = UNSPEC;
-    cmdline_p->bottom = UNSPEC;
-    cmdline_p->width = UNSPEC;
-    cmdline_p->height = UNSPEC;
-    cmdline_p->pad = FALSE;
-    cmdline_p->verbose = FALSE;
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
-
-    optParseOptions2(&argc, argv, opt, 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
-
-    if (cmdline_p->width < 0)
-        pm_error("-width may not be negative.");
-    if (cmdline_p->height < 0)
-        pm_error("-height may not be negative.");
-
-    if ((argc-1) != 0 && (argc-1) != 1 && (argc-1) != 4 && (argc-1) != 5)
-        pm_error("Wrong number of arguments.  "
-                 "Must be 0, 1, 4, or 5 arguments.");
-
-    switch (argc-1) {
-    case 0:
-        cmdline_p->input_filespec = "-";
-        break;
-    case 1:
-        cmdline_p->input_filespec = argv[1];
-        break;
-    case 4:
-    case 5: {
-        int warg, harg;  /* The "width" and "height" command line arguments */
-
-        if (sscanf(argv[1], "%d", &cmdline_p->left) != 1)
-            pm_error("Invalid number for left column argument");
-        if (sscanf(argv[2], "%d", &cmdline_p->top) != 1)
-            pm_error("Invalid number for top row argument");
-        if (sscanf(argv[3], "%d", &warg) != 1)
-            pm_error("Invalid number for width argument");
-        if (sscanf(argv[4], "%d", &harg) != 1)
-            pm_error("Invalid number for height argument");
-
-        if (warg > 0) {
-            cmdline_p->width = warg;
-            cmdline_p->right = UNSPEC;
-        } else {
-            cmdline_p->width = UNSPEC;
-            cmdline_p->right = warg -1;
-        }
-        if (harg > 0) {
-            cmdline_p->height = harg;
-            cmdline_p->bottom = UNSPEC;
-        } else {
-            cmdline_p->height = UNSPEC;
-            cmdline_p->bottom = harg - 1;
-        }
-
-        if (argc-1 == 4)
-            cmdline_p->input_filespec = "-";
-        else
-            cmdline_p->input_filespec = argv[5];
-        break;
-    }
-    }
-}
-
-
-
-static void
-compute_cut_bounds(const int cols, const int rows,
-                   const int leftarg, const int rightarg, 
-                   const int toparg, const int bottomarg,
-                   const int widtharg, const int heightarg,
-                   int * const leftcol_p, int * const rightcol_p,
-                   int * const toprow_p, int * const bottomrow_p) {
-/*----------------------------------------------------------------------------
-   From the values given on the command line 'leftarg', 'rightarg',
-   'toparg', 'bottomarg', 'widtharg', and 'heightarg', determine what
-   rectangle the user wants cut out.
-
-   Any of these arguments may be UNSPEC to indicate "not specified".
-   Any except 'widtharg' and 'heightarg' may be negative to indicate
-   relative to the far edge.  'widtharg' and 'heightarg' are positive.
-
-   Return the location of the rectangle as *leftcol_p, *rightcol_p,
-   *toprow_p, and *bottomrow_p.  
------------------------------------------------------------------------------*/
-
-    int leftcol, rightcol, toprow, bottomrow;
-        /* The left and right column numbers and top and bottom row numbers
-           specified by the user, except with negative values translated
-           into the actual values.
-
-           Note that these may very well be negative themselves, such
-           as when the user says "column -10" and there are only 5 columns
-           in the image.
-           */
-
-    /* Translate negative column and row into real column and row */
-    /* Exploit the fact that UNSPEC is a positive number */
-
-    if (leftarg >= 0)
-        leftcol = leftarg;
-    else
-        leftcol = cols + leftarg;
-    if (rightarg >= 0)
-        rightcol = rightarg;
-    else
-        rightcol = cols + rightarg;
-    if (toparg >= 0)
-        toprow = toparg;
-    else
-        toprow = rows + toparg;
-    if (bottomarg >= 0)
-        bottomrow = bottomarg;
-    else
-        bottomrow = rows + bottomarg;
-
-    /* Sort out left, right, and width specifications */
-
-    if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = 0;
-        *rightcol_p = cols - 1;
-    }
-    if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) {
-        *leftcol_p = 0;
-        *rightcol_p = 0 + widtharg - 1;
-    }
-    if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = 0;
-        *rightcol_p = rightcol;
-    }
-    if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg != UNSPEC) {
-        *leftcol_p = rightcol - widtharg + 1;
-        *rightcol_p = rightcol;
-    }
-    if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = leftcol;
-        *rightcol_p = cols - 1;
-    }
-    if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) {
-        *leftcol_p = leftcol;
-        *rightcol_p = leftcol + widtharg - 1;
-    }
-    if (leftcol != UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = leftcol;
-        *rightcol_p = rightcol;
-    }
-    if (leftcol != UNSPEC && rightcol != UNSPEC && widtharg != UNSPEC) {
-        pm_error("You may not specify left, right, and width.\n"
-                 "Choose at most two of these.");
-    }
-
-
-    /* Sort out top, bottom, and height specifications */
-
-    if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = 0;
-        *bottomrow_p = rows - 1;
-    }
-    if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) {
-        *toprow_p = 0;
-        *bottomrow_p = 0 + heightarg - 1;
-    }
-    if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = 0;
-        *bottomrow_p = bottomrow;
-    }
-    if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg != UNSPEC) {
-        *toprow_p = bottomrow - heightarg + 1;
-        *bottomrow_p = bottomrow;
-    }
-    if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = toprow;
-        *bottomrow_p = rows - 1;
-    }
-    if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) {
-        *toprow_p = toprow;
-        *bottomrow_p = toprow + heightarg - 1;
-    }
-    if (toprow != UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = toprow;
-        *bottomrow_p = bottomrow;
-    }
-    if (toprow != UNSPEC && bottomrow != UNSPEC && heightarg != UNSPEC) {
-        pm_error("You may not specify top, bottom, and height.\n"
-                 "Choose at most two of these.");
-    }
-
-}
-
-
-
-static void
-reject_out_of_bounds(const int cols, const int rows, 
-                     const int leftcol, const int rightcol, 
-                     const int toprow, const int bottomrow) {
-
-    /* Reject coordinates off the edge */
-
-    if (leftcol < 0)
-        pm_error("You have specified a left edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", leftcol);
-    if (rightcol > cols-1)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", rightcol, cols-1);
-    if (rightcol < 0)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", rightcol);
-    if (rightcol > cols-1)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", rightcol, cols-1);
-    if (leftcol > rightcol) 
-        pm_error("You have specified a left edge (%d) that is to the right\n"
-                 "of the right edge you specified (%d)", 
-                 leftcol, rightcol);
-    
-    if (toprow < 0)
-        pm_error("You have specified a top edge (%d) that is above the top "
-                 "edge of the image (0)", toprow);
-    if (bottomrow > rows-1)
-        pm_error("You have specified a bottom edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", bottomrow, rows-1);
-    if (bottomrow < 0)
-        pm_error("You have specified a bottom edge (%d) that is above the\n"
-                 "top edge of the image (0)", bottomrow);
-    if (bottomrow > rows-1)
-        pm_error("You have specified a bottom edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", bottomrow, rows-1);
-    if (toprow > bottomrow) 
-        pm_error("You have specified a top edge (%d) that is below\n"
-                 "the bottom edge you specified (%d)", 
-                 toprow, bottomrow);
-}
-
-
-
-static void
-write_black_rows(FILE *outfile, const int rows, const int cols, 
-                 xel * const output_row,
-                 const pixval maxval, const int format) {
-/*----------------------------------------------------------------------------
-   Write out to file 'outfile' 'rows' rows of 'cols' black xels each,
-   part of an image of format 'format' with maxval 'maxval'.  
-
-   Use *output_row as a buffer.  It is at least 'cols' xels wide.
------------------------------------------------------------------------------*/
-    int row;
-    for (row = 0; row < rows; row++) {
-        int col;
-        for (col = 0; col < cols; col++) output_row[col] = black_xel;
-        pnm_writepnmrow(outfile, output_row, cols, maxval, format, 0);
-    }
-}
-
-
-
-int
-main(int argc, char *argv[]) {
-
-    FILE* ifp;
-    xel* xelrow;  /* Row from input image */
-    xel* output_row;  /* Row of output image */
-    xelval maxval;
-    int rows, cols, format, row;
-    int leftcol, rightcol, toprow, bottomrow;
-    int output_cols;  /* Width of output image */
-    struct cmdline_info cmdline;
-
-    pnm_init( &argc, argv );
-
-    parse_command_line(argc, argv, &cmdline);
-
-    ifp = pm_openr(cmdline.input_filespec);
-
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
-    xelrow = pnm_allocrow(cols);
-
-    black_xel = pnm_blackxel(maxval, format);
-
-    compute_cut_bounds(cols, rows, 
-                       cmdline.left, cmdline.right, 
-                       cmdline.top, cmdline.bottom, 
-                       cmdline.width, cmdline.height, 
-                       &leftcol, &rightcol, &toprow, &bottomrow);
-
-    if (!cmdline.pad)
-        reject_out_of_bounds(cols, rows, leftcol, rightcol, toprow, bottomrow);
-
-    if (cmdline.verbose) {
-        pm_message("Image goes from Row 0, Column 0 through Row %d, Column %d",
-                   rows-1, cols-1);
-        pm_message("Cutting from Row %d, Column %d through Row %d Column %d",
-                   toprow, leftcol, bottomrow, rightcol);
-    }
-
-    output_cols = rightcol-leftcol+1;
-    output_row = pnm_allocrow(output_cols);
-    
-    pnm_writepnminit(stdout, output_cols, bottomrow-toprow+1, 
-                     maxval, format, 0 );
-
-    /* Implementation note:  If speed is ever an issue, we can probably
-       speed up significantly the non-padding case by writing a special
-       case loop here for the case cmdline.pad == FALSE.
-       */
-
-    /* Write out top padding */
-    write_black_rows(stdout, 0 - toprow, output_cols, output_row, 
-                     maxval, format);
-    
-    /* Read input and write out rows extracted from it */
-    for (row = 0; row < rows; row++) {
-        pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
-        if (row >= toprow && row <= bottomrow) {
-            int col;
-            /* Put in left padding */
-            for (col = leftcol; col < 0; col++) { 
-                output_row[col-leftcol] = black_xel;
-            }
-            /* Put in extracted columns */
-            for (col = MAX(leftcol, 0); col <= MIN(rightcol, cols-1); col++) {
-                output_row[col-leftcol] = xelrow[col];
-            }
-            /* Put in right padding */
-            for (col = MAX(cols, leftcol); col <= rightcol; col++) {
-                output_row[col-leftcol] = black_xel;
-            }
-            pnm_writepnmrow(stdout, output_row, output_cols, 
-                            maxval, format, 0);
-        }
-    }
-    /* Note that we may be tempted just to quit after reaching the bottom
-       of the extracted image, but that would cause a broken pipe problem
-       for the process that's feeding us the image.
-       */
-    /* Write out bottom padding */
-    write_black_rows(stdout, bottomrow - (rows-1), output_cols, output_row, 
-                     maxval, format);
-
-    pnm_freerow(output_row);
-    pnm_freerow(xelrow);
-    pm_close(ifp);
-    pm_close(stdout);
-    
-    exit( 0 );
-}
-
diff --git a/editor/pnminvert.test b/editor/pnminvert.test
index 606e4e5c..5534f20d 100644
--- a/editor/pnminvert.test
+++ b/editor/pnminvert.test
@@ -2,7 +2,7 @@ echo Test 1.  Should print 1240379484 41
 ./pnminvert ../testgrid.pbm | cksum
 echo Test 2.  Should print 1416115901 101484
 ./pnminvert ../testimg.ppm | cksum
-echo Test 3.  Should print 4215652354 33838
+echo Test 3.  Should print 2961441369 33838
 ppmtopgm ../testimg.ppm | ./pnminvert | cksum
 echo Test 4.  Should print 2595564405 14
 pbmmake -w 7 7 | ./pnminvert | cksum
diff --git a/editor/pnmscale.c b/editor/pnmscale.c
deleted file mode 100644
index f75f440c..00000000
--- a/editor/pnmscale.c
+++ /dev/null
@@ -1,748 +0,0 @@
-/* pnmscale.c - read a portable anymap and scale it
-**
-** Copyright (C) 1989, 1991 by Jef Poskanzer.
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
-**
-*/
-
-/* 
-
-      DON'T ADD NEW FUNCTION TO THIS PROGRAM.  ADD IT TO pamscale.c
-      INSTEAD.
-
-*/
-
- 
-#include <math.h>
-#include <string.h>
-
-#include "pnm.h"
-#include "shhopt.h"
-
-/* The pnm library allows us to code this program without branching cases
-   for PGM and PPM, but we do the branch anyway to speed up processing of 
-   PGM images.
-*/
-
-
-struct cmdline_info {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *input_filespec;  /* Filespecs of input files */
-    unsigned int xsize;
-    unsigned int ysize;
-    float xscale;
-    float yscale;
-    unsigned int xbox;
-    unsigned int ybox;
-    unsigned int pixels;
-    unsigned int verbose;
-    unsigned int nomix;
-};
-
-
-static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-    unsigned int xysize;
-    int xsize, ysize, pixels;
-    int reduce;
-    float xscale, yscale, scale_parm;
-
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "xsize",     OPT_UINT,    &xsize,               NULL, 0);
-    OPTENT3(0, "width",     OPT_UINT,    &xsize,               NULL, 0);
-    OPTENT3(0, "ysize",     OPT_UINT,    &ysize,               NULL, 0);
-    OPTENT3(0, "height",    OPT_UINT,    &ysize,               NULL, 0);
-    OPTENT3(0, "xscale",    OPT_FLOAT,   &xscale,              NULL, 0);
-    OPTENT3(0, "yscale",    OPT_FLOAT,   &yscale,              NULL, 0);
-    OPTENT3(0, "pixels",    OPT_UINT,    &pixels,              NULL, 0);
-    OPTENT3(0, "reduce",    OPT_UINT,    &reduce,              NULL, 0);
-    OPTENT3(0, "xysize",    OPT_FLAG,    NULL, &xysize,              0);
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL, &cmdline_p->verbose,  0);
-    OPTENT3(0, "nomix",     OPT_FLAG,    NULL, &cmdline_p->nomix,    0);
-
-    /* Set the defaults. -1 = unspecified */
-    /* (Now that we're using ParseOptions3, we don't have to do this -1
-       nonsense, but we don't want to risk screwing these complex 
-       option compatibilities up, so we'll convert that later.
-    */
-    xsize = -1;
-    ysize = -1;
-    xscale = -1.0;
-    yscale = -1.0;
-    pixels = -1;
-    reduce = -1;
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
-
-    if (xsize == 0)
-        pm_error("-xsize/width must be greater than zero.");
-    if (ysize == 0)
-        pm_error("-ysize/height must be greater than zero.");
-    if (xscale != -1.0 && xscale <= 0.0)
-        pm_error("-xscale must be greater than zero.");
-    if (yscale != -1.0 && yscale <= 0.0)
-        pm_error("-yscale must be greater than zero.");
-    if (reduce <= 0 && reduce != -1)
-        pm_error("-reduce must be greater than zero.");
-
-    if (xsize != -1 && xscale != -1)
-        pm_error("Cannot specify both -xsize/width and -xscale.");
-    if (ysize != -1 && yscale != -1)
-        pm_error("Cannot specify both -ysize/height and -yscale.");
-    
-    if (xysize && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1 || 
-         reduce != -1 || pixels != -1) )
-        pm_error("Cannot specify -xysize with other dimension options.");
-    if (pixels != -1 && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1 ||
-         reduce != -1) )
-        pm_error("Cannot specify -pixels with other dimension options.");
-    if (reduce != -1 && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1) )
-        pm_error("Cannot specify -reduce with other dimension options.");
-
-    if (pixels == 0)
-        pm_error("-pixels must be greater than zero");
-
-    /* Get the program parameters */
-
-    if (xysize) {
-        /* parameters are xbox, ybox, and optional filespec */
-        scale_parm = 0.0;
-        if (argc-1 < 2)
-            pm_error("You must supply at least two parameters with -xysize:\n "
-                     "x and y dimensions of the bounding box.");
-        else if (argc-1 > 3)
-            pm_error("Too many arguments.  With -xysize, you need 2 or 3 "
-                     "arguments.");
-        else {
-            char * endptr;
-            cmdline_p->xbox = strtol(argv[1], &endptr, 10);
-            if (strlen(argv[1]) > 0 && *endptr != '\0')
-                pm_error("horizontal xysize not an integer: '%s'", argv[1]);
-            if (cmdline_p->xbox <= 0)
-                pm_error("horizontal size is not positive: %d", 
-                         cmdline_p->xbox);
-
-            cmdline_p->ybox = strtol(argv[2], &endptr, 10);
-            if (strlen(argv[2]) > 0 && *endptr != '\0')
-                pm_error("vertical xysize not an integer: '%s'", argv[2]);
-            if (cmdline_p->ybox <= 0)
-                pm_error("vertical size is not positive: %d", 
-                         cmdline_p->ybox);
-            
-            if (argc-1 < 3)
-                cmdline_p->input_filespec = "-";
-            else
-                cmdline_p->input_filespec = argv[3];
-        }
-    } else {
-        cmdline_p->xbox = 0;
-        cmdline_p->ybox = 0;
-        
-        if (xsize == -1 && xscale == -1 && ysize == -1 && yscale == -1
-            && pixels == -1 && reduce == -1) {
-            /* parameters are scale factor and optional filespec */
-            if (argc-1 < 1)
-                pm_error("With no dimension options, you must supply at least "
-                         "one parameter: \nthe scale factor.");
-            else {
-                scale_parm = atof(argv[1]);
-
-                if (scale_parm == 0.0)
-                    pm_error("The scale parameter %s is not "
-                             "a positive number.",
-                             argv[1]);
-                else {
-                    if (argc-1 < 2)
-                        cmdline_p->input_filespec = "-";
-                    else
-                        cmdline_p->input_filespec = argv[2];
-                }
-            }
-        } else {
-            /* Only parameter allowed is optional filespec */
-            if (argc-1 < 1)
-                cmdline_p->input_filespec = "-";
-            else
-                cmdline_p->input_filespec = argv[1];
-
-            if (reduce != -1) {
-                scale_parm = ((double) 1.0) / ((double) reduce);
-                pm_message("reducing by %d gives scale factor of %f.", 
-                           reduce, scale_parm);
-            } else
-                scale_parm = 0.0;
-        }
-    }
-
-    cmdline_p->xsize = xsize == -1 ? 0 : xsize;
-    cmdline_p->ysize = ysize == -1 ? 0 : ysize;
-    cmdline_p->pixels = pixels == -1 ? 0 : pixels;
-
-    if (scale_parm) {
-        cmdline_p->xscale = scale_parm;
-        cmdline_p->yscale = scale_parm;
-    } else {
-        cmdline_p->xscale = xscale == -1.0 ? 0.0 : xscale;
-        cmdline_p->yscale = yscale == -1.0 ? 0.0 : yscale;
-    }
-}
-
-
-
-static void
-compute_output_dimensions(const struct cmdline_info cmdline, 
-                          const int rows, const int cols,
-                          int * newrowsP, int * newcolsP) {
-
-    if (cmdline.pixels) {
-        if (rows * cols <= cmdline.pixels) {
-            *newrowsP = rows;
-            *newcolsP = cols;
-        } else {
-            const double scale =
-                sqrt( (float) cmdline.pixels / ((float) cols * (float) rows));
-            *newrowsP = rows * scale;
-            *newcolsP = cols * scale;
-        }
-    } else if (cmdline.xbox) {
-        const double aspect_ratio = (float) cols / (float) rows;
-        const double box_aspect_ratio = 
-            (float) cmdline.xbox / (float) cmdline.ybox;
-        
-        if (box_aspect_ratio > aspect_ratio) {
-            *newrowsP = cmdline.ybox;
-            *newcolsP = *newrowsP * aspect_ratio + 0.5;
-        } else {
-            *newcolsP = cmdline.xbox;
-            *newrowsP = *newcolsP / aspect_ratio + 0.5;
-        }
-    } else {
-        if (cmdline.xsize)
-            *newcolsP = cmdline.xsize;
-        else if (cmdline.xscale)
-            *newcolsP = cmdline.xscale * cols + .5;
-        else if (cmdline.ysize)
-            *newcolsP = cols * ((float) cmdline.ysize/rows) +.5;
-        else
-            *newcolsP = cols;
-
-        if (cmdline.ysize)
-            *newrowsP = cmdline.ysize;
-        else if (cmdline.yscale)
-            *newrowsP = cmdline.yscale * rows +.5;
-        else if (cmdline.xsize)
-            *newrowsP = rows * ((float) cmdline.xsize/cols) +.5;
-        else
-            *newrowsP = rows;
-    }    
-
-    /* If the calculations above yielded (due to rounding) a zero 
-       dimension, we fudge it up to 1.  We do this rather than considering
-       it a specification error (and dying) because it's friendlier to 
-       automated processes that work on arbitrary input.  It saves them
-       having to check their numbers to avoid catastrophe.
-    */
-
-    if (*newcolsP < 1) *newcolsP = 1;
-    if (*newrowsP < 1) *newrowsP = 1;
-}        
-
-
-
-static void
-horizontal_scale(const xel inputxelrow[], xel newxelrow[], 
-                 const int cols, const int newcols, const float xscale, 
-                 const int format, const xelval maxval,
-                 float * const stretchP) {
-/*----------------------------------------------------------------------------
-   Take the input row inputxelrow[], which is 'cols' columns wide, and
-   scale it by a factor of 'xscale', to create
-   the output row newxelrow[], which is 'newcols' columns wide.
-
-   'format' and 'maxval' describe the Netpbm format of the both input and
-   output rows.
------------------------------------------------------------------------------*/
-    float r, g, b;
-    float fraccoltofill, fraccolleft;
-    unsigned int col;
-    unsigned int newcol;
-    
-    newcol = 0;
-    fraccoltofill = 1.0;  /* Output column is "empty" now */
-    r = g = b = 0;          /* initial value */
-    for (col = 0; col < cols; ++col) {
-        /* Process one pixel from input ('inputxelrow') */
-        fraccolleft = xscale;
-        /* Output all columns, if any, that can be filled using information
-           from this input column, in addition to what's already in the output
-           column.
-        */
-        while (fraccolleft >= fraccoltofill) {
-            /* Generate one output pixel in 'newxelrow'.  It will consist
-               of anything accumulated from prior input pixels in 'r','g', 
-               and 'b', plus a fraction of the current input pixel.
-            */
-            switch (PNM_FORMAT_TYPE(format)) {
-            case PPM_TYPE:
-                r += fraccoltofill * PPM_GETR(inputxelrow[col]);
-                g += fraccoltofill * PPM_GETG(inputxelrow[col]);
-                b += fraccoltofill * PPM_GETB(inputxelrow[col]);
-                PPM_ASSIGN( newxelrow[newcol], 
-                            MIN(maxval, (int) (r + 0.5)), 
-                            MIN(maxval, (int) (g + 0.5)), 
-                            MIN(maxval, (int) (b + 0.5))
-                    );
-                break;
-
-            default:
-                g += fraccoltofill * PNM_GET1(inputxelrow[col]);
-                PNM_ASSIGN1( newxelrow[newcol], MIN(maxval, (int) (g + 0.5)));
-                break;
-            }
-            fraccolleft -= fraccoltofill;
-            /* Set up to start filling next output column */
-            newcol++;
-            fraccoltofill = 1.0;
-            r = g = b = 0.0;
-        }
-        /* There's not enough left in the current input pixel to fill up 
-           a whole output column, so just accumulate the remainder of the
-           pixel into the current output column.
-        */
-        if (fraccolleft > 0.0) {
-            switch (PNM_FORMAT_TYPE(format)) {
-            case PPM_TYPE:
-                r += fraccolleft * PPM_GETR(inputxelrow[col]);
-                g += fraccolleft * PPM_GETG(inputxelrow[col]);
-                b += fraccolleft * PPM_GETB(inputxelrow[col]);
-                break;
-                    
-            default:
-                g += fraccolleft * PNM_GET1(inputxelrow[col]);
-                break;
-            }
-            fraccoltofill -= fraccolleft;
-        }
-    }
-
-    if (newcol < newcols-1 || newcol > newcols)
-        pm_error("Internal error: last column filled is %d, but %d "
-                 "is the rightmost output column.",
-                 newcol, newcols-1);
-
-    if (newcol < newcols ) {
-        /* We were still working on the last output column when we 
-           ran out of input columns.  This would be because of rounding
-           down, and we should be missing only a tiny fraction of that
-           last output column.
-        */
-
-        *stretchP = fraccoltofill;
-
-        switch (PNM_FORMAT_TYPE(format)) {
-        case PPM_TYPE:
-            r += fraccoltofill * PPM_GETR(inputxelrow[cols-1]);
-            g += fraccoltofill * PPM_GETG(inputxelrow[cols-1]);
-            b += fraccoltofill * PPM_GETB(inputxelrow[cols-1]);
-
-            PPM_ASSIGN(newxelrow[newcol], 
-                       MIN(maxval, (int) (r + 0.5)), 
-                       MIN(maxval, (int) (g + 0.5)), 
-                       MIN(maxval, (int) (b + 0.5))
-                );
-            break;
-                
-        default:
-            g += fraccoltofill * PNM_GET1(inputxelrow[cols-1]);
-            PNM_ASSIGN1( newxelrow[newcol], MIN(maxval, (int) (g + 0.5)));
-            break;
-        }
-    } else 
-        *stretchP = 0;
-}
-
-
-
-static void
-zeroAccum(int const cols, int const format, 
-          float rs[], float gs[], float bs[]) {
-
-    int col;
-
-    for ( col = 0; col < cols; ++col )
-        rs[col] = gs[col] = bs[col] = 0.0;
-}
-
-
-
-static void
-accumOutputRow(xel * const xelrow, float const fraction, 
-               float rs[], float gs[], float bs[], 
-               int const cols, int const format) {
-/*----------------------------------------------------------------------------
-   Take 'fraction' times the color in row xelrow and add it to 
-   rs/gs/bs.  'fraction' is less than 1.0.
------------------------------------------------------------------------------*/
-    int col;
-
-    switch ( PNM_FORMAT_TYPE(format) ) {
-    case PPM_TYPE:
-        for ( col = 0; col < cols; ++col ) {
-            rs[col] += fraction * PPM_GETR(xelrow[col]);
-            gs[col] += fraction * PPM_GETG(xelrow[col]);
-            bs[col] += fraction * PPM_GETB(xelrow[col]);
-        }
-        break;
-
-    default:
-        for ( col = 0; col < cols; ++col)
-            gs[col] += fraction * PNM_GET1(xelrow[col]);
-        break;
-    }
-}
-
-
-
-static void
-makeRow(xel * const xelrow, float rs[], float gs[], float bs[],
-        int const cols, xelval const maxval, int const format) {
-/*----------------------------------------------------------------------------
-   Make an xel row at 'xelrow' with format 'format' and
-   maxval 'maxval' out of the color values in 
-   rs[], gs[], and bs[].
------------------------------------------------------------------------------*/
-    int col;
-
-    switch ( PNM_FORMAT_TYPE(format) ) {
-    case PPM_TYPE:
-        for ( col = 0; col < cols; ++col) {
-            PPM_ASSIGN(xelrow[col], 
-                       MIN(maxval, (int) (rs[col] + 0.5)), 
-                       MIN(maxval, (int) (gs[col] + 0.5)), 
-                       MIN(maxval, (int) (bs[col] + 0.5))
-                );
-        }
-        break;
-
-    default:
-        for ( col = 0; col < cols; ++col ) {
-            PNM_ASSIGN1(xelrow[col], 
-                        MIN(maxval, (int) (gs[col] + 0.5)));
-        }
-        break;
-    }
-}
-
-
-
-static void
-scaleWithMixing(FILE * const ifP,
-                int const cols, int const rows,
-                xelval const maxval, int const format,
-                int const newcols, int const newrows,
-                xelval const newmaxval, int const newformat,
-                float const xscale, float const yscale,
-                bool const verbose) {
-/*----------------------------------------------------------------------------
-   Scale the image on input file 'ifP' (which is described by 
-   'cols', 'rows', 'format', and 'maxval') by xscale horizontally and
-   yscale vertically and write the result to standard output as format
-   'newformat' and with maxval 'newmaxval'.
-
-   The input file is positioned past the header, to the beginning of the
-   raster.  The output file is too.
-
-   Mix colors from input rows together in the output rows.
------------------------------------------------------------------------------*/
-    /* Here's how we think of the color mixing scaling operation:  
-       
-       First, I'll describe scaling in one dimension.  Assume we have
-       a one row image.  A raster row is ordinarily a sequence of
-       discrete pixels which have no width and no distance between
-       them -- only a sequence.  Instead, think of the raster row as a
-       bunch of pixels 1 unit wide adjacent to each other.  For
-       example, we are going to scale a 100 pixel row to a 150 pixel
-       row.  Imagine placing the input row right above the output row
-       and stretching it so it is the same size as the output row.  It
-       still contains 100 pixels, but they are 1.5 units wide each.
-       Our goal is to make the output row look as much as possible
-       like the input row, while observing that a pixel can be only
-       one color.
-
-       Output Pixel 0 is completely covered by Input Pixel 0, so we
-       make Output Pixel 0 the same color as Input Pixel 0.  Output
-       Pixel 1 is covered half by Input Pixel 0 and half by Input
-       Pixel 1.  So we make Output Pixel 1 a 50/50 mix of Input Pixels
-       0 and 1.  If you stand back far enough, input and output will
-       look the same.
-
-       This works for all scale factors, both scaling up and scaling down.
-       
-       This program always stretches or squeezes the input row to be the
-       same length as the output row; The output row's pixels are always
-       1 unit wide.
-
-       The same thing works in the vertical direction.  We think of
-       rows as stacked strips of 1 unit height.  We conceptually
-       stretch the image vertically first (same process as above, but
-       in place of a single-color pixels, we have a vector of colors).
-       Then we take each row this vertical stretching generates and
-       stretch it horizontally.  
-    */
-
-    xel* xelrow;  /* An input row */
-    xel* vertScaledRow;
-        /* An output row after vertical scaling, but before horizontal
-           scaling
-        */
-    xel* newxelrow;
-    float rowsleft;
-        /* The number of rows of output that need to be formed from the
-           current input row (the one in xelrow[]), less the number that 
-           have already been formed (either in the rs/gs/bs accumulators
-           or output to the file).  This can be fractional because of the
-           way we define rows as having height.
-        */
-    float fracrowtofill;
-        /* The fraction of the current output row (the one in vertScaledRow[])
-           that hasn't yet been filled in from an input row.
-        */
-    float *rs, *gs, *bs;
-        /* The red, green, and blue color intensities so far accumulated
-           from input rows for the current output row.
-        */
-    int rowsread;
-        /* Number of rows of the input file that have been read */
-    int row;
-    
-    xelrow = pnm_allocrow(cols); 
-    vertScaledRow = pnm_allocrow(cols);
-    rs = (float*) pm_allocrow( cols, sizeof(float) );
-    gs = (float*) pm_allocrow( cols, sizeof(float) );
-    bs = (float*) pm_allocrow( cols, sizeof(float) );
-    rowsread = 0;
-    rowsleft = 0.0;
-    zeroAccum(cols, format, rs, gs, bs);
-    fracrowtofill = 1.0;
-
-    newxelrow = pnm_allocrow( newcols );
-
-    for ( row = 0; row < newrows; ++row ) {
-        /* First scale Y from xelrow[] into vertScaledRow[]. */
-
-        if ( newrows == rows ) { /* shortcut Y scaling if possible */
-            pnm_readpnmrow( ifP, vertScaledRow, cols, newmaxval, format );
-	    } else {
-            while (fracrowtofill > 0) {
-                if (rowsleft <= 0.0) {
-                    if (rowsread < rows) {
-                        pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
-                        ++rowsread;
-                    } else {
-                        /* We need another input row to fill up this
-                           output row, but there aren't any more.
-                           That's because of rounding down on our
-                           scaling arithmetic.  So we go ahead with
-                           the data from the last row we read, which
-                           amounts to stretching out the last output
-                           row.  
-                        */
-                        if (verbose)
-                            pm_message("%f of bottom row stretched due to "
-                                       "arithmetic imprecision", 
-                                       fracrowtofill);
-                    }
-                    rowsleft = yscale;
-                }
-                if (rowsleft < fracrowtofill) {
-                    accumOutputRow(xelrow, rowsleft, rs, gs, bs, 
-                                   cols, format);
-                    fracrowtofill -= rowsleft;
-                    rowsleft = 0.0;
-                } else {
-                    accumOutputRow(xelrow, fracrowtofill, rs, gs, bs,
-                                   cols, format);
-                    rowsleft = rowsleft - fracrowtofill;
-                    fracrowtofill = 0.0;
-                }
-            }
-            makeRow(vertScaledRow, rs, gs, bs, cols, newmaxval, format);
-            zeroAccum(cols, format, rs, gs, bs);
-            fracrowtofill = 1.0;
-	    }
-
-        /* Now scale vertScaledRow horizontally into newxelrow and write
-           it out. 
-        */
-
-        if (newcols == cols)	/* shortcut X scaling if possible */
-            pnm_writepnmrow(stdout, vertScaledRow, newcols, 
-                            newmaxval, newformat, 0);
-        else {
-            float stretch;
-
-            horizontal_scale(vertScaledRow, newxelrow, cols, newcols, xscale, 
-                             format, newmaxval, &stretch);
-            
-            if (verbose && row == 0)
-                pm_message("%f of right column stretched due to "
-                           "arithmetic imprecision", 
-                           stretch);
-            
-            pnm_writepnmrow(stdout, newxelrow, newcols, 
-                            newmaxval, newformat, 0 );
-        }
-	}
-    pnm_freerow(newxelrow);
-    pnm_freerow(xelrow);
-    pnm_freerow(vertScaledRow);
-}
-
-
-
-static void
-scaleWithoutMixing(FILE * const ifP,
-                   int const cols, int const rows,
-                   xelval const maxval, int const format,
-                   int const newcols, int const newrows,
-                   xelval const newmaxval, int const newformat,
-                   float const xscale, float const yscale) {
-/*----------------------------------------------------------------------------
-   Scale the image on input file 'ifP' (which is described by 
-   'cols', 'rows', 'format', and 'maxval') by xscale horizontally and
-   yscale vertically and write the result to standard output as format
-   'newformat' and with maxval 'newmaxval'.
-
-   The input file is positioned past the header, to the beginning of the
-   raster.  The output file is too.
-
-   Don't mix colors from different input pixels together in the output
-   pixels.  Each output pixel is an exact copy of some corresponding 
-   input pixel.
------------------------------------------------------------------------------*/
-    xel* xelrow;  /* An input row */
-    xel* newxelrow;
-    int row;
-    int rowInXelrow;
-
-    xelrow = pnm_allocrow(cols); 
-    rowInXelrow = -1;
-
-    newxelrow = pnm_allocrow(newcols);
-
-    for (row = 0; row < newrows; ++row) {
-        int col;
-        
-        int const inputRow = (int) (row / yscale);
-
-        for (; rowInXelrow < inputRow; ++rowInXelrow) 
-            pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
-        
-
-        for (col = 0; col < newcols; ++col) {
-            int const inputCol = (int) (col / xscale);
-            
-            newxelrow[col] = xelrow[inputCol];
-        }
-
-        pnm_writepnmrow(stdout, newxelrow, newcols, 
-                        newmaxval, newformat, 0 );
-	}
-    pnm_freerow(xelrow);
-    pnm_freerow(newxelrow);
-}
-
-
-
-int
-main(int argc, char **argv ) {
-
-    struct cmdline_info cmdline;
-    FILE* ifP;
-    int rows, cols, format, newformat, newrows, newcols;
-    xelval maxval, newmaxval;
-    float xscale, yscale;
-
-    pnm_init( &argc, argv );
-
-    parse_command_line(argc, argv, &cmdline);
-
-    ifP = pm_openr(cmdline.input_filespec);
-
-    pnm_readpnminit( ifP, &cols, &rows, &maxval, &format );
-
-    /* Promote PBM files to PGM. */
-    if ( PNM_FORMAT_TYPE(format) == PBM_TYPE ) {
-        newformat = PGM_TYPE;
-        newmaxval = PGM_MAXMAXVAL;
-        pm_message( "promoting from PBM to PGM" );
-	} else {
-        newformat = format;
-        newmaxval = maxval;
-    }
-    compute_output_dimensions(cmdline, rows, cols, &newrows, &newcols);
-
-    /* We round the scale factor down so that we never fill up the 
-       output while (a fractional pixel of) input remains unused.  Instead,
-       we will run out of input while (a fractional pixel of) output is 
-       unfilled -- which is easier for our algorithm to handle.
-       */
-    xscale = (float) newcols / cols;
-    yscale = (float) newrows / rows;
-
-    if (cmdline.verbose) {
-        pm_message("Scaling by %f horizontally to %d columns.", 
-                   xscale, newcols );
-        pm_message("Scaling by %f vertically to %d rows.", 
-                   yscale, newrows);
-    }
-
-    if (xscale * cols < newcols - 1 ||
-        yscale * rows < newrows - 1) 
-        pm_error("Arithmetic precision of this program is inadequate to "
-                 "do the specified scaling.  Use a smaller input image "
-                 "or a slightly different scale factor.");
-
-    pnm_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0);
-
-    if (cmdline.nomix) 
-        scaleWithoutMixing(ifP, cols, rows, maxval, format,
-                           newcols, newrows, newmaxval, newformat, 
-                           xscale, yscale);
-    else
-        scaleWithMixing(ifP, cols, rows, maxval, format,
-                        newcols, newrows, newmaxval, newformat, 
-                        xscale, yscale, cmdline.verbose);
-
-    pm_close(ifP);
-    pm_close(stdout);
-    
-    exit(0);
-}
diff --git a/editor/specialty/Makefile b/editor/specialty/Makefile
new file mode 100644
index 00000000..eda54882
--- /dev/null
+++ b/editor/specialty/Makefile
@@ -0,0 +1,55 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = editor/specialty
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/config.mk
+
+PORTBINARIES = pamdeinterlace \
+	       pammixinterlace \
+	       pamoil \
+	       pampop9 \
+	       pbmlife \
+	       pgmabel \
+	       pgmbentley \
+	       pgmmorphconv \
+	       pnmindex \
+	       ppm3d \
+	       ppmglobe \
+	       ppmntsc \
+	       ppmrelief \
+	       ppmshift \
+	       ppmspread \
+	       ppmtv \
+
+# We don't include programs that have special library dependencies in the
+# merge scheme, because we don't want those dependencies to prevent us
+# from building all the other programs.
+
+NOMERGEBINARIES = 
+MERGEBINARIES = $(PORTBINARIES)
+
+
+BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES)
+SCRIPTS =
+
+OBJECTS = $(BINARIES:%=%.o)
+
+MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
+
+.PHONY: all
+all: $(BINARIES)
+
+include $(SRCDIR)/common.mk
+
+install.bin: install.bin.local
+
+.PHONY: install.bin.local
+install.bin.local: $(PKGDIR)/bin
+# Remember that $(SYMLINK) might just be a copy command.
+# pamoil replaced pgmoil in June 2001.
+	cd $(PKGDIR)/bin ; \
+	rm -f pgmoil ; \
+	$(SYMLINK) pamoil$(EXE) pgmoil
diff --git a/editor/pamdeinterlace.c b/editor/specialty/pamdeinterlace.c
index 9ed1d8eb..9ed1d8eb 100644
--- a/editor/pamdeinterlace.c
+++ b/editor/specialty/pamdeinterlace.c
diff --git a/editor/pammixinterlace.c b/editor/specialty/pammixinterlace.c
index f22563f6..f22563f6 100644
--- a/editor/pammixinterlace.c
+++ b/editor/specialty/pammixinterlace.c
diff --git a/editor/pamoil.c b/editor/specialty/pamoil.c
index 6cb8d3ac..6cb8d3ac 100644
--- a/editor/pamoil.c
+++ b/editor/specialty/pamoil.c
diff --git a/editor/pampop9.c b/editor/specialty/pampop9.c
index d6c61e4f..d6c61e4f 100644
--- a/editor/pampop9.c
+++ b/editor/specialty/pampop9.c
diff --git a/editor/pbmlife.c b/editor/specialty/pbmlife.c
index be34cc69..be34cc69 100644
--- a/editor/pbmlife.c
+++ b/editor/specialty/pbmlife.c
diff --git a/editor/pgmabel.c b/editor/specialty/pgmabel.c
index 4914c4be..4914c4be 100644
--- a/editor/pgmabel.c
+++ b/editor/specialty/pgmabel.c
diff --git a/editor/pgmbentley.c b/editor/specialty/pgmbentley.c
index aed92074..aed92074 100644
--- a/editor/pgmbentley.c
+++ b/editor/specialty/pgmbentley.c
diff --git a/editor/pgmmorphconv.c b/editor/specialty/pgmmorphconv.c
index abc4e718..abc4e718 100644
--- a/editor/pgmmorphconv.c
+++ b/editor/specialty/pgmmorphconv.c
diff --git a/editor/pnmindex.c b/editor/specialty/pnmindex.c
index 7b405ef3..7b405ef3 100644
--- a/editor/pnmindex.c
+++ b/editor/specialty/pnmindex.c
diff --git a/editor/ppm3d.c b/editor/specialty/ppm3d.c
index 6f317a0b..6f317a0b 100644
--- a/editor/ppm3d.c
+++ b/editor/specialty/ppm3d.c
diff --git a/editor/ppmglobe.c b/editor/specialty/ppmglobe.c
index ee1a57c3..ee1a57c3 100644
--- a/editor/ppmglobe.c
+++ b/editor/specialty/ppmglobe.c
diff --git a/editor/ppmntsc.c b/editor/specialty/ppmntsc.c
index b9f2ac2f..b9f2ac2f 100644
--- a/editor/ppmntsc.c
+++ b/editor/specialty/ppmntsc.c
diff --git a/editor/ppmrelief.c b/editor/specialty/ppmrelief.c
index 5e0669c3..5e0669c3 100644
--- a/editor/ppmrelief.c
+++ b/editor/specialty/ppmrelief.c
diff --git a/editor/ppmshift.c b/editor/specialty/ppmshift.c
index a765daa5..a765daa5 100644
--- a/editor/ppmshift.c
+++ b/editor/specialty/ppmshift.c
diff --git a/editor/ppmspread.c b/editor/specialty/ppmspread.c
index 6753f4fe..6753f4fe 100644
--- a/editor/ppmspread.c
+++ b/editor/specialty/ppmspread.c
diff --git a/editor/ppmtv.c b/editor/specialty/ppmtv.c
index da25102a..da25102a 100644
--- a/editor/ppmtv.c
+++ b/editor/specialty/ppmtv.c