diff -up netpbm-10.47.05/editor/pnmmontage.c.pnmmontagefix netpbm-10.47.05/editor/pnmmontage.c --- netpbm-10.47.05/editor/pnmmontage.c.pnmmontagefix 2009-12-10 08:34:32.000000000 +0100 +++ netpbm-10.47.05/editor/pnmmontage.c 2010-03-16 20:47:38.000000000 +0100 @@ -10,136 +10,19 @@ * implied warranty. */ -#define _BSD_SOURCE /* Make sure strdup() is in */ -#include #include #include -#include "pm_c_util.h" -#include "mallocvar.h" -#include "nstring.h" -#include "shhopt.h" #include "pam.h" +#include "shhopt.h" +#include "nstring.h" +#include "mallocvar.h" +typedef struct { int f[sizeof(int) * 8 + 1]; } factorset; +typedef struct { int x; int y; } coord; - -struct cmdlineInfo { - const char * header; - const char * data; - const char * prefix; - unsigned int quality; - unsigned int quality2; - unsigned int nFiles; - const char ** inFileName; -}; - - - -static void -parseCommandLine(int argc, const char ** argv, - struct cmdlineInfo * const cmdlineP) { -/*---------------------------------------------------------------------------- - parse program command line described in Unix standard form by argc - and argv. Return the information in the options as *cmdlineP. - - If command line is internally inconsistent (invalid options, etc.), - issue error message to stderr and abort program. - - Note that the strings we return are stored in the storage that - was passed to us as the argv array. We also trash *argv. ------------------------------------------------------------------------------*/ - optEntry * option_def; - /* Instructions to OptParseOptions3 on how to parse our options. */ - optStruct3 opt; - unsigned int dataSpec, headerSpec, prefixSpec, qualitySpec; - unsigned int option_def_index; - unsigned int i; - unsigned int q[10]; - - MALLOCARRAY_NOFAIL(option_def, 100); - - option_def_index = 0; /* incremented by OPTENTRY */ - OPTENT3( 0, "data", OPT_STRING, &cmdlineP->data, &dataSpec, 0); - OPTENT3( 0, "header", OPT_STRING, &cmdlineP->header, &headerSpec, 0); - OPTENT3('q', "quality", OPT_UINT, &cmdlineP->quality, &qualitySpec, 0); - OPTENT3('p', "prefix", OPT_STRING, &cmdlineP->prefix, &prefixSpec, 0); - OPTENT3('0', "0", OPT_FLAG, NULL, &q[0], 0); - OPTENT3('1', "1", OPT_FLAG, NULL, &q[1], 0); - OPTENT3('2', "2", OPT_FLAG, NULL, &q[2], 0); - OPTENT3('3', "3", OPT_FLAG, NULL, &q[3], 0); - OPTENT3('4', "4", OPT_FLAG, NULL, &q[4], 0); - OPTENT3('5', "5", OPT_FLAG, NULL, &q[5], 0); - OPTENT3('6', "6", OPT_FLAG, NULL, &q[6], 0); - OPTENT3('7', "7", OPT_FLAG, NULL, &q[7], 0); - OPTENT3('8', "8", OPT_FLAG, NULL, &q[8], 0); - OPTENT3('9', "9", OPT_FLAG, NULL, &q[9], 0); - - opt.opt_table = option_def; - opt.short_allowed = FALSE; - opt.allowNegNum = FALSE; - - optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0); - - if (!dataSpec) - cmdlineP->data = NULL; - if (!headerSpec) - cmdlineP->header = NULL; - if (!prefixSpec) - cmdlineP->prefix = ""; - if (!qualitySpec) - cmdlineP->quality = 200; - - - /* cmdlineP->quality2 is the greatest number from the --1, --2, etc. - options, or 5 if none of those are specified. - */ - cmdlineP->quality2 = 5; /* initial value */ - for (i = 0; i < 10; ++i) { - if (q[i]) - cmdlineP->quality2 = i; - } - - cmdlineP->nFiles = argc-1; - - MALLOCARRAY_NOFAIL(cmdlineP->inFileName, argc-1); - - for (i = 0; i < argc-1; ++i) { - if (cmdlineP->data && strchr(argv[i+1], ':')) - pm_error("Filename '%s' contains a \":\", which is forbidden " - "with -data", argv[i+1]); - else - cmdlineP->inFileName[i] = strdup(argv[1+1]); - } -} - - - -typedef struct { - int f[sizeof(int) * 8 + 1]; -} factorset; - -typedef struct { - int x; int y; -} coord; - -typedef struct { - coord ul; - coord size; -} rectangle; - -static coord -lr(rectangle const r) { -/*---------------------------------------------------------------------------- - Return the coordinates of the lower right corner of 'r' - (i.e. the pixel just beyond the lowest rightmost one). ------------------------------------------------------------------------------*/ - coord retval; - - retval.x = r.ul.x + r.size.x; - retval.y = r.ul.y + r.size.y; - - return retval; -} +static int qfactor = 200; +static int quality = 5; static factorset factor(int n) @@ -180,151 +63,109 @@ gcd(int n, int m) return (g); } +static __inline__ int imax(int n, int m) { return (n > m ? n : m); } - -static bool -overlaps(rectangle const a, - rectangle const b) { - - return - (a.ul.x < lr(b).x && a.ul.y < lr(b).y) && - (lr(a).x > b.ul.x && lr(a).y > b.ul.y); -} - - - -static bool -collides(rectangle const test, - const rectangle * const fieldList, - unsigned int const n) { -/*---------------------------------------------------------------------------- - Return true iff the rectangle 'test' overlaps any of the 'n' rectangles - fieldList[]. ------------------------------------------------------------------------------*/ - unsigned int i; - - for (i = 0; i < n; ++i) - if (overlaps(fieldList[i], test)) - return true; - - return false; +static int +checkcollision(coord *locs, coord *szs, coord *cloc, coord *csz, int n) +{ + int i; + for (i = 0; i < n; ++i) + { + if ((locs[i].x < cloc->x + csz->x) && + (locs[i].y < cloc->y + csz->y) && + (locs[i].x + szs[i].x > cloc->x) && + (locs[i].y + szs[i].y > cloc->y)) + return (1); + } + return (0); } - - static void -recursefindpack(rectangle * const current, - coord const currentsz, - coord * const best, - unsigned int const minarea, - unsigned int * const maxareaP, - unsigned int const depth, - unsigned int const n, - unsigned int const xinc, - unsigned int const yinc, - unsigned int const quality, - unsigned int const qfactor) { - - if (depth == n) { - if (currentsz.x * currentsz.y < *maxareaP) { - unsigned int i; - for (i = 0; i < n; ++i) - best[i] = current[i].ul; - *maxareaP = currentsz.x * currentsz.y; - } - } else { - unsigned int i; - - rectangle * const newP = ¤t[depth]; +recursefindpack(coord *current, coord currentsz, coord *set, + coord *best, int minarea, int *maxarea, + int depth, int n, int xinc, int yinc) +{ + coord c; + if (depth == n) + { + if (currentsz.x * currentsz.y < *maxarea) + { + memcpy(best, current, sizeof(coord) * n); + *maxarea = currentsz.x * currentsz.y; + } + return; + } - for (i = 0; ; ++i) { - for (newP->ul.x = 0, newP->ul.y = i * yinc; - newP->ul.y <= i * yinc;) { - - coord c; - - c.x = MAX(lr(*newP).x, currentsz.x); - c.y = MAX(lr(*newP).y, currentsz.y); - pm_message("current = (%u.%u, %u.%u) new = (%u.%u, %u.%u)", - current[0].ul.x, current[0].size.x, - current[0].ul.y, current[0].size.y, - newP->ul.x, newP->size.x, - newP->ul.y, newP->size.y); - if (!collides(*newP, current, depth)) { - pm_message("Depth %u: Doesn't collide at i=%u", depth,i); - recursefindpack(current, c, best, minarea, maxareaP, - depth + 1, n, xinc, yinc, - quality, qfactor); - if (*maxareaP <= minarea) - return; - } - if (newP->ul.x == (i - 1) * xinc) - newP->ul.y = 0; - if (newP->ul.x < i * xinc) - newP->ul.x += xinc; - else - newP->ul.y += yinc; - } - } + for (current[depth].x = 0; + imax(current[depth].x + set[depth].x, currentsz.x) * + imax(currentsz.y, set[depth].y) < *maxarea; + current[depth].x += xinc) + { + for (current[depth].y = 0; + imax(current[depth].x + set[depth].x, currentsz.x) * + imax(currentsz.y, current[depth].y + set[depth].y) < *maxarea; + current[depth].y += yinc) + { + c.x = imax(current[depth].x + set[depth].x, currentsz.x); + c.y = imax(current[depth].y + set[depth].y, currentsz.y); + if (!checkcollision(current, set, ¤t[depth], &set[depth], depth)) + { + recursefindpack(current, c, set, best, minarea, maxarea, + depth + 1, n, xinc, yinc); + if (*maxarea <= minarea) + return; + } } + } } - - static void -findpack(struct pam * const imgs, - unsigned int const n, - coord * const coords, - unsigned int const quality, - unsigned int const qfactor) { +findpack(struct pam *imgs, int n, coord *coords) +{ + int minarea; + int i; + int rdiv; + int cdiv; + int minx = -1; + int miny = -1; + coord *current; + coord *set; + int z = INT_MAX; + coord c = { 0, 0 }; - int minarea; - int i; - int rdiv; - int cdiv; - int minx; - int miny; - rectangle * current; - unsigned int z; - coord c; - - minx = -1; miny = -1; /* initial value */ - z = UINT_MAX; /* initial value */ - c.x = 0; c.y = 0; /* initial value */ - - if (quality > 1) { - unsigned int realMinarea; - for (realMinarea = i = 0; i < n; ++i) - realMinarea += imgs[i].height * imgs[i].width, - minx = MAX(minx, imgs[i].width), - miny = MAX(miny, imgs[i].height); - - minarea = realMinarea * qfactor / 100; - } else { - minarea = INT_MAX - 1; - } + if (quality > 1) + { + for (minarea = i = 0; i < n; ++i) + minarea += imgs[i].height * imgs[i].width, + minx = imax(minx, imgs[i].width), + miny = imax(miny, imgs[i].height); - /* It's relatively easy to show that, if all the images - * are multiples of a particular size, then a best - * packing will always align the images on a grid of - * that size. - * - * This speeds computation immensely. - */ - for (rdiv = imgs[0].height, i = 1; i < n; ++i) - rdiv = gcd(imgs[i].height, rdiv); - - for (cdiv = imgs[0].width, i = 1; i < n; ++i) - cdiv = gcd(imgs[i].width, cdiv); - - MALLOCARRAY(current, n); - - for (i = 0; i < n; ++i) { - current[i].size.x = imgs[i].width; - current[i].size.y = imgs[i].height; - } - recursefindpack(current, c, coords, minarea, &z, 0, n, cdiv, rdiv, - quality, qfactor); + minarea = minarea * qfactor / 100; + } + else + { + minarea = INT_MAX - 1; + } + + /* It's relatively easy to show that, if all the images + * are multiples of a particular size, then a best + * packing will always align the images on a grid of + * that size. + * + * This speeds computation immensely. + */ + for (rdiv = imgs[0].height, i = 1; i < n; ++i) + rdiv = gcd(imgs[i].height, rdiv); + + for (cdiv = imgs[0].width, i = 1; i < n; ++i) + cdiv = gcd(imgs[i].width, cdiv); + + MALLOCARRAY(current, n); + MALLOCARRAY(set, n); + for (i = 0; i < n; ++i) + set[i].x = imgs[i].width, + set[i].y = imgs[i].height; + recursefindpack(current, c, set, coords, minarea, &z, 0, n, cdiv, rdiv); } @@ -396,264 +237,204 @@ writePam(struct pam * const outpam -static void -writeData(FILE * const dataFileP, - unsigned int const width, - unsigned int const height, - unsigned int const nfiles, - const char ** const names, - const coord * const coords, - const struct pam * const imgs) { - - unsigned int i; - - fprintf(dataFileP, ":0:0:%u:%u\n", width, height); - - for (i = 0; i < nfiles; ++i) { - fprintf(dataFileP, "%s:%u:%u:%u:%u\n", names[i], coords[i].x, - coords[i].y, imgs[i].width, imgs[i].height); - } -} - - - -static void -writeHeader(FILE * const headerFileP, - const char * const prefix, - unsigned int const width, - unsigned int const height, - unsigned int const nfiles, - const char ** const names, - const coord * const coords, - const struct pam * imgs) { - - unsigned int i; - - fprintf(headerFileP, "#define %sOVERALLX %u\n", prefix, width); - - fprintf(headerFileP, "#define %sOVERALLY %u\n", prefix, height); - - fprintf(headerFileP, "\n"); - - for (i = 0; i < nfiles; ++i) { - char * const buffer = strdup(names[i]); - coord const coord = coords[i]; - struct pam const img = imgs[i]; - - unsigned int j; - - *strchr(buffer, '.') = 0; - for (j = 0; buffer[j]; ++j) { - if (ISLOWER(buffer[j])) - buffer[j] = TOUPPER(buffer[j]); - } - fprintf(headerFileP, "#define %s%sX %u\n", - prefix, buffer, coord.x); - - fprintf(headerFileP, "#define %s%sY %u\n", - prefix, buffer, coord.y); - - fprintf(headerFileP, "#define %s%sSZX %u\n", - prefix, buffer, img.width); - - fprintf(headerFileP, "#define %s%sSZY %u\n", - prefix, buffer, img.height); - - fprintf(headerFileP, "\n"); - } -} +int +main(int argc, char **argv) +{ + struct pam *imgs; + struct pam outimg; + struct pam p; + int nfiles; + int i, j; + unsigned int q[10]; + coord *coords; + const char *headfname = NULL; + const char *datafname = NULL; + const char *prefix = ""; + FILE *header; + FILE *data; + char **names; + char *c; + + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3( 0, "data", OPT_STRING, &datafname, NULL, 0); + OPTENT3( 0, "header", OPT_STRING, &headfname, NULL, 0); + OPTENT3('q', "quality", OPT_UINT, &qfactor, NULL, 0); + OPTENT3('p', "prefix", OPT_STRING, &prefix, NULL, 0); + OPTENT3('0', "0", OPT_FLAG, NULL, &q[0], 0); + OPTENT3('1', "1", OPT_FLAG, NULL, &q[1], 0); + OPTENT3('2', "2", OPT_FLAG, NULL, &q[2], 0); + OPTENT3('3', "3", OPT_FLAG, NULL, &q[3], 0); + OPTENT3('4', "4", OPT_FLAG, NULL, &q[4], 0); + OPTENT3('5', "5", OPT_FLAG, NULL, &q[5], 0); + OPTENT3('6', "6", OPT_FLAG, NULL, &q[6], 0); + OPTENT3('7', "7", OPT_FLAG, NULL, &q[7], 0); + OPTENT3('8', "8", OPT_FLAG, NULL, &q[8], 0); + OPTENT3('9', "9", OPT_FLAG, NULL, &q[9], 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; + opt.allowNegNum = FALSE; + + pnm_init(&argc, argv); + + /* Check for flags. */ + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + if (headfname) + header = pm_openw(headfname); + if (datafname) + data = pm_openw(datafname); -static void -sortImagesByArea(unsigned int const nfiles, - struct pam * const imgs, - const char ** const names) { -/*---------------------------------------------------------------------------- - Sort the images described by 'imgs' and 'names' in place, from largest - area to smallest. ------------------------------------------------------------------------------*/ - /* Bubble sort */ - - unsigned int i; - - for (i = 0; i < nfiles - 1; ++i) { - unsigned int j; - for (j = i + 1; j < nfiles; ++j) { - if (imgs[j].width * imgs[j].height > - imgs[i].width * imgs[i].height) { - - struct pam p; - const char * c; - - p = imgs[i]; imgs[i] = imgs[j]; imgs[j] = p; - c = names[i]; names[i] = names[j]; names[j] = c; - } - } + for (i = 0; i < 10; ++i) + { + if (q[i]) + { + quality = i; + switch (quality) + { + case 0: case 1: break; + case 2: case 3: case 4: case 5: case 6: + qfactor = 100 * (8 - quality); + break; + case 7: qfactor = 150; break; + case 8: qfactor = 125; break; + case 9: qfactor = 100; break; + } } -} - + } + if (1 < argc) + nfiles = argc - 1; + else + nfiles = 1; + + MALLOCARRAY(imgs, nfiles); + MALLOCARRAY(coords, nfiles); + MALLOCARRAY(names, nfiles); + + if (!imgs || !coords || !names) + pm_error("out of memory"); -static void -computeOutputType(sample * const maxvalP, - int * const formatP, - char * const tupleTypeP, - unsigned int * const depthP, - unsigned int const nfiles, - const struct pam * const imgs) { - - unsigned int i; - - sample maxval; - int format; - const char * tupleType; - unsigned int depth; - - assert(nfiles > 0); - - /* initial guesses */ - maxval = imgs[0].maxval; - format = imgs[0].format; - depth = imgs[0].depth; - tupleType = imgs[0].tuple_type; - - for (i = 1; i < nfiles; ++i) { - if (PAM_FORMAT_TYPE(imgs[i].format) > PAM_FORMAT_TYPE(format)) { - format = imgs[i].format; - tupleType = imgs[i].tuple_type; - } - maxval = MAX(maxval, imgs[i].maxval); - depth = MAX(depth, imgs[i].depth); + if (1 < argc) + { + for (i = 0; i < nfiles; ++i) + { + if (strchr(argv[i+1], ':')) + { + imgs[i].file = pm_openr(strchr(argv[i+1], ':') + 1); + *strchr(argv[i+1], ':') = 0; + names[i] = argv[i+1]; + } + else + { + imgs[i].file = pm_openr(argv[i+1]); + names[i] = argv[i+1]; + } } + } + else + { + imgs[0].file = stdin; + } - *maxvalP = maxval; - *formatP = format; - *depthP = depth; - memcpy(tupleTypeP, tupleType, sizeof(imgs[0].tuple_type)); -} + pnm_readpaminit(imgs[0].file, &imgs[0], PAM_STRUCT_SIZE(tuple_type)); + memset(&outimg, 0, sizeof(outimg)); + outimg.maxval = imgs[0].maxval; + outimg.format = imgs[0].format; + memcpy(outimg.tuple_type, imgs[0].tuple_type, sizeof(imgs[0].tuple_type)); + outimg.depth = imgs[0].depth; + for (i = 1; i < nfiles; ++i) + { + pnm_readpaminit(imgs[i].file, &imgs[i], PAM_STRUCT_SIZE(tuple_type)); + if (PAM_FORMAT_TYPE(imgs[i].format) > PAM_FORMAT_TYPE(outimg.format)) + outimg.format = imgs[i].format, + memcpy(outimg.tuple_type, imgs[i].tuple_type, + sizeof(imgs[i].tuple_type)); + outimg.maxval = imax(imgs[i].maxval, outimg.maxval); + outimg.depth = imax(imgs[i].depth, outimg.depth); + } + for (i = 0; i < nfiles - 1; ++i) + for (j = i + 1; j < nfiles; ++j) + if (imgs[j].width * imgs[j].height > imgs[i].width * imgs[i].height) + p = imgs[i], imgs[i] = imgs[j], imgs[j] = p, + c = names[i], names[i] = names[j], names[j] = c; -static void -computeOutputDimensions(int * const widthP, - int * const heightP, - unsigned int const nfiles, - const struct pam * const imgs, - const coord * const coords) { - - unsigned int widthGuess, heightGuess; - unsigned int i; - - widthGuess = 0; /* initial value */ - heightGuess = 0; /* initial value */ - - for (i = 0; i < nfiles; ++i) { - widthGuess = MAX(widthGuess, imgs[i].width + coords[i].x); - heightGuess = MAX(heightGuess, imgs[i].height + coords[i].y); - } + findpack(imgs, nfiles, coords); - *widthP = widthGuess; - *heightP = heightGuess; -} + outimg.height = outimg.width = 0; + for (i = 0; i < nfiles; ++i) + { + outimg.width = imax(outimg.width, imgs[i].width + coords[i].x); + outimg.height = imax(outimg.height, imgs[i].height + coords[i].y); + } + outimg.size = sizeof(outimg); + outimg.len = sizeof(outimg); + outimg.file = stdout; + outimg.bytes_per_sample = 0; + for (i = outimg.maxval; i; i >>= 8) + ++outimg.bytes_per_sample; + writePam(&outimg, nfiles, coords, imgs); -int -main(int argc, const char **argv) { + if (datafname) + { + fprintf(data, ":0:0:%u:%u\n", outimg.width, outimg.height); - struct cmdlineInfo cmdline; - struct pam * imgs; - struct pam outimg; - unsigned int nfiles; - coord * coords; - FILE * header; - FILE * data; - const char ** names; - unsigned int i; - unsigned int qfactor; /* In per cent */ - - pm_proginit(&argc, argv); - - parseCommandLine(argc, argv, &cmdline); - - header = cmdline.header ? pm_openw(cmdline.header) : NULL; - data = cmdline.data ? pm_openw(cmdline.data) : NULL; - - switch (cmdline.quality2) { - case 0: case 1: - qfactor = cmdline.quality; - break; - case 2: case 3: case 4: case 5: case 6: - qfactor = 100 * (8 - cmdline.quality2); - break; - case 7: qfactor = 150; break; - case 8: qfactor = 125; break; - case 9: qfactor = 100; break; - default: pm_error("Internal error - impossible value of 'quality2': %u", - cmdline.quality2); + for (i = 0; i < nfiles; ++i) + { + fprintf(data, "%s:%u:%u:%u:%u\n", names[i], coords[i].x, + coords[i].y, imgs[i].width, imgs[i].height); } + } - nfiles = cmdline.nFiles > 0 ? cmdline.nFiles : 1; - - MALLOCARRAY(imgs, nfiles); - MALLOCARRAY(coords, nfiles); - MALLOCARRAY(names, nfiles); - - if (!imgs || !coords || !names) - pm_error("out of memory"); - - if (cmdline.nFiles > 0) { - unsigned int i; - - for (i = 0; i < cmdline.nFiles; ++i) { - imgs[i].file = pm_openr(cmdline.inFileName[i]); - names[i] = strdup(cmdline.inFileName[i]); - } - } else { - imgs[0].file = stdin; - names[0] = strdup("stdin"); - } + if (headfname) + { + fprintf(header, "#define %sOVERALLX %u\n" + "#define %sOVERALLY %u\n" + "\n", + prefix, outimg.width, + prefix, outimg.height); for (i = 0; i < nfiles; ++i) - pnm_readpaminit(imgs[i].file, &imgs[i], PAM_STRUCT_SIZE(tuple_type)); - - sortImagesByArea(nfiles, imgs, names); - - findpack(imgs, nfiles, coords, cmdline.quality2, qfactor); - - computeOutputType(&outimg.maxval, &outimg.format, outimg.tuple_type, - &outimg.depth, nfiles, imgs); - - computeOutputDimensions(&outimg.width, &outimg.height, nfiles, - imgs, coords); - - pnm_setminallocationdepth(&outimg, outimg.depth); - - outimg.size = sizeof(outimg); - outimg.len = sizeof(outimg); - outimg.file = stdout; - outimg.bytes_per_sample = 0; - for (i = outimg.maxval; i; i >>= 8) - ++outimg.bytes_per_sample; - - writePam(&outimg, nfiles, coords, imgs); + { + *strchr(names[i], '.') = 0; + for (j = 0; names[i][j]; ++j) + { + if (ISLOWER(names[i][j])) + names[i][j] = TOUPPER(names[i][j]); + } + fprintf(header, "#define %s%sX %u\n" + "#define %s%sY %u\n" + "#define %s%sSZX %u\n" + "#define %s%sSZY %u\n" + "\n", + prefix, names[i], coords[i].x, + prefix, names[i], coords[i].y, + prefix, names[i], imgs[i].width, + prefix, names[i], imgs[i].height); + } + } - if (data) - writeData(data, outimg.width, outimg.height, - nfiles, names, coords, imgs); + for (i = 0; i < nfiles; ++i) + pm_close(imgs[i].file); + pm_close(stdout); - if (header) - writeHeader(header, cmdline.prefix, outimg.width, outimg.height, - nfiles, names, coords, imgs); + if (headfname) + pm_close(header); - for (i = 0; i < nfiles; ++i) - pm_close(imgs[i].file); - pm_close(stdout); - if (header) - pm_close(header); - if (data) - pm_close(data); + if (datafname) + pm_close(data); - return 0; + return 0; }