Blame converter/ppm/ppmtoarbtxt.c

Packit 78deda
/* ppmtoarbtxt.c - convert PPM to a custom text-based format
Packit 78deda
**
Packit 78deda
** Renamed from ppmtotxt.c by Bryan Henderson in January 2003.
Packit 78deda
**
Packit 78deda
** Copyright (C) 1995 by Peter Kirchgessner
Packit 78deda
**
Packit 78deda
** Permission to use, copy, modify, and distribute this software and its
Packit 78deda
** documentation for any purpose and without fee is hereby granted, provided
Packit 78deda
** that the above copyright notice appear in all copies and that both that
Packit 78deda
** copyright notice and this permission notice appear in supporting
Packit 78deda
** documentation.  This software is provided "as is" without express or
Packit 78deda
** implied warranty.
Packit 78deda
*/
Packit 78deda
Packit 78deda
#include <assert.h>
Packit 78deda
#include <string.h>
Packit 78deda
#ifdef __GLIBC__
Packit 78deda
  #include <printf.h>  /* Necessary for parse_printf_format() */
Packit 78deda
#endif
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "nstring.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "ppm.h"
Packit 78deda
Packit 78deda
/* HAVE_PARSE_PRINTF_FORMAT means the system library has
Packit 78deda
   parse_printf_format(), declared in <printf.h>.  This essentially means
Packit 78deda
   systems with GNU libc.
Packit 78deda
*/
Packit 78deda
Packit 78deda
#ifndef HAVE_PARSE_PRINTF_FORMAT
Packit 78deda
  #ifdef PA_FLAG_MASK                   /* Defined in printf.h */
Packit 78deda
    #define HAVE_PARSE_PRINTF_FORMAT 1
Packit 78deda
  #else
Packit 78deda
    #define HAVE_PARSE_PRINTF_FORMAT 0
Packit 78deda
  #endif
Packit 78deda
#endif
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct CmdlineInfo {
Packit 78deda
    /* All the information the user supplied in the command line,
Packit 78deda
       in a form easy for the program to use.
Packit 78deda
    */
Packit 78deda
    const char * inputFileName;
Packit 78deda
    const char * bodySklFileName;
Packit 78deda
    const char * hd;
Packit 78deda
    const char * tl;
Packit 78deda
    unsigned int debug;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int argc, const char ** argv,
Packit 78deda
                 struct CmdlineInfo * const cmdlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Note that many of the strings that this function returns in the
Packit 78deda
   *cmdline_p structure are actually in the supplied argv array.  And
Packit 78deda
   sometimes, one of these strings is actually just a suffix of an entry
Packit 78deda
   in argv!
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    optEntry * option_def;
Packit 78deda
        /* Instructions to OptParseOptions3 on how to parse our options.
Packit 78deda
         */
Packit 78deda
    optStruct3 opt;
Packit 78deda
Packit 78deda
    unsigned int hdSpec, tlSpec;
Packit 78deda
Packit 78deda
    unsigned int option_def_index;
Packit 78deda
    
Packit 78deda
    MALLOCARRAY(option_def, 100);
Packit 78deda
Packit 78deda
    option_def_index = 0;   /* incremented by OPTENTRY */
Packit 78deda
    OPTENT3(0,   "hd",   OPT_STRING, &cmdlineP->hd, 
Packit 78deda
            &hdSpec,             0);
Packit 78deda
    OPTENT3(0,   "tl",   OPT_STRING, &cmdlineP->tl,
Packit 78deda
            &tlSpec,             0);
Packit 78deda
    OPTENT3(0,   "debug", OPT_FLAG, NULL,
Packit 78deda
            &cmdlineP->debug,      0);
Packit 78deda
Packit 78deda
    opt.opt_table = option_def;
Packit 78deda
    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
Packit 78deda
    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
Packit 78deda
Packit 78deda
    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
Packit 78deda
    free(option_def);
Packit 78deda
Packit 78deda
    if (!hdSpec)
Packit 78deda
        cmdlineP->hd = NULL;
Packit 78deda
Packit 78deda
    if (!tlSpec)
Packit 78deda
        cmdlineP->tl = NULL;
Packit 78deda
Packit 78deda
    if (argc-1 < 1)
Packit 78deda
        pm_error("You must specify the body skeleton file name as an "
Packit 78deda
                 "argument");
Packit 78deda
    else {
Packit 78deda
        cmdlineP->bodySklFileName = strdup(argv[1]);
Packit 78deda
Packit 78deda
        if (argc-1 < 2)
Packit 78deda
            cmdlineP->inputFileName = strdup("-");  /* he wants stdin */
Packit 78deda
        else {
Packit 78deda
            cmdlineP->inputFileName = strdup(argv[2]);
Packit 78deda
            if (argc-1 > 2)
Packit 78deda
                pm_error("Too many arguments.  The only possible arguments "
Packit 78deda
                         "are the body skeleton file name and input image "
Packit 78deda
                         "file name");
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
typedef enum {
Packit 78deda
/* The types of object we handle */
Packit 78deda
    BDATA, IRED, IGREEN, IBLUE, ILUM, FRED, FGREEN, FBLUE, FLUM,
Packit 78deda
    WIDTH, HEIGHT, POSX, POSY
Packit 78deda
} SkeletonObjectType;
Packit 78deda
Packit 78deda
typedef enum {
Packit 78deda
    OBJTYP_ICOLOR, OBJTYP_FCOLOR, OBJTYP_INT, OBJTYP_BDATA
Packit 78deda
} SkeletonObjectClass;
Packit 78deda
Packit 78deda
/* Maximum size for a format string ("%d" etc.) */
Packit 78deda
/* Add one to this size for the terminating '\0'. */
Packit 78deda
#define MAXFORMAT 16
Packit 78deda
Packit 78deda
typedef union {
Packit 78deda
/* The data we keep for each object */
Packit 78deda
    struct Bndat {
Packit 78deda
        char * bdat;   /* Binary data (text with newlines etc.) */
Packit 78deda
        unsigned int ndat;
Packit 78deda
    } binData;
Packit 78deda
    
Packit 78deda
    struct Icdat {
Packit 78deda
        char icformat[MAXFORMAT+1];  /* Integer colors */
Packit 78deda
        unsigned int icolmin, icolmax;
Packit 78deda
    } icolData;
Packit 78deda
Packit 78deda
    struct Fcdat {
Packit 78deda
        char fcformat[MAXFORMAT+1];  /* Float colors */
Packit 78deda
        double fcolmin, fcolmax;
Packit 78deda
    } fcolData;
Packit 78deda
    
Packit 78deda
    struct Idat {
Packit 78deda
        char iformat[MAXFORMAT+1];   /* Integer data */
Packit 78deda
    } iData;
Packit 78deda
} SkeletonObjectData;
Packit 78deda
Packit 78deda
Packit 78deda
/* Each object has a type and some data */
Packit 78deda
typedef struct { 
Packit 78deda
    SkeletonObjectType objType;
Packit 78deda
    SkeletonObjectData odata;
Packit 78deda
} SkeletonObject;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
#define MAX_SKL_HEAD_OBJ 64
Packit 78deda
#define MAX_SKL_BODY_OBJ 256
Packit 78deda
#define MAX_SKL_TAIL_OBJ 64
Packit 78deda
#define MAX_LINE_BUF 1024
Packit 78deda
#define MAX_OBJ_BUF 80
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
dumpSkeleton(SkeletonObject ** const skeletonPList,
Packit 78deda
             unsigned int      const nSkeleton) {
Packit 78deda
Packit 78deda
    unsigned int i;
Packit 78deda
Packit 78deda
    pm_message("%u objects", nSkeleton);
Packit 78deda
Packit 78deda
    for (i = 0; i < nSkeleton; ++i) {
Packit 78deda
        SkeletonObject * const skeletonP = skeletonPList[i];
Packit 78deda
Packit 78deda
        pm_message("  Object: Type %u", skeletonP->objType);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
dumpAllSkeleton(SkeletonObject ** const bodySkeletonPList,
Packit 78deda
                unsigned int      const bodyNskl,
Packit 78deda
                SkeletonObject ** const headSkeletonPList, 
Packit 78deda
                unsigned int      const headNskl,
Packit 78deda
                SkeletonObject ** const tailSkeletonPList,
Packit 78deda
                unsigned int      const tailNskl) {
Packit 78deda
    
Packit 78deda
    pm_message("Body skeleton:");
Packit 78deda
    dumpSkeleton(bodySkeletonPList, bodyNskl);
Packit 78deda
Packit 78deda
    pm_message("Head skeleton:");
Packit 78deda
    dumpSkeleton(headSkeletonPList, headNskl);
Packit 78deda
Packit 78deda
    pm_message("Tail skeleton:");
Packit 78deda
    dumpSkeleton(tailSkeletonPList, tailNskl);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeBndat(FILE *           const ofP,
Packit 78deda
           SkeletonObject * const objectP) {
Packit 78deda
Packit 78deda
    struct Bndat * const bdataP = &objectP->odata.binData;
Packit 78deda
Packit 78deda
    fwrite(bdataP->bdat, bdataP->ndat, 1, ofP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeIcol(FILE *           const ofP,
Packit 78deda
          SkeletonObject * const objectP,
Packit 78deda
          double           const value) {
Packit 78deda
Packit 78deda
    /* Unlike Netpbm, the output format does not have an upper limit for
Packit 78deda
       maxval.  Here we allow all values representable by unsigned int.
Packit 78deda
    */
Packit 78deda
Packit 78deda
    struct Icdat * const icdataP = &objectP->odata.icolData;
Packit 78deda
    unsigned int const outValue =
Packit 78deda
        ROUNDU( icdataP->icolmin +
Packit 78deda
                ((double)icdataP->icolmax - icdataP->icolmin) * value);
Packit 78deda
Packit 78deda
    fprintf(ofP, icdataP->icformat, outValue);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeFcol(FILE *           const ofP,
Packit 78deda
          SkeletonObject * const objectP,
Packit 78deda
          double           const value) {
Packit 78deda
Packit 78deda
    struct Fcdat * const fcdataP = &objectP->odata.fcolData;
Packit 78deda
    
Packit 78deda
    fprintf(ofP, fcdataP->fcformat,
Packit 78deda
            (double)
Packit 78deda
            (fcdataP->fcolmin
Packit 78deda
             + (fcdataP->fcolmax - fcdataP->fcolmin) * value));
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeIdat(FILE *           const ofP,
Packit 78deda
          SkeletonObject * const objectP,
Packit 78deda
          unsigned int     const value) {
Packit 78deda
Packit 78deda
    struct Idat * const idataP = &objectP->odata.iData;
Packit 78deda
    
Packit 78deda
    fprintf(ofP, idataP->iformat, value);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeText(FILE *            const ofP,
Packit 78deda
          unsigned int      const nObj,
Packit 78deda
          SkeletonObject ** const obj,
Packit 78deda
          unsigned int      const width,
Packit 78deda
          unsigned int      const height,
Packit 78deda
          unsigned int      const x,
Packit 78deda
          unsigned int      const y,
Packit 78deda
          double            const red,
Packit 78deda
          double            const green,
Packit 78deda
          double            const blue) {
Packit 78deda
    
Packit 78deda
    unsigned int i;
Packit 78deda
Packit 78deda
    for (i = 0; i < nObj; ++i) {
Packit 78deda
        switch (obj[i]->objType) {
Packit 78deda
        case BDATA:
Packit 78deda
            writeBndat(ofP, obj[i]);
Packit 78deda
            break;
Packit 78deda
        case IRED:
Packit 78deda
            writeIcol(ofP, obj[i], red);
Packit 78deda
            break;
Packit 78deda
        case IGREEN:
Packit 78deda
            writeIcol(ofP, obj[i], green);
Packit 78deda
            break;
Packit 78deda
        case IBLUE:
Packit 78deda
            writeIcol(ofP, obj[i], blue);
Packit 78deda
            break;
Packit 78deda
        case ILUM:
Packit 78deda
            writeIcol(ofP, obj[i],
Packit 78deda
                      PPM_LUMINR*red + PPM_LUMING*green + PPM_LUMINB*blue);
Packit 78deda
            break;
Packit 78deda
        case FRED:
Packit 78deda
            writeFcol(ofP, obj[i], red);
Packit 78deda
            break;
Packit 78deda
        case FGREEN:
Packit 78deda
            writeFcol(ofP, obj[i], green);
Packit 78deda
            break;
Packit 78deda
        case FBLUE:
Packit 78deda
            writeFcol(ofP, obj[i], blue);
Packit 78deda
            break;
Packit 78deda
        case FLUM:
Packit 78deda
            writeFcol(ofP, obj[i],
Packit 78deda
                      PPM_LUMINR*red + PPM_LUMING*green + PPM_LUMINB*blue);
Packit 78deda
            break;
Packit 78deda
        case WIDTH:
Packit 78deda
            writeIdat(ofP, obj[i], width);
Packit 78deda
            break;
Packit 78deda
        case HEIGHT:
Packit 78deda
            writeIdat(ofP, obj[i], height);
Packit 78deda
            break;
Packit 78deda
        case POSX:
Packit 78deda
            writeIdat(ofP, obj[i], x);
Packit 78deda
            break;
Packit 78deda
        case POSY:
Packit 78deda
            writeIdat(ofP, obj[i], y);
Packit 78deda
            break;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObjectClass
Packit 78deda
objClass(SkeletonObjectType const objType) {
Packit 78deda
Packit 78deda
    switch (objType) {
Packit 78deda
    case IRED:
Packit 78deda
    case IGREEN:
Packit 78deda
    case IBLUE:
Packit 78deda
    case ILUM:
Packit 78deda
        return OBJTYP_ICOLOR;
Packit 78deda
Packit 78deda
    case FRED:
Packit 78deda
    case FGREEN:
Packit 78deda
    case FBLUE:
Packit 78deda
    case FLUM:
Packit 78deda
        return OBJTYP_FCOLOR;
Packit 78deda
Packit 78deda
    case WIDTH:
Packit 78deda
    case HEIGHT:
Packit 78deda
    case POSX:
Packit 78deda
    case POSY:
Packit 78deda
        return OBJTYP_INT;
Packit 78deda
    case BDATA:
Packit 78deda
        return OBJTYP_BDATA;
Packit 78deda
    }
Packit 78deda
    return 999; /* quiet compiler warning */
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Format string validation
Packit 78deda
Packit 78deda
  We validate format strings (such as "%f" "%03d") found in the skeleton files
Packit 78deda
  for convenience, even though behavior is documented as undefined when the
Packit 78deda
  user supplies a bogus format string.  Certain strings, most notably those
Packit 78deda
  with "%n", are especially risky; they pose a security threat.
Packit 78deda
Packit 78deda
  On systems with Glibc, we check with parse_printf_format().  On other
Packit 78deda
  systems we conduct a cursory scan of the characters in the format string,
Packit 78deda
  looking for characters that trigger non-numeric conversions, etc.
Packit 78deda
Packit 78deda
  Documentation for parse_printf_format() is usually available in texinfo
Packit 78deda
  format on GNU/Linux systems.  As of Dec. 2014 there is no official man page.
Packit 78deda
  
Packit 78deda
  Online documentation is available from:
Packit 78deda
  https://
Packit 78deda
  www.gnu.org/software/libc/manual/html_node/Parsing-a-Template-String.html
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
Packit 78deda
#if HAVE_PARSE_PRINTF_FORMAT
Packit 78deda
static void
Packit 78deda
validateParsePrintfFlag(int                const printfConversion,
Packit 78deda
                        SkeletonObjectType const ctyp,
Packit 78deda
                        const char **      const errorP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Assuming 'printfConversion' is the value reported by parse_printf_format()
Packit 78deda
  as the type of argument a format string requires, 
Packit 78deda
  return an explanation of how it is incompatible with 'ctyp' as
Packit 78deda
  *errorP -- return null string if it is compatible.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    /* We first check for "%n", then the type modifiers, and finally the
Packit 78deda
       actual conversion type (char, int, float, double, string or pointer.)
Packit 78deda
    */
Packit 78deda
    switch (printfConversion & PA_FLAG_MASK) {
Packit 78deda
    case PA_FLAG_PTR:  /* This means %n */
Packit 78deda
        pm_asprintf(errorP, "Contains a %%n conversion specifier");
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case PA_FLAG_SHORT:
Packit 78deda
    case PA_FLAG_LONG:
Packit 78deda
    case PA_FLAG_LONG_LONG:
Packit 78deda
        /* We omit PA_FLAG_LONG_DOUBLE because it is a synonym for
Packit 78deda
           PA_FLAG_LONG_LONG: listing both causes compilation errors.
Packit 78deda
        */
Packit 78deda
        pm_asprintf(errorP, "Invalid type modifier");
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    default:
Packit 78deda
        switch (printfConversion & ~PA_FLAG_MASK) {
Packit 78deda
        case PA_CHAR:
Packit 78deda
            pm_message("Warning: char type conversion."); 
Packit 78deda
        case PA_INT:
Packit 78deda
            if(objClass(ctyp) == OBJTYP_ICOLOR ||
Packit 78deda
               objClass(ctyp) == OBJTYP_INT )
Packit 78deda
                *errorP = NULL;
Packit 78deda
            else
Packit 78deda
                pm_asprintf(errorP, "Conversion specifier requires a "
Packit 78deda
                            "character or integer argument, but it is in "
Packit 78deda
                            "a replacement sequence for a different type");
Packit 78deda
            break;
Packit 78deda
        case PA_DOUBLE:
Packit 78deda
            if(objClass(ctyp) == OBJTYP_FCOLOR)
Packit 78deda
                *errorP = NULL;
Packit 78deda
            else
Packit 78deda
                pm_asprintf(errorP, "Conversion specifier requires a "
Packit 78deda
                            "double precision floating point argument, "
Packit 78deda
                            "but it is in "
Packit 78deda
                            "a replacement sequence for a different type");
Packit 78deda
            break;
Packit 78deda
        case PA_FLOAT:
Packit 78deda
        case PA_STRING:    /* %s */
Packit 78deda
        case PA_POINTER:   /* %p */
Packit 78deda
        default:
Packit 78deda
            pm_asprintf(errorP, "Conversion specifier requires an argument of "
Packit 78deda
                        "a type that this program never provides for "
Packit 78deda
                        "any replacement sequence");
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
#endif
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
#if HAVE_PARSE_PRINTF_FORMAT
Packit 78deda
static void
Packit 78deda
validateFormatWithPpf(const char *       const format,
Packit 78deda
                      SkeletonObjectType const ctyp,
Packit 78deda
                      const char **      const errorP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Validate format string 'format' for use with a skeleton of type 'ctyp',
Packit 78deda
  using the system parse_printf_format() function.
Packit 78deda
Packit 78deda
  Return as *errorP an explanation of how it is invalid, or a null string
Packit 78deda
  if it is valid.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    /* We request parse_printf_format() to report the details of the first
Packit 78deda
       8 conversions.  8 because the maximum length of format is 16 means it
Packit 78deda
       can have up to 8 conversions: "%d%d%d%d%d%d%d%d".
Packit 78deda
Packit 78deda
       Actually this is more than necessary: we are concerned with only the
Packit 78deda
       first conversion and whether there it is the only one.
Packit 78deda
    */
Packit 78deda
Packit 78deda
    int printfConversion[MAXFORMAT/2] = {0, 0, 0, 0, 0, 0, 0, 0};
Packit 78deda
Packit 78deda
    size_t const n =
Packit 78deda
        parse_printf_format(format, MAXFORMAT/2, printfConversion);
Packit 78deda
Packit 78deda
    switch (n) {
Packit 78deda
    case 0:
Packit 78deda
        pm_asprintf(errorP, "No transformation found");
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 1:
Packit 78deda
        validateParsePrintfFlag(printfConversion[0], ctyp, errorP);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    default:
Packit 78deda
        pm_asprintf(errorP, "Has %lu extra transformation%s ",
Packit 78deda
                    (unsigned long)n-1, n-1 > 1 ? "s" : "");
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
#endif
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
validateFormatOne(char               const typeSpecifier,
Packit 78deda
                  bool               const isLastInString,
Packit 78deda
                  SkeletonObjectType const ctyp,
Packit 78deda
                  bool *             const validatedP,
Packit 78deda
                  const char **      const errorP) {
Packit 78deda
Packit 78deda
    switch (typeSpecifier) {
Packit 78deda
        /* Valid character encountered.  Skip. */
Packit 78deda
        /* ' ' (space) is listed here, but should never occur for
Packit 78deda
           we use sscanf() to parse the fields.
Packit 78deda
        */
Packit 78deda
    case ' ': case '-': case '+': case '\'': case '#': case '.':
Packit 78deda
    case '0': case '1': case '2': case '3': case '4': case '5':
Packit 78deda
    case '6': case '7': case '8': case '9':
Packit 78deda
        break;
Packit 78deda
        
Packit 78deda
    case 'c': case 'C':
Packit 78deda
        pm_message("Warning: char type conversion: %%%c.", typeSpecifier);
Packit 78deda
    case 'i': case 'd': case 'u': case 'o': case 'x': case 'X':
Packit 78deda
        if (!isLastInString)
Packit 78deda
            pm_asprintf(errorP, "Extra characters at end");
Packit 78deda
        else if(objClass(ctyp) != OBJTYP_ICOLOR &&
Packit 78deda
                objClass(ctyp) != OBJTYP_INT )
Packit 78deda
            pm_asprintf(errorP, "Conversion type mismatch");
Packit 78deda
        else
Packit 78deda
            *validatedP = true;
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case 'f': case 'F': case 'g': case 'G': case 'a': case 'A':
Packit 78deda
        if (!isLastInString)
Packit 78deda
            pm_asprintf(errorP, "Extra characters at end");
Packit 78deda
        else if(objClass(ctyp) != OBJTYP_FCOLOR)
Packit 78deda
            pm_asprintf(errorP, "Conversion type mismatch");
Packit 78deda
        else
Packit 78deda
            *validatedP = true;
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case '\0':
Packit 78deda
        pm_asprintf(errorP, "No conversion specified");
Packit 78deda
        break;
Packit 78deda
    case '%':
Packit 78deda
        pm_asprintf(errorP, "No more than one %% is allowed");
Packit 78deda
        break;
Packit 78deda
    case '$':
Packit 78deda
    case '*':
Packit 78deda
        pm_asprintf(errorP, "%c is not allowed", typeSpecifier);
Packit 78deda
        break;
Packit 78deda
    case 'h': case 'l': case 'L': case 'q': case 'j': case 'Z': case 't':
Packit 78deda
        pm_asprintf(errorP, "Modifier %c is not allowed in format",
Packit 78deda
                    typeSpecifier);
Packit 78deda
        break;
Packit 78deda
    case 's': case 'S': case 'm': case 'p': case 'n':
Packit 78deda
        pm_asprintf(errorP, "Invalid conversion type");
Packit 78deda
        break;
Packit 78deda
    default:
Packit 78deda
        pm_asprintf(errorP, "Abnormal character");
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
validateFormatGen(const char *       const format,
Packit 78deda
                  SkeletonObjectType const ctyp,
Packit 78deda
                  const char **      const errorP)  {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Validate format string 'format' for use with a skeleton of type 'ctyp',
Packit 78deda
  without using the system parse_printf_format() function.
Packit 78deda
Packit 78deda
  The string must begin with "%" and end with the translation type character
Packit 78deda
  ("%d", "%x", "%f", etc.)
Packit 78deda
Packit 78deda
  We check only for invalid characters.  Invalid constructs, such as
Packit 78deda
  "%00.00.00d" will pass this test.
Packit 78deda
Packit 78deda
  Return as *errorP an explanation of how it is invalid, or a null string
Packit 78deda
  if it is valid.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if (format[0] != '%')
Packit 78deda
        pm_asprintf(errorP, "Does not start with %%");
Packit 78deda
    else {
Packit 78deda
        unsigned int i;
Packit 78deda
        bool validated;
Packit 78deda
Packit 78deda
        for (i = 1, validated = false, *errorP = NULL;
Packit 78deda
             !validated && !*errorP;
Packit 78deda
             ++i) {
Packit 78deda
Packit 78deda
            validateFormatOne(format[i], format[i+1] == '\0', ctyp,
Packit 78deda
                              &validated, errorP);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
validateFormat(const char *       const format,
Packit 78deda
               SkeletonObjectType const ctyp) {
Packit 78deda
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    if (strlen(format) > MAXFORMAT)
Packit 78deda
        pm_asprintf(&error, "Too long");
Packit 78deda
    else {
Packit 78deda
#if HAVE_PARSE_PRINTF_FORMAT
Packit 78deda
        if (true)
Packit 78deda
            validateFormatWithPpf(format, ctyp, &error);
Packit 78deda
        else  /* Silence compiler warning about unused function */
Packit 78deda
            validateFormatGen(format, ctyp, &error);
Packit 78deda
#else
Packit 78deda
        validateFormatGen(format, ctyp, &error);
Packit 78deda
#endif
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (error)
Packit 78deda
        pm_error("Invalid format string '%s'.  %s", format, error);
Packit 78deda
}              
Packit 78deda
               
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newBinDataObj(unsigned int const nDat, 
Packit 78deda
              const char * const bdat) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Create a binary data object.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonObject * objectP;
Packit 78deda
Packit 78deda
    objectP = malloc(sizeof(*objectP) + nDat);
Packit 78deda
Packit 78deda
    if (!objectP)
Packit 78deda
        pm_error("Failed to allocate memory for binary data object "
Packit 78deda
                 "with %u bytes", nDat);
Packit 78deda
Packit 78deda
    objectP->objType = BDATA;
Packit 78deda
    objectP->odata.binData.ndat = nDat;
Packit 78deda
    objectP->odata.binData.bdat = ((char *)objectP) + sizeof(SkeletonObject);
Packit 78deda
    memcpy(objectP->odata.binData.bdat, bdat, nDat);
Packit 78deda
Packit 78deda
    return objectP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newIcolDataObj(SkeletonObjectType const ctyp,
Packit 78deda
               const char *       const format,
Packit 78deda
               unsigned int       const icolmin,
Packit 78deda
               unsigned int       const icolmax) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Create integer color data object.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonObject * objectP;
Packit 78deda
Packit 78deda
    MALLOCVAR(objectP);
Packit 78deda
Packit 78deda
    if (!objectP)
Packit 78deda
        pm_error("Failed to allocate memory for an integer color data "
Packit 78deda
                 "object");
Packit 78deda
Packit 78deda
    objectP->objType = ctyp;
Packit 78deda
    validateFormat(format, ctyp);
Packit 78deda
    strcpy(objectP->odata.icolData.icformat, format);
Packit 78deda
    objectP->odata.icolData.icolmin = icolmin;
Packit 78deda
    objectP->odata.icolData.icolmax = icolmax;
Packit 78deda
Packit 78deda
    return objectP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newFcolDataObj(SkeletonObjectType  const ctyp,
Packit 78deda
               const char *        const format,
Packit 78deda
               double              const fcolmin,
Packit 78deda
               double              const fcolmax) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Create float color data object.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonObject * objectP;
Packit 78deda
Packit 78deda
    MALLOCVAR(objectP);
Packit 78deda
Packit 78deda
    if (!objectP)
Packit 78deda
        pm_error("Failed to allocate memory for a float color data object");
Packit 78deda
Packit 78deda
    objectP->objType = ctyp;
Packit 78deda
    validateFormat(format, ctyp);
Packit 78deda
    strcpy(objectP->odata.fcolData.fcformat, format);
Packit 78deda
    objectP->odata.fcolData.fcolmin = fcolmin;
Packit 78deda
    objectP->odata.fcolData.fcolmax = fcolmax;
Packit 78deda
Packit 78deda
    return objectP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newIdataObj(SkeletonObjectType const ctyp,
Packit 78deda
            const char *       const format) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Create universal data object.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonObject * objectP;
Packit 78deda
Packit 78deda
    MALLOCVAR(objectP);
Packit 78deda
Packit 78deda
    if (!objectP)
Packit 78deda
        pm_error("Failed to allocate memory for a universal data object");
Packit 78deda
Packit 78deda
    objectP->objType = ctyp;
Packit 78deda
    validateFormat(format, ctyp);
Packit 78deda
    strcpy(objectP->odata.iData.iformat, format);
Packit 78deda
Packit 78deda
    return objectP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static char const escape = '#';
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObjectType
Packit 78deda
interpretObjType(const char * const typstr) {
Packit 78deda
Packit 78deda
    SkeletonObjectType objType;
Packit 78deda
Packit 78deda
    /* handle integer colors */
Packit 78deda
    if      (streq(typstr, "ired")  ) objType = IRED;
Packit 78deda
    else if (streq(typstr, "igreen")) objType = IGREEN;
Packit 78deda
    else if (streq(typstr, "iblue") ) objType = IBLUE;
Packit 78deda
    else if (streq(typstr, "ilum")  ) objType = ILUM;
Packit 78deda
    /* handle real colors */
Packit 78deda
    else if (streq(typstr, "fred")  ) objType = FRED;
Packit 78deda
    else if (streq(typstr, "fgreen")) objType = FGREEN;
Packit 78deda
    else if (streq(typstr, "fblue") ) objType = FBLUE;
Packit 78deda
    else if (streq(typstr, "flum")  ) objType = FLUM;
Packit 78deda
    /* handle integer data */
Packit 78deda
    else if (streq(typstr, "width") ) objType = WIDTH;
Packit 78deda
    else if (streq(typstr, "height")) objType = HEIGHT;
Packit 78deda
    else if (streq(typstr, "posx")  ) objType = POSX;
Packit 78deda
    else if (streq(typstr, "posy")  ) objType = POSY;
Packit 78deda
    else                              objType = BDATA;
Packit 78deda
Packit 78deda
    return objType;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newIcSkelFromReplString(const char *       const icolorObjstr,
Packit 78deda
                        SkeletonObjectType const objType) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  A new skeleton for an integer color substitution specifier (class
Packit 78deda
  OBJTYP_ICOLOR) whose replacement string (the stuff between the parentheses
Packit 78deda
  in #(...)) says substitution type 'objType' and the rest of the
Packit 78deda
  replacement string is 'icolorObjstr'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonObject * retval;
Packit 78deda
    unsigned int icolmin, icolmax;
Packit 78deda
    char formstr[MAX_OBJ_BUF];
Packit 78deda
    int nOdata;
Packit 78deda
Packit 78deda
    nOdata = sscanf(icolorObjstr, "%s%u%u", formstr, &icolmin, &icolmax);
Packit 78deda
Packit 78deda
    if (nOdata == 3)
Packit 78deda
        retval = newIcolDataObj(objType, formstr, icolmin, icolmax);
Packit 78deda
    else if (nOdata == EOF) {
Packit 78deda
        /* No arguments specified.  Use defaults */
Packit 78deda
        retval = newIcolDataObj(objType, "%u", 0, 255);
Packit 78deda
    } else
Packit 78deda
        retval = NULL;
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newFcSkelFromReplString(const char *       const fcolorObjstr,
Packit 78deda
                        SkeletonObjectType const objType) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  A new skeleton for a floating point color substitution specifier (class
Packit 78deda
  OBJTYP_FCOLOR) whose replacement string (the stuff between the parentheses
Packit 78deda
  in #(...)) says substitution type 'objType' and the rest of the
Packit 78deda
  replacement string is 'fcolorObjstr'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonObject * retval;
Packit 78deda
    double fcolmin, fcolmax;
Packit 78deda
    char formstr[MAX_OBJ_BUF];
Packit 78deda
    int nOdata;
Packit 78deda
Packit 78deda
    nOdata = sscanf(fcolorObjstr, "%s%lf%lf", formstr, &fcolmin, &fcolmax);
Packit 78deda
Packit 78deda
    if (nOdata == 3)
Packit 78deda
        retval = newFcolDataObj(objType, formstr, fcolmin, fcolmax);
Packit 78deda
    else if (nOdata == EOF) {
Packit 78deda
        /* No arguments specified.  Use defaults */
Packit 78deda
        retval = newFcolDataObj(objType, "%f", 0.0, 1.0);
Packit 78deda
    } else
Packit 78deda
        retval = NULL;
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
} 
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newISkelFromReplString(const char *       const intObjstr,
Packit 78deda
                       SkeletonObjectType const objType) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  A new skeleton for an integer substitution specifier (class OBJTYP_INT)
Packit 78deda
  whose replacement string (the stuff between the parentheses in #(...))
Packit 78deda
  says substitution type 'objType' and the rest of the replacement string is
Packit 78deda
  'intObjstr'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonObject * retval;
Packit 78deda
    char formstr[MAX_OBJ_BUF];
Packit 78deda
    int nOdata;
Packit 78deda
Packit 78deda
    nOdata = sscanf(intObjstr, "%s", formstr);
Packit 78deda
    
Packit 78deda
    if (nOdata == 1)
Packit 78deda
        retval = newIdataObj(objType, formstr);
Packit 78deda
    else if (nOdata == EOF) {
Packit 78deda
        /* No arguments specified.  Use defaults */
Packit 78deda
        retval = newIdataObj(objType, "%u");
Packit 78deda
    } else
Packit 78deda
        retval = NULL;
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
} 
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static SkeletonObject *
Packit 78deda
newSkeletonFromReplString(const char * const objstr) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  A new skeleton created from the replacement string 'objstr' (the stuff
Packit 78deda
  between the parentheses in #(...) ).
Packit 78deda
Packit 78deda
  Return NULL if it isn't a valid replacement string.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    /* We use sscanf() to parse the contents of objstr, giving it a format
Packit 78deda
       template with the largest number of fields possible plus one extra to
Packit 78deda
       pick up and check for the existence of invalid trailing characters.  We
Packit 78deda
       read and discard fields beyond the first, if any.  The appropriate
Packit 78deda
       new**SkelFromReplString() function determines their contents with a
Packit 78deda
       separate call to sscanf().
Packit 78deda
    */
Packit 78deda
Packit 78deda
    SkeletonObject * retval;
Packit 78deda
    char typstr[MAX_OBJ_BUF];
Packit 78deda
    int typlen;
Packit 78deda
    SkeletonObjectType objType;
Packit 78deda
    int conversionCt;
Packit 78deda
    char s1[MAX_OBJ_BUF];    /* Dry read. */
Packit 78deda
    char s2[MAX_OBJ_BUF];    /* Extra tailing characters. */
Packit 78deda
    float f1, f2;            /* Dry read. */ 
Packit 78deda
Packit 78deda
    typstr[0] = '\0';  /* initial value */
Packit 78deda
Packit 78deda
    conversionCt = sscanf(objstr, "%s%n%s%f%f%s",
Packit 78deda
                          typstr, &typlen, s1, &f1, &f2, s2);
Packit 78deda
    switch (conversionCt) {
Packit 78deda
    case 1: case 2: case 4:
Packit 78deda
        objType = interpretObjType(typstr);
Packit 78deda
      break;
Packit 78deda
    default:
Packit 78deda
        objType = BDATA;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    switch (objClass(objType)) {
Packit 78deda
    case OBJTYP_ICOLOR:
Packit 78deda
        retval = newIcSkelFromReplString(&objstr[typlen], objType);
Packit 78deda
        break;
Packit 78deda
    case OBJTYP_FCOLOR:
Packit 78deda
        retval = newFcSkelFromReplString(&objstr[typlen], objType);
Packit 78deda
        break;
Packit 78deda
    case OBJTYP_INT:
Packit 78deda
        retval = newISkelFromReplString(&objstr[typlen], objType);
Packit 78deda
        break;
Packit 78deda
    case OBJTYP_BDATA:
Packit 78deda
        retval = NULL;
Packit 78deda
    }
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readThroughCloseParen(FILE * const ifP,
Packit 78deda
                      char * const objstr,
Packit 78deda
                      size_t const objstrSize,
Packit 78deda
                      bool * const unclosedP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read *ifP up through close parenthesis ( ')' ) into 'objstr', which
Packit 78deda
   is of size 'objstrSize'.  Make it a NUL-terminated string.
Packit 78deda
Packit 78deda
   Return *unclosedP true iff we run out of file or run out of objstr
Packit 78deda
   before we see a close parenthesis.  In this case, return the rest of
Packit 78deda
   the file, or as much as fits, in 'objstr', not NUL-terminated.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int i;
Packit 78deda
    bool eof;
Packit 78deda
    bool gotEscSeq;
Packit 78deda
Packit 78deda
    for (i= 0, eof = false, gotEscSeq = false;
Packit 78deda
         i < objstrSize - 1 && !gotEscSeq && !eof;
Packit 78deda
         ++i) {
Packit 78deda
Packit 78deda
        int rc;
Packit 78deda
Packit 78deda
        rc = getc(ifP);
Packit 78deda
        if (rc == EOF)
Packit 78deda
            eof = true;
Packit 78deda
        else {
Packit 78deda
            char const chr = rc;
Packit 78deda
            if (chr == ')') {
Packit 78deda
                gotEscSeq = true;
Packit 78deda
                objstr[i] = '\0';
Packit 78deda
	        } else
Packit 78deda
                objstr[i] = chr;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    *unclosedP = !gotEscSeq;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
typedef struct {
Packit 78deda
    unsigned int      capacity;
Packit 78deda
    SkeletonObject ** skeletonPList;
Packit 78deda
    unsigned int      nSkeleton;
Packit 78deda
} SkeletonBuffer;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
SkeletonBuffer_init(SkeletonBuffer *  const bufferP,
Packit 78deda
                    unsigned int      const capacity,
Packit 78deda
                    SkeletonObject ** const skeletonPList) {
Packit 78deda
Packit 78deda
    bufferP->capacity      = capacity;
Packit 78deda
    bufferP->skeletonPList = skeletonPList;
Packit 78deda
    bufferP->nSkeleton     = 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
SkeletonBuffer_add(SkeletonBuffer * const bufferP,
Packit 78deda
                   SkeletonObject * const skeletonP) {
Packit 78deda
Packit 78deda
    if (bufferP->nSkeleton >= bufferP->capacity)
Packit 78deda
        pm_error("Too many skeletons.  Max = %u", bufferP->capacity);
Packit 78deda
Packit 78deda
    bufferP->skeletonPList[bufferP->nSkeleton++] = skeletonP;
Packit 78deda
}                   
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
typedef struct {
Packit 78deda
Packit 78deda
    char data[MAX_LINE_BUF + MAX_OBJ_BUF + 16];
Packit 78deda
Packit 78deda
    unsigned int length;
Packit 78deda
Packit 78deda
    SkeletonBuffer * skeletonBufferP;
Packit 78deda
        /* The buffer to which we flush.  Flushing means turning all the
Packit 78deda
           characters currently in our buffer into a binary skeleton object
Packit 78deda
           here.
Packit 78deda
        */
Packit 78deda
Packit 78deda
} Buffer;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
Buffer_init(Buffer *         const bufferP,
Packit 78deda
            SkeletonBuffer * const skeletonBufferP) {
Packit 78deda
Packit 78deda
    bufferP->skeletonBufferP = skeletonBufferP;
Packit 78deda
    bufferP->length = 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
Buffer_flush(Buffer * const bufferP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Flush the buffer out to a binary skeleton object.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    SkeletonBuffer_add(bufferP->skeletonBufferP,
Packit 78deda
                       newBinDataObj(bufferP->length, bufferP->data));
Packit 78deda
Packit 78deda
    bufferP->length = 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
Buffer_add(Buffer * const bufferP,
Packit 78deda
           char     const newChar) {
Packit 78deda
Packit 78deda
    if (bufferP->length >= MAX_LINE_BUF)
Packit 78deda
        Buffer_flush(bufferP);
Packit 78deda
Packit 78deda
    assert(bufferP->length < MAX_LINE_BUF);
Packit 78deda
Packit 78deda
    bufferP->data[bufferP->length++] = newChar;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
Buffer_dropFinalNewline(Buffer * const bufferP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   If the last thing in the buffer is a newline, remove it.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if (bufferP->length >= 1 && bufferP->data[bufferP->length-1] == '\n') {
Packit 78deda
            /* Drop finishing newline character */
Packit 78deda
            --bufferP->length;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
addImpostorReplacementSeq(Buffer *     const bufferP,
Packit 78deda
                          const char * const seqContents) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Add to buffer *bufferP something that looks like a replacement sequence but
Packit 78deda
  doesn't have the proper contents (the stuff between the parentheses) to be
Packit 78deda
  one.  For example,
Packit 78deda
Packit 78deda
  "#(fread x)"
Packit 78deda
Packit 78deda
  seqContents[] is the contents, NUL-terminated.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    const char * p;
Packit 78deda
Packit 78deda
    Buffer_add(bufferP, escape);
Packit 78deda
    Buffer_add(bufferP, '(');
Packit 78deda
Packit 78deda
    for (p = &seqContents[0]; *p; ++p)
Packit 78deda
        Buffer_add(bufferP, *p);
Packit 78deda
Packit 78deda
    Buffer_add(bufferP, ')');
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readSkeletonFile(const char *      const filename,
Packit 78deda
                 unsigned int      const maxskl,
Packit 78deda
                 const char **     const errorP,
Packit 78deda
                 unsigned int *    const nSkeletonP,
Packit 78deda
                 SkeletonObject ** const skeletonPList) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    FILE * sklfileP;
Packit 78deda
    SkeletonBuffer skeletonBuffer;
Packit 78deda
        /* A buffer for accumulating skeleton objects */
Packit 78deda
    Buffer buffer;
Packit 78deda
        /* A buffer for accumulating binary (literal; unsubstituted) data, on
Packit 78deda
           its way to becoming a binary skeleton object. 
Packit 78deda
        */
Packit 78deda
    bool eof;
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    SkeletonBuffer_init(&skeletonBuffer, maxskl, skeletonPList);
Packit 78deda
Packit 78deda
    Buffer_init(&buffer, &skeletonBuffer);
Packit 78deda
Packit 78deda
    sklfileP = pm_openr(filename);
Packit 78deda
Packit 78deda
    for (eof = false, error = NULL; !eof && !error; ) {
Packit 78deda
Packit 78deda
        int rc;
Packit 78deda
Packit 78deda
        rc = getc(sklfileP);
Packit 78deda
Packit 78deda
        if (rc == EOF)
Packit 78deda
            eof = true;
Packit 78deda
        else {
Packit 78deda
            char const chr = rc;
Packit 78deda
Packit 78deda
            if (chr != escape) {
Packit 78deda
                /* Not a replacement sequence; just a literal character */
Packit 78deda
                Buffer_add(&buffer, chr);
Packit 78deda
            } else {
Packit 78deda
                int rc;
Packit 78deda
                rc = getc(sklfileP);
Packit 78deda
                if (rc == EOF) {
Packit 78deda
                    /* Not a replacement sequence, just an escape caharacter
Packit 78deda
                       at the end of the file.
Packit 78deda
                    */
Packit 78deda
                    Buffer_add(&buffer, escape);
Packit 78deda
                    eof = true;
Packit 78deda
                } else {
Packit 78deda
                    char const chr = rc;
Packit 78deda
Packit 78deda
                    if (chr != '(') {
Packit 78deda
                        /* Not a replacement sequence, just a lone escape
Packit 78deda
                           character
Packit 78deda
                        */
Packit 78deda
                        Buffer_add(&buffer, escape);
Packit 78deda
                        Buffer_add(&buffer, chr);
Packit 78deda
                    } else {
Packit 78deda
                        char objstr[MAX_OBJ_BUF];
Packit 78deda
                        bool unclosed;
Packit 78deda
                        readThroughCloseParen(sklfileP,
Packit 78deda
                                              objstr, sizeof(objstr),
Packit 78deda
                                              &unclosed);
Packit 78deda
                        if (unclosed)
Packit 78deda
                            pm_asprintf(&error, "Unclosed parentheses "
Packit 78deda
                                        "in #() escape sequence");
Packit 78deda
                        else {
Packit 78deda
                            SkeletonObject * const skeletonP =
Packit 78deda
                                newSkeletonFromReplString(objstr);
Packit 78deda
Packit 78deda
                            if (skeletonP) {
Packit 78deda
                                Buffer_flush(&buffer);
Packit 78deda
                                SkeletonBuffer_add(&skeletonBuffer, skeletonP);
Packit 78deda
                            } else
Packit 78deda
                                addImpostorReplacementSeq(&buffer, objstr);
Packit 78deda
                        }
Packit 78deda
                    }
Packit 78deda
                }
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (!error) {
Packit 78deda
        Buffer_dropFinalNewline(&buffer);
Packit 78deda
        Buffer_flush(&buffer);
Packit 78deda
    }
Packit 78deda
    *errorP = error;
Packit 78deda
    *nSkeletonP = skeletonBuffer.nSkeleton;
Packit 78deda
Packit 78deda
    fclose(sklfileP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
convertIt(FILE *            const ifP,
Packit 78deda
          FILE *            const ofP,
Packit 78deda
          SkeletonObject ** const bodySkeletonPList,
Packit 78deda
          unsigned int      const bodyNskl,
Packit 78deda
          SkeletonObject ** const headSkeletonPList, 
Packit 78deda
          unsigned int      const headNskl,
Packit 78deda
          SkeletonObject ** const tailSkeletonPList,
Packit 78deda
          unsigned int      const tailNskl) {
Packit 78deda
Packit 78deda
    pixel * pixelrow;
Packit 78deda
    pixval maxval;
Packit 78deda
    double dmaxval;
Packit 78deda
    int rows, cols;
Packit 78deda
    int format;
Packit 78deda
    unsigned int row;
Packit 78deda
Packit 78deda
    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
Packit 78deda
Packit 78deda
    pixelrow = ppm_allocrow(cols);
Packit 78deda
Packit 78deda
    dmaxval = (double)maxval;
Packit 78deda
Packit 78deda
    /* Write header */
Packit 78deda
    writeText(ofP, headNskl, headSkeletonPList, 
Packit 78deda
              cols, rows , 0, 0, 0.0, 0.0, 0.0);
Packit 78deda
Packit 78deda
    /* Write raster */
Packit 78deda
    for (row = 0; row < rows; ++row) {
Packit 78deda
        unsigned int col;
Packit 78deda
Packit 78deda
        ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
Packit 78deda
Packit 78deda
        for (col = 0; col < cols; ++col) {
Packit 78deda
            pixel const thisPixel = pixelrow[col];
Packit 78deda
Packit 78deda
            writeText(ofP, bodyNskl, bodySkeletonPList,
Packit 78deda
                      cols, rows, col, row,
Packit 78deda
                      PPM_GETR(thisPixel)/dmaxval,
Packit 78deda
                      PPM_GETG(thisPixel)/dmaxval,
Packit 78deda
                      PPM_GETB(thisPixel)/dmaxval);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Write trailer */
Packit 78deda
    writeText(ofP, tailNskl, tailSkeletonPList, 
Packit 78deda
              cols, rows, 0, 0, 0.0, 0.0, 0.0);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int           argc,
Packit 78deda
     const char ** argv) {
Packit 78deda
    
Packit 78deda
    struct CmdlineInfo cmdline;
Packit 78deda
Packit 78deda
    unsigned int headNskl, bodyNskl, tailNskl;
Packit 78deda
    SkeletonObject * headSkeletonPList[MAX_SKL_HEAD_OBJ];
Packit 78deda
    SkeletonObject * bodySkeletonPList[MAX_SKL_BODY_OBJ];
Packit 78deda
    SkeletonObject * tailSkeletonPList[MAX_SKL_TAIL_OBJ];
Packit 78deda
    FILE * ifP;
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifP = pm_openr(cmdline.inputFileName);
Packit 78deda
Packit 78deda
    readSkeletonFile(cmdline.bodySklFileName, ARRAY_SIZE(bodySkeletonPList),
Packit 78deda
                     &error, &bodyNskl, bodySkeletonPList);
Packit 78deda
    if (error)
Packit 78deda
        pm_error("Invalid body skeleton file '%s'.  %s",
Packit 78deda
                 cmdline.bodySklFileName, error);
Packit 78deda
Packit 78deda
    if (cmdline.hd) {
Packit 78deda
        readSkeletonFile(cmdline.hd, ARRAY_SIZE(headSkeletonPList),
Packit 78deda
                         &error, &headNskl, headSkeletonPList);
Packit 78deda
        if (error)
Packit 78deda
            pm_error("Invalid head skeleton file '%s'.  %s",
Packit 78deda
                     cmdline.hd, error);
Packit 78deda
    } else
Packit 78deda
        headNskl = 0;
Packit 78deda
Packit 78deda
    if (cmdline.tl) {
Packit 78deda
        readSkeletonFile(cmdline.tl, ARRAY_SIZE(tailSkeletonPList),
Packit 78deda
                         &error, &tailNskl, tailSkeletonPList);
Packit 78deda
        if (error)
Packit 78deda
            pm_error("Invalid tail skeleton file '%s'.  %s",
Packit 78deda
                     cmdline.tl, error);
Packit 78deda
    } else
Packit 78deda
        tailNskl = 0;
Packit 78deda
Packit 78deda
    if (cmdline.debug)
Packit 78deda
        dumpAllSkeleton(bodySkeletonPList, bodyNskl,
Packit 78deda
                        headSkeletonPList, headNskl,
Packit 78deda
                        tailSkeletonPList, tailNskl);
Packit 78deda
Packit 78deda
    convertIt(ifP, stdout,
Packit 78deda
              bodySkeletonPList, bodyNskl,
Packit 78deda
              headSkeletonPList, headNskl,
Packit 78deda
              tailSkeletonPList, tailNskl);
Packit 78deda
Packit 78deda
    pm_close(ifP);
Packit 78deda
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda
Packit 78deda