about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2023-11-30 18:56:23 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2023-11-30 18:56:23 +0000
commit563d2b921e5611e8db9a83a2ddc847e556e5965f (patch)
tree86ab988c1736b8f220a6648e25579df29d7606c6
parent888f84e1ee7cc65d822fa8e84e02743d529a9c12 (diff)
downloadnetpbm-mirror-563d2b921e5611e8db9a83a2ddc847e556e5965f.tar.gz
netpbm-mirror-563d2b921e5611e8db9a83a2ddc847e556e5965f.tar.xz
netpbm-mirror-563d2b921e5611e8db9a83a2ddc847e556e5965f.zip
Add -color, -promote, -detect-background
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@4796 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--doc/HISTORY2
-rw-r--r--editor/pnmpad.c459
2 files changed, 391 insertions, 70 deletions
diff --git a/doc/HISTORY b/doc/HISTORY
index fc97a63c..c06ddd9a 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -6,7 +6,7 @@ CHANGE HISTORY
 
 not yet  BJH  Release 11.05.00
 
-              pnmpad: Add -extend-edge.
+              pnmpad: Add -color, -promote, -extend-edge, -detect-background .
 
               pamtosvg: fix "zero determinant" failure.  Introduced in
               Netpbm 11.04 (September 2023).
diff --git a/editor/pnmpad.c b/editor/pnmpad.c
index 7268a67c..da7eed2b 100644
--- a/editor/pnmpad.c
+++ b/editor/pnmpad.c
@@ -9,6 +9,7 @@
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include <nstring.h>
 #include "shhopt.h"
 #include "pnm.h"
 
@@ -17,6 +18,49 @@
        arithmetic overflow
     */
 
+enum PadType {
+    /* PAD_COLOR means the specified padding is not black, white, or gray --
+       even regardless of whether user specified it with a -color option.
+       E.g. -color=black is PAD_BLACK.
+    */
+    PAD_BLACK,
+    PAD_WHITE,
+    PAD_GRAY,
+    PAD_COLOR,
+    PAD_DETECTED_BG,
+    PAD_EXTEND_EDGE
+};
+
+enum PromoteType {
+    PROMOTE_NONE,
+    PROMOTE_FORMAT,
+    PROMOTE_ALL
+};
+
+
+
+static enum PadType
+padTypeFmColorStr(const char * const colorStr) {
+
+    pixel const color = ppm_parsecolor(colorStr, PPM_OVERALLMAXVAL);
+
+    enum PadType retval;
+
+    if (PPM_ISGRAY(color)) {
+        if (PPM_GETR(color) == 0)
+            retval = PAD_BLACK;
+        else if  (PPM_GETR(color) == PPM_OVERALLMAXVAL)
+            retval = PAD_WHITE;
+        else
+            retval = PAD_GRAY;
+    } else
+        retval = PAD_COLOR;
+
+    return retval;
+}
+
+
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -38,8 +82,9 @@ struct CmdlineInfo {
     float yalign;
     unsigned int mwidth;
     unsigned int mheight;
-    unsigned int white;     /* >0: pad white; 0: pad black */
-    unsigned int extend_edge;
+    enum PadType padType;
+    const char * color;
+    enum PromoteType promote;
     unsigned int reportonly;
     unsigned int verbose;
 };
@@ -96,8 +141,12 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
-    unsigned int blackOpt;
+    unsigned int black, white, extend_edge;
     unsigned int xalignSpec, yalignSpec, mwidthSpec, mheightSpec;
+    unsigned int colorSpec;
+    unsigned int detect_background;
+    unsigned int promoteSpec;
+    const char * promoteOpt;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -127,19 +176,25 @@ parseCommandLine(int argc, const char ** argv,
     OPTENT3(0,   "valign",    OPT_FLOAT,   &cmdlineP->yalign,
             &yalignSpec,           0);
     OPTENT3(0,   "mwidth",    OPT_UINT,    &cmdlineP->mwidth,
-            &mwidthSpec,         0);
+            &mwidthSpec,           0);
     OPTENT3(0,   "mheight",   OPT_UINT,    &cmdlineP->mheight,
-            &mheightSpec,        0);
+            &mheightSpec,          0);
     OPTENT3(0,   "black",     OPT_FLAG,    NULL,
-            &blackOpt,           0);
+            &black,                0);
     OPTENT3(0,   "white",     OPT_FLAG,    NULL,
-            &cmdlineP->white,    0);
+            &white,                0);
+    OPTENT3(0,   "color",     OPT_STRING,  &cmdlineP->color,
+            &colorSpec,            0);
     OPTENT3(0,   "extend-edge", OPT_FLAG,  NULL,
-            &cmdlineP->extend_edge,  0);
+            &extend_edge,          0);
+    OPTENT3(0,   "detect-background",  OPT_FLAG,  NULL,
+            &detect_background,    0);
+    OPTENT3(0,   "promote",     OPT_STRING, &promoteOpt,
+            &promoteSpec,          0);
     OPTENT3(0,   "reportonly", OPT_FLAG,   NULL,
-            &cmdlineP->reportonly,   0);
-    OPTENT3(0,   "verbose",   OPT_FLAG,    NULL,
-            &cmdlineP->verbose,  0);
+            &cmdlineP->reportonly, 0);
+    OPTENT3(0,   "verbose",    OPT_FLAG,   NULL,
+            &cmdlineP->verbose,    0);
 
     opt.opt_table = option_def;
     opt.short_allowed = false;  /* We have no short (old-fashioned) options */
@@ -148,14 +203,62 @@ parseCommandLine(int argc, const char ** argv,
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (cmdlineP->extend_edge) {
-        if (blackOpt || cmdlineP->white)
-            pm_error("You cannot specify -extend-edge with -black or -white");
+    if (!colorSpec)
+        cmdlineP->color = NULL;
+
+    if (black + white + colorSpec + detect_background + extend_edge > 1)
+        pm_error("You can specify only one one of -black, -white, "
+                 "-color -detect-background and -extend-edge");
+    else if (black)
+        cmdlineP->padType = PAD_BLACK;
+    else if (white)
+        cmdlineP->padType = PAD_WHITE;
+    else if (colorSpec)
+        cmdlineP->padType = padTypeFmColorStr(cmdlineP->color);
+    else if (extend_edge)
+        cmdlineP->padType = PAD_EXTEND_EDGE;
+    else if (detect_background)
+        cmdlineP->padType = PAD_DETECTED_BG;
+    else {
+        cmdlineP->padType = PAD_BLACK;
+        if (cmdlineP->verbose)
+            pm_message("No pad color specified.  Padding with black.");
     }
 
-    if (blackOpt && cmdlineP->white)
-        pm_error("You cannot specify both -black and -white");
-
+    if (promoteSpec) {
+        if (!colorSpec)
+            pm_error("-promote requires -color");
+        else {
+            switch (cmdlineP->padType) {
+            case PAD_COLOR:
+            case PAD_GRAY:
+                if (streq(promoteOpt, "none"))
+                    cmdlineP->promote = PROMOTE_NONE;
+                else if (streq(promoteOpt, "format"))
+                    cmdlineP->promote = PROMOTE_FORMAT;
+                else if (streq(promoteOpt, "all"))
+                    cmdlineP->promote = PROMOTE_ALL;
+                else
+                    pm_error("Invalid value for -promote.  "
+                             "Valid values are 'none', 'format', and 'all'");
+                break;
+            case PAD_BLACK:
+            case PAD_WHITE:
+                cmdlineP->promote = PROMOTE_NONE;
+                break;
+            case PAD_DETECTED_BG:
+            case PAD_EXTEND_EDGE:
+                assert(false);  /* Because we have -color */
+                break;
+            }
+        }
+    } else {
+        if (cmdlineP->padType == PAD_COLOR ||
+            cmdlineP->padType == PAD_GRAY) {
+            cmdlineP->promote = PROMOTE_ALL;
+        } else
+            cmdlineP->promote = PROMOTE_NONE;
+    }
     if (cmdlineP->topSpec > 1)
        pm_error("You can specify -top only once");
 
@@ -489,7 +592,9 @@ reportPadSizes(int          const inCols,
 static unsigned char
 bitPeek(const unsigned char * const bitrow,
         unsigned int          const position) {
-
+/*----------------------------------------------------------------------------
+  Value of pixel (bit) of 'bitrow' at 'position'.
+-----------------------------------------------------------------------------*/
     bool retval;
 
     unsigned int const charPosition = position / 8;
@@ -505,7 +610,10 @@ bitPeek(const unsigned char * const bitrow,
 static void
 extendLeftPbm(unsigned char * const bitrow,
               unsigned int    const lpad) {
-
+/*----------------------------------------------------------------------------
+  Add 'lpad' pixels of padding the the left side of PBM row 'bitrow' by
+  duplicating leftmost pixel.
+-----------------------------------------------------------------------------*/
     unsigned int const padCharCt  = lpad / 8;
     unsigned int const fractBitCt = lpad % 8;
     unsigned int const color      = bitPeek(bitrow, lpad);
@@ -528,13 +636,16 @@ static void
 extendRightPbm(unsigned char * const bitrow,
                unsigned int    const lcols,
                unsigned int    const rpad) {
-
-    unsigned int const  rpad0 = (8 - lcols % 8) % 8;
-    unsigned int const  rpad1 = rpad - rpad0;
-    unsigned int const  rpad1CharCt =
+/*----------------------------------------------------------------------------
+  Add 'rpad' pixels of padding the the left side of PBM row 'bitrow',
+  which is 'lcols' columns wide, by duplicating leftmost pixel.
+-----------------------------------------------------------------------------*/
+    unsigned int  const rpad0       = (8 - lcols % 8) % 8;
+    unsigned int  const rpad1       = rpad - rpad0;
+    unsigned int  const rpad1CharCt =
         rpad1 > 0 ? pbm_packed_bytes(rpad1) : 0;
-    unsigned int const  lastColChar = lcols / 8 - (rpad0 == 0 ? 1 : 0);
-    unsigned char const fillColor = bitPeek(bitrow, lcols - 1);
+    unsigned int  const lastColChar = lcols / 8 - (rpad0 == 0 ? 1 : 0);
+    unsigned char const fillColor   = bitPeek(bitrow, lcols - 1);
 
     unsigned int colChar;
 
@@ -542,12 +653,13 @@ extendRightPbm(unsigned char * const bitrow,
         if (fillColor == 0x1) {
             bitrow[lastColChar] =
                 bitrow[lastColChar] | (bitrow[lastColChar] - 1);
-        } else {
-            pbm_cleanrowend_packed(bitrow, lcols);
-            /* Function pbm_cleanrowend_packed() is employed an atypical way
-               here.  It cleans bits beyond the active image content, but the
-               position is not the row end.
-            */
+        } else if (lcols % 8 > 0) {
+            /* This is essentially pbm_cleanrowend_packed(bitrow, lcols); */
+
+            unsigned int const last = pbm_packed_bytes(lcols) - 1;
+
+            bitrow[last] >>= 8 - lcols % 8;
+            bitrow[last] <<= 8 - lcols % 8;
         }
     }
 
@@ -596,7 +708,12 @@ extendEdgePbm(FILE *       const ifP,
     for (row = 0; row < tpad + 1; ++row)
         pbm_writepbmrow_packed(ofP, newbitrow, newcols, 0);
 
-    /* Read rows, shift and write with left and right margins added */
+    /* Read rows, shift and write with left and right margins added.
+
+       Alter left/right margin only when the color of leftmost/rightmost
+       pixel is different from that of the previous row.
+    */
+
     for (row = 1;  row < rows; ++row) {
         pbm_readpbmrow_bitoffset(ifP, newbitrow, cols, format, lpad);
 
@@ -681,7 +798,7 @@ padPbm(FILE *       const ifP,
        unsigned int const rpad,
        unsigned int const tpad,
        unsigned int const bpad,
-       bool         const colorWhite,
+       enum PadType const padType,
        FILE *       const ofP) {
 /*----------------------------------------------------------------------------
   Fast padding routine for PBM
@@ -690,7 +807,7 @@ padPbm(FILE *       const ifP,
     unsigned char * const newrow = pbm_allocrow_packed(newcols);
 
     unsigned char const padChar =
-        0xff * (colorWhite ? PBM_WHITE : PBM_BLACK);
+        0xff * (padType == PAD_WHITE ? PBM_WHITE : PBM_BLACK);
 
     unsigned int const newColChars = pbm_packed_bytes(newcols);
 
@@ -729,56 +846,245 @@ padPbm(FILE *       const ifP,
 
 
 
+static xel
+regressColor(int  const format,
+             int  const maxval,
+             xel  const color) {
+
+    gray const grayval = (gray) ppm_luminosity(color);
+
+    xel retval;
+
+    if (format == PBM_TYPE) {
+        gray const threshold = maxval / 2;
+
+        retval = grayval > threshold ?
+            pnm_whitexel(maxval, format) : pnm_blackxel(maxval, format);
+    } else { /* PGM_TYPE */
+        retval.b = grayval;
+        retval.r = retval.g = 0;
+    }
+    return retval;
+}
+
+
+
+static void
+computeOutputFormatAndBackground(int              const format,
+                                 int              const maxval,
+                                 enum PadType     const padType,
+                                 const char *     const colorStr,
+                                 enum PromoteType const promoteType,
+                                 int *            const newformatP,
+                                 pixval *         const newmaxvalP,
+                                 xel *            const backgroundP) {
+
+    int    newformat;
+    xelval newmaxval;
+    xel    background;
+
+    switch (promoteType) {
+    case PROMOTE_NONE:
+        newformat = format;
+        newmaxval = maxval;
+
+        switch (padType) {
+        case PAD_BLACK:
+            background = pnm_blackxel(newmaxval, newformat);
+            break;
+        case PAD_WHITE:
+            background = pnm_whitexel(newmaxval, newformat);
+            break;
+        case PAD_GRAY:
+        case PAD_COLOR:
+            background = regressColor(format, maxval,
+                                      ppm_parsecolor(colorStr, newmaxval));
+            break;
+        case PAD_DETECTED_BG:
+        case PAD_EXTEND_EDGE:
+            pm_error("internal error -- impossible value of 'promoteType' "
+                     "in computeOutputFormatAndBackground");
+        } break;
+
+    case PROMOTE_FORMAT:
+    case PROMOTE_ALL: {
+        newmaxval = promoteType == PROMOTE_ALL ? MAX(255, maxval) : maxval;
+
+        switch (padType) {
+        case PAD_BLACK:
+            newformat = format;
+            background = pnm_blackxel(newmaxval, newformat);
+            break;
+        case PAD_WHITE:
+            newformat = format;
+            background = pnm_whitexel(newmaxval, newformat);
+            break;
+        case PAD_GRAY:
+            newformat =
+                PNM_FORMAT_TYPE(format) == PBM_TYPE ? PGM_TYPE : format;
+            background = ppm_parsecolor(colorStr, newmaxval);
+            break;
+        case PAD_COLOR:
+            newformat = PPM_TYPE;
+            background = ppm_parsecolor(colorStr, newmaxval);
+            break;
+        case PAD_DETECTED_BG:
+        case PAD_EXTEND_EDGE:
+            pm_error("internal error -- impossible value of 'promoteType' "
+                     "in computeOutputFormatAndBackground");
+        } break;
+    }
+    }
+
+    *newformatP  = newformat;
+    *newmaxvalP  = newmaxval;
+    *backgroundP = background;
+}
+
+
+
+static void
+padDetectedBg(FILE *       const ifP,
+              unsigned int const cols,
+              unsigned int const rows,
+              xelval       const maxval,
+              int          const format,
+              unsigned int const newcols,
+              unsigned int const lpad,
+              unsigned int const rpad,
+              unsigned int const tpad,
+              unsigned int const bpad,
+              FILE *       const ofP) {
+/*----------------------------------------------------------------------------
+  Pad using top left pixel as background color.
+
+  (There is no special PBM routine for this case)
+-----------------------------------------------------------------------------*/
+    xel * const bgrow  = pnm_allocrow(newcols);
+        /* A row of background color (for top and bottom padding) */
+    xel * const xelrow = pnm_allocrow(newcols);
+        /* A row of padded output image.  Left and right padding is
+           constant; middle varies from row to row.
+        */
+    xel * const window = &xelrow[lpad];
+        /* The foreground part of the current row */
+
+    unsigned int row;;
+
+    pnm_writepnminit(ofP, newcols, rows + tpad + bpad, maxval, format, 0);
+
+    pnm_readpnmrow(ifP, window, cols, maxval, format);
+
+    {
+        /* Set 'bgrow' and constant parts of 'xelrow' */
+        xel const background = window[0];
+
+        unsigned int col;
+
+        for (col = 0; col < newcols; ++col)
+            bgrow[col] = background;
+
+        for (col = 0; col < lpad; ++col)
+            xelrow[col] = background;
+
+        for (col = lpad + cols; col < newcols; ++col)
+            xelrow[col] = background;
+    }
+
+    /* Write top padding */
+    for (row = 0; row < tpad; ++row)
+        pnm_writepnmrow(ofP, bgrow, newcols, maxval, format, 0);
+
+    /* Write body of image */
+    pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0);
+
+    for (row = 1; row < rows; ++row) {
+        pnm_readpnmrow(ifP, window, cols, maxval, format);
+        pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0);
+    }
+    pnm_freerow(xelrow);
+
+    /* Write bottom padding */
+    for (row = 0; row < bpad; ++row)
+        pnm_writepnmrow(ofP, bgrow, newcols, maxval, format, 0);
+
+    pnm_freerow(bgrow);
+}
+
+
+
 static void
-padGeneral(FILE *       const ifP,
-           unsigned int const cols,
-           unsigned int const rows,
-           xelval       const maxval,
-           int          const format,
-           unsigned int const newcols,
-           unsigned int const lpad,
-           unsigned int const rpad,
-           unsigned int const tpad,
-           unsigned int const bpad,
-           bool         const colorWhite,
-           FILE *       const ofP) {
+padGeneral(FILE *           const ifP,
+           unsigned int     const cols,
+           unsigned int     const rows,
+           xelval           const maxval,
+           int              const format,
+           unsigned int     const newcols,
+           unsigned int     const lpad,
+           unsigned int     const rpad,
+           unsigned int     const tpad,
+           unsigned int     const bpad,
+           enum PadType     const padType,
+           const char *     const colorStr,
+           enum PromoteType const promoteType,
+           FILE *           const ofP) {
 /*----------------------------------------------------------------------------
   General padding routine (logic works for PBM)
 -----------------------------------------------------------------------------*/
-    xel * const bgrow      = pnm_allocrow(newcols);
-    xel const   background = colorWhite ?
-        pnm_whitexel(maxval, format) : pnm_blackxel(maxval, format);
+    xel * const bgrow = pnm_allocrow(newcols);
+        /* A row of background color (for top and bottom padding) */
+
+    int    newformat;
+    pixval newmaxval;
+    xel    background;
 
     unsigned int row, col;
 
+    computeOutputFormatAndBackground(
+        format, maxval, padType, colorStr, promoteType,
+        &newformat, &newmaxval, &background);
+
+    if (newformat != format)
+        pm_message("Promoting to %s", pnm_formattypenm(newformat));
+
     for (col = 0; col < newcols; ++col)
         bgrow[col] = background;
 
-    pnm_writepnminit(ofP, newcols, rows + tpad + bpad, maxval, format, 0);
+    pnm_writepnminit(ofP, newcols, rows + tpad + bpad,
+                     newmaxval, newformat, 0);
 
     /* Write top padding */
 
     for (row = 0; row < tpad; ++row)
-        pnm_writepnmrow(ofP, bgrow, newcols, maxval, format, 0);
+        pnm_writepnmrow(ofP, bgrow, newcols, newmaxval, newformat, 0);
 
     /* Write body of image */
     {
         xel * const xelrow = pnm_allocrow(newcols);
+            /* A row of padded output image.  Left and right padding is
+               constant; middle varies from row to row.
+            */
+        xel * const window = &xelrow[lpad];
+            /* The foreground part of the current row */
 
-        /* initial value for 'xelrow': all background */
+        /* Initial value for 'xelrow': all background */
         for (col = 0; col < newcols; ++col)
             xelrow[col] = bgrow[col] = background;
 
         for (row = 0; row < rows; ++row) {
-            pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format);
-            pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0);
+            pnm_readpnmrow(ifP, window, cols, maxval, format);
+            if (maxval != newmaxval || format != newformat) {
+                pnm_promoteformatrow(window, cols, maxval, format,
+                                     newmaxval, newformat);
+            }
+            pnm_writepnmrow(ofP, xelrow, newcols, newmaxval, newformat, 0);
         }
         pnm_freerow(xelrow);
     }
 
     /* Write bottom padding */
     for (row = 0; row < bpad; ++row)
-        pnm_writepnmrow(ofP, bgrow, newcols, maxval, format, 0);
+        pnm_writepnmrow(ofP, bgrow, newcols, newmaxval, newformat, 0);
 
     pnm_freerow(bgrow);
 }
@@ -805,7 +1111,8 @@ main(int argc, const char ** argv) {
 
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
 
-    if (cmdline.verbose) pm_message("image WxH = %dx%d", cols, rows);
+    if (cmdline.verbose)
+        pm_message("image WxH = %dx%d", cols, rows);
 
     computePadSizes(cmdline, cols, rows, &lpad, &rpad, &tpad, &bpad);
 
@@ -813,21 +1120,35 @@ main(int argc, const char ** argv) {
 
     if (cmdline.reportonly)
         reportPadSizes(cols, rows, lpad, rpad, tpad, bpad);
-    else if (cmdline.extend_edge) {
-        if (PNM_FORMAT_TYPE(format) == PBM_TYPE)
-            extendEdgePbm(ifP, cols, rows, format,
-                          newcols, lpad, rpad, tpad, bpad, stdout);
-        else
-            extendEdgeGeneral(ifP, cols, rows, maxval, format,
+    else {
+        switch (cmdline.padType) {
+        case PAD_EXTEND_EDGE:
+            if (PNM_FORMAT_TYPE(format) == PBM_TYPE)
+                extendEdgePbm(ifP, cols, rows, format,
                               newcols, lpad, rpad, tpad, bpad, stdout);
-    } else {
-        if (PNM_FORMAT_TYPE(format) == PBM_TYPE)
-            padPbm(ifP, cols, rows, format,
-                   newcols, lpad, rpad, tpad, bpad, !!cmdline.white, stdout);
-        else
-            padGeneral(ifP, cols, rows, maxval, format,
-                       newcols, lpad, rpad, tpad, bpad, !!cmdline.white,
+            else
+                extendEdgeGeneral(ifP, cols, rows, maxval, format,
+                                  newcols, lpad, rpad, tpad, bpad, stdout);
+            break;
+        case PAD_DETECTED_BG:
+            padDetectedBg(ifP, cols, rows, maxval, format,
+                          newcols, lpad, rpad, tpad, bpad, stdout);
+            break;
+        case PAD_BLACK:
+        case PAD_WHITE:
+        case PAD_GRAY:
+        case PAD_COLOR:
+            if (PNM_FORMAT_TYPE(format) == PBM_TYPE &&
+                (cmdline.padType == PAD_WHITE || cmdline.padType == PAD_BLACK))
+                padPbm(ifP, cols, rows, format,
+                       newcols, lpad, rpad, tpad, bpad, cmdline.padType,
                        stdout);
+            else
+                padGeneral(ifP, cols, rows, maxval, format,
+                           newcols, lpad, rpad, tpad, bpad, cmdline.padType,
+                           cmdline.color, cmdline.promote, stdout);
+            break;
+        }
     }
 
     pm_close(ifP);