"Fossies" - the Fresh Open Source Software Archive

Member "foomatic-db-engine-4.0-20221101/foomatic-combo-xml.c" (1 Nov 2022, 94261 Bytes) of package /linux/misc/foomatic-db-engine-4.0-20221101.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "foomatic-combo-xml.c" see the Fossies "Dox" file reference documentation.

A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.


    1 /*
    2  *   Foomatic Combo XML
    3  *   ------------------
    4  *
    5  *   Compute a Foomatic printer/driver combo by a pure C program doing
    6  *   a simplified sequential-reading XML-parsing. Perl libraries which
    7  *   make Perl data structures out of the XML files are very slow and
    8  *   memory-consuming.
    9  *
   10  *   Copyright 2001-2011 by Till Kamppeter, Christopher Yeleighton
   11  *
   12  *   This program is free software; you can redistribute it and/or
   13  *   modify it under the terms of the GNU General Public License as
   14  *   published by the Free Software Foundation; either version 2 of the
   15  *   License, or (at your option) any later version.
   16  *
   17  *   This program is distributed in the hope that it will be useful,
   18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   20  *   GNU General Public License for more details.
   21  *
   22  *   You should have received a copy of the GNU General Public License
   23  *   along with this program; if not, write to the Free Software
   24  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   25  *   02111-1307  USA
   26  *
   27  */
   28 
   29 /*
   30  * Include necessary headers (standard libraries)
   31  */
   32 
   33 #include <stdlib.h>
   34 #include <stdio.h>
   35 #include <string.h>
   36 #include <sys/types.h>
   37 #include <dirent.h>
   38 #include <assert.h>
   39 
   40 /*
   41  * Data structures for the printer/driver combo by printer list for the
   42  * overview ("-O" option)
   43  */
   44 
   45 typedef struct { /* structure for a driver entry (linear list) */
   46   char                  name[128]; /* Name of driver */
   47   char                  *functionality; /* Exceptions in driver
   48                                       functionality profile for this
   49                       printer */
   50   struct driverlist_t   *next;     /* pointer to next driver */
   51 } driverlist_t;
   52 
   53 typedef struct { /* structure for a printer entry (linear list) */
   54   char                  id[128];   /* ID of printer */
   55   driverlist_t          *drivers;  /* pointer to the list of the drivers
   56                       with which this printer works */
   57   struct printerlist_t  *next;     /* pointer to next printer */
   58 } printerlist_t;
   59 
   60 typedef struct { /* structure for a ready-made PPD entry (linear list) */
   61   char                  driver[128]; /* ID of driver */
   62   char                  ppd[1024];   /* ID of PPD URL */
   63   struct ppdlist_t  *next;           /* pointer to next PPD */
   64 } ppdlist_t;
   65 
   66 typedef struct { /* structure for printer ID translations (linear list) */
   67   char                  *oldid,    /* old ID of printer */
   68                         *newid;    /* current ID of printer */
   69   struct idlist_t       *next;     /* pointer to next entry */
   70 } idlist_t;
   71 
   72 /*
   73  * function to load a file into the memory
   74  */
   75 
   76 char  /* O - pointer to the file in memory */
   77 *loadfile(const char *filename) { /* I - file name */
   78   
   79   const int blocksize = 1024 * 1024;
   80   FILE *inputfile;          /* file to be read currently */
   81   char buffer[blocksize + 1];/* data block currently read */
   82   char *data = NULL;        /* the read data */
   83   int size = 1;             /* size of the data in memory */
   84   int bytesread;            /* bytes actually read */
   85 
   86   /* Open the file */
   87 
   88   inputfile = fopen(filename, "r");
   89   if (inputfile == NULL) {
   90     return NULL;
   91   }
   92 
   93   /* Read the whole file into the memory */
   94 
   95   data = (char *)malloc(size);
   96   data[0] = '\0';
   97   while((bytesread = fread(buffer,1,blocksize,inputfile))) {
   98     data = (char *)realloc(data, size + bytesread); 
   99     buffer[bytesread] = '\0';
  100     strcat(data, buffer);
  101     size += bytesread;
  102   }
  103   fclose(inputfile);
  104   /* Make space for additional data */
  105   data = (char *)realloc(data, size + 4096); 
  106   if (strcmp(data, "")) return(data); else { free((void *)data); return(NULL); }
  107 }
  108 
  109 /*
  110  * function to load the printer ID translation table
  111  */
  112 
  113 idlist_t /* O - pointer to the printer ID translation table */
  114 *loadidlist(const char *filename) { /* I - file name */
  115   char          *idlistbuffer = NULL; /* ID list in memory */
  116   char          *scan; /* pointer for scanning through the list */
  117   char          *oldid = NULL,
  118                 *newid = NULL; /* pointers to IDs in the current line */
  119   int           inoldid = 0, /* Are we reading and old or a new ID */
  120                 innewid = 0; /* currently */
  121   idlist_t      *idlist = NULL, /* Pointer to ID list */
  122                 *currentitem = NULL, /* Pointer to current ID list item */
  123                 *newitem; /* Pointer to newly created ID list item */
  124 
  125   idlistbuffer = loadfile(filename);
  126   if (!idlistbuffer) {
  127     fprintf(stderr, "Cannot read file %s!\n", filename);
  128     return NULL;
  129   }
  130   for (scan = idlistbuffer; *scan != '\0'; scan++) {
  131     switch(*scan) {
  132     case '\r':
  133     case '\n': /* line break */
  134       /* line end */
  135       if (inoldid) {
  136     /* Line with only one word, ignore it */
  137     inoldid = 0;
  138     oldid = NULL;
  139     break;
  140       }
  141     case '\t': /* tab */
  142     case ' ': /* space */
  143       /* white space */
  144       if (inoldid) {
  145     /* old ID completed */
  146     inoldid = 0;
  147     *scan = '\0';
  148       }
  149       if (innewid) {
  150     /* new ID completed */
  151     innewid = 0;
  152     *scan = '\0';
  153     if (oldid && newid && (*oldid != '#')) {
  154       /* found a pair of old ID and new ID, add it to the translation
  155          list */
  156       newitem = 
  157         (idlist_t *)malloc(sizeof(idlist_t));
  158       newitem->oldid = oldid;
  159       newitem->newid = newid;
  160       newitem->next = NULL;
  161       if (currentitem) {
  162         currentitem->next = (struct idlist_t *)newitem;
  163       } else {
  164         idlist = newitem;
  165       }
  166       currentitem = newitem;
  167     }
  168     oldid = NULL;
  169     newid = NULL;
  170       }
  171       break;
  172     default:
  173       /* all other characters, so no whitespace now */
  174       if (!(inoldid || innewid)) {
  175     /* last character was whitespace */
  176     if (oldid) {
  177       /* Beginning of second word in the line */
  178       innewid = 1;
  179       newid = scan;
  180     } else {
  181       /* Beginning of first word in the line */
  182       inoldid = 1;
  183       oldid = scan;
  184     }
  185       }
  186     }
  187   }
  188   return idlist;
  189 }
  190 
  191 /*
  192  * function to translate an old printer ID into a new one
  193  */
  194 
  195 char  /* O - new ID */
  196 *translateid(const char *oldid, /* I - Old ID */
  197          idlist_t *idlist) { /* I - ID translation table */
  198   idlist_t      *currentitem = idlist; /* Pointer to current ID list item */
  199 
  200   while(currentitem) {
  201     if (strcmp(oldid, currentitem->oldid) == 0) {
  202       return currentitem->newid;
  203     }
  204     currentitem = (idlist_t *)(currentitem->next);
  205   }
  206   return (char *)oldid;
  207 }
  208 
  209 /*
  210  * function to parse an XML file and do a task on it
  211  */
  212 
  213 enum parse_operation 
  214 { 
  215     PARSE_OP_PRINTER, PARSE_OP_DRIVER, PARSE_OP_OPTION, 
  216     PARSE_OP_OV_DRIVER, PARSE_OP_OV_PRINTER };
  217 
  218 int /* O - Is the requested printer/driver combo already confirmed by the
  219            <drivers> section in the printer XML file (operation = 
  220        PARSE_OP_PRINTER only) or by the <printers> section in the driver
  221            XML file (operation = PARSE_OP_DRIVER only). 1: yes, 0: no. For
  222            all other operations there will be returned always 0. */
  223 parse(char **data, /* I/O - Data to process */
  224       const char *pid,   /* I - Foomatic printer ID */
  225       const char *driver,/* I - driver name */
  226       const char *filename, /* I - file name for error messages */
  227       printerlist_t **printerlist, /* I/O printer list for overview */
  228       int operation,     /* I - See "enum parse_operation" above */
  229       const char **defaultsettings, /* I - Default option settings given on
  230                        the command line */
  231       int num_defaultsettings, /* I - Number of default option settings */
  232       int *nopjl,        /* I/O - 0: driver allows PJL options, 1: driver
  233                 does not allow PJL options (has "<nnpjl />"
  234                 flag in "<execution>" section) */
  235       idlist_t *idlist,  /* I - ID translation table */
  236       int debug) {       /* I - Debug flag: If set, debugging output is
  237                     produced */
  238 
  239   char          *trpid = NULL; /* current printer ID translated according
  240                   to translation table */
  241   int           datalength = 0;  /* length of the XML file */
  242   int           linecount = 1;   /* Count the lines for error messages */
  243   int           nestinglevel = 0;/* How many XML environments are nested
  244                     at the point where we are */
  245   int           inxmlheader = 1;
  246   int           intag = 0;
  247   int           incomment = 0;
  248   int           tagnamefound = 0;
  249   int           intagword = 0;
  250   int           inquotes = 0;
  251   int           insinglequotes = 0;
  252   int           indoublequotes = 0;
  253   enum tag_type /* Arithmetic values, do not change! */ 
  254   { TAG_OPEN = 01, TAG_CLOSE = -01, TAG_EMPTY = 0 };
  255   int           tagtype = 0; /*1: opening, -1: closing, 0: opening & closing*/
  256   int           inprinter = 0;
  257   int           inmake = 0;
  258   int           inmodel = 0;
  259   int           inautodetect = 0;
  260   int           indriver = 0;
  261   int           indrivers = 0;
  262   int           inexecution = 0;
  263   int           inprototype = 0;
  264   int           innopjl = 0;
  265   int           inprinters = 0;
  266   int           inid = 0;
  267   int           inppd = 0;
  268   int           inlang = 0;
  269   int           inpostscript = 0;
  270   int           inoption = 0;
  271   int           inargshortname = 0;
  272   int           inargexecution = 0;
  273   int           inargpjl = 0;
  274   int           inevshortname = 0;
  275   int           inen = 0;
  276   int           inargmax = 0;
  277   int           inargmin = 0;
  278   int           inenumval = 0;
  279   int           inconstraints = 0;
  280   int           inconstraint = 0;
  281   int           inargdefault = 0;
  282   int           infunctionality = 0;
  283   int           inunverified = 0;
  284   int           indfunctionality = 0;
  285   int           incomments = 0;
  286   int           printertobesaved = 0;
  287   int           printerentryfound = 0;
  288   int           enumvaltoberemoved = 0;
  289   int           optionqualified = 0;
  290   int           enumvalqualified = 1;
  291   int           numenumvals = 1; /* Number of enumeration values,
  292                     disqualifies option when 0 at the end of
  293                     the file. It is set to zero in the 
  294                     beginning of an enum option, for boolean
  295                     or numerical options it stays 1 to not
  296                     disqualify the option */ 
  297     enum option_types 
  298     { 
  299         OPTION_TYPE_ENUM, OPTION_TYPE_BOOL, 
  300         OPTION_TYPE_INT, OPTION_TYPE_FLOAT };
  301   int           optiontype = 0; /* 0: enum, 1: bool, 2: int, 3: float */
  302   int           printerscore = 0;
  303   int           driverscore = 0;
  304   int           printerhiscore = 0;
  305   int           driverhiscore = 0;
  306   int           defaultlinelength = 0;
  307   char          currtagname[256];
  308   char          currtagparam[256];
  309   char          currtagbody[65536];
  310   int           userdefault = 0;
  311   int           userdefaultfound = 0;
  312   char          userdefaultvalue[256];
  313   char          userdefaultid[256];
  314   char          currevid[256];
  315   double        maxnumvalue = 0;
  316   double        minnumvalue = 0;
  317   int           csense = 0;
  318   char          cprinter[256];
  319   char          cmake[256];
  320   char          cmodel[256];
  321   char          cdriver[256];
  322   char          cid[256];
  323   char          cppd[1024];
  324   char          cfunctionality[256];
  325   int           cunverified = 0;
  326   char          cautodetectentry[4096];
  327   char          cargdefault[256];
  328   char          argdefault[256];
  329   char          defaultline[256];
  330   char          printerentry[1024*1024];
  331   char          dfunctionalityentry[10240];
  332   const char    *scan;               /* pointer for scanning through the file*/
  333   const char    *lasttag = NULL;     /* Start of last XML tag */
  334   const char    *lasttagend = NULL;  /* End of last XML tag */
  335   const char    *tagwordstart = NULL;/* Beginning of tagname */
  336   char    *lastprinters = NULL;      /* Start of last <printers> tag */
  337   char    *lastprinter = NULL;       /* Start of last <printer> tag */
  338   char    *lastenumval = NULL;       /* Start of last <enum_val> tag */
  339   char    *lastconstraints = NULL;   /* Start of last <constraints> tag */
  340   char    *lastoption = NULL;        /* Start of last <option> tag */
  341   char    *lastautodetect = NULL;    /* Start of last <autodetect> tag */
  342   char    *lastdfunctionality = NULL;/* Start of last <functionality> tag */
  343   char    *lastcomments = NULL;      /* Start of last <comments> tag */
  344   char    *lastprototype = NULL;     /* Start of last <prototype> tag */
  345   static char   make[256];           /* Printer make/model read from printer */
  346   static char   model[256];          /* XML file needed by constraints in */
  347                                      /* option XML files */
  348   int           comboconfirmed = 0;
  349   int           driverhasproto = 0;
  350   int           exceptionfound = 0;
  351 
  352   char          *s;
  353   int           l;
  354   int           j;
  355   int           k;
  356   printerlist_t *plistpointer;   /* pointers to navigate through the printer */
  357   driverlist_t  *dlistpointer;   /* list for the overview */
  358   ppdlist_t     *ppdlistpointer;
  359   printerlist_t *plistpreventry;
  360   driverlist_t  *dlistpreventry;
  361   driverlist_t  *dlistnextentry;
  362   ppdlist_t     *ppdlistpreventry;
  363   ppdlist_t     *ppdlist = NULL;
  364 
  365   /* Translate printer ID */
  366   if ((pid) && (operation < 3)) trpid = translateid(pid, idlist);
  367 
  368   j = 0;
  369   datalength = strlen(*data); /* Compute the length of the file once,
  370                      this is very time-intensive */
  371   if (operation == 1) *nopjl = 0; /* When we parse a driver, put the nopjl
  372                      flag to zero, the driver can switch it
  373                      to 1 when it contains "<nopjl />" */
  374   for (scan = *data; *scan != '\0'; scan++) {
  375     switch(*scan) {
  376     case '<': /* open angle bracket */
  377       if (!inquotes) {
  378     if (intag) {
  379       if (!incomment && !inxmlheader) {
  380         /* Unless a tag is a comment, angle brackets cannot appear inside
  381            the tag. */
  382         fprintf(stderr, "XML error: Nested angle brackets in %s, line %d!\n",
  383             filename, linecount);
  384         exit(1);
  385       }
  386           /* In a comment angle brackets are treated as a part of a word */
  387       /*if (!intagword) {
  388         intagword = 1;
  389         tagwordstart = scan;
  390         }*/
  391     } else {
  392       intag = 1;
  393       if (scan + 3 < *data + datalength) {
  394         if ((*(scan + 1) == '!') && (*(scan + 2) == '-') &&
  395         (*(scan + 3) == '-')) {
  396           incomment = 1;
  397           tagtype = TAG_EMPTY;
  398           if (debug) fprintf(stderr, "    Start of a comment\n");
  399         }
  400       }
  401       if (!incomment) {
  402         tagnamefound = 0;
  403         tagtype = TAG_OPEN;
  404         lasttag = scan;
  405       }
  406     }
  407       }
  408       break;
  409     case '\n': /* line break */
  410       linecount ++;
  411     case '/': /* tag closing */
  412     case '>': /* close angle bracket */
  413     case ' ': /* white space */
  414     case '\t':
  415     case '\r': /* Carriage return of dossy XML file */
  416       /* word delimiters */
  417       if (!inquotes) {
  418     if (intag) {
  419       if (!incomment) {
  420         if (intagword) { /* a word in the XML tag finished */
  421           intagword = 0;
  422           if (!tagnamefound) { /* 1st word = tag name */
  423         tagnamefound = 1;
  424         memmove(currtagname, tagwordstart, scan - tagwordstart);
  425         currtagname[scan - tagwordstart] = '\0';
  426         if (debug)
  427                 fprintf(stderr, "    Tag Name: '%s'\n", currtagname);
  428         switch (+operation) {
  429         case (+PARSE_OP_PRINTER):  /* Printer XML file */
  430         switch (+*currtagname) {
  431           case 'm': if (strcmp(currtagname, "make") == 0) {
  432             inmake = nestinglevel + 1;
  433           } else if (strcmp(currtagname, "model") == 0) {
  434             inmodel = nestinglevel + 1;
  435           } break;
  436           case 'a': if (strcmp(currtagname, "autodetect") == 0) {
  437             inautodetect = nestinglevel + 1;
  438           } break;
  439           case 'd': if (strcmp(currtagname, "driver") == 0) {
  440             indriver = nestinglevel + 1;
  441             if (indrivers) {
  442               if (+tagtype == +TAG_OPEN) {
  443             if (debug)
  444               fprintf(stderr, 
  445                   "    Resetting Driver.\n");
  446             cid[0] = '\0';
  447               }
  448             }
  449           } else if (strcmp(currtagname, "drivers") == 0) {
  450             indrivers = nestinglevel + 1;
  451           } break;
  452           case 'i': if (strcmp(currtagname, "id") == 0) {
  453             inid = nestinglevel + 1;
  454           } break; 
  455           case 'p': if (strcmp(currtagname, "printer") == 0) {
  456             inprinter = nestinglevel + 1;
  457             if (+tagtype == +TAG_OPEN) {
  458               /* XML body of the file is starting here */
  459               inxmlheader = 0;
  460               nestinglevel = 1;
  461               /* Remove the whole header of the XML file */
  462               if (debug) 
  463             fprintf(stderr,
  464                 "    Removing XML file header\n");
  465               memmove(*data, lasttag, 
  466                   *data + datalength + 1 - lasttag);
  467               datalength -= lasttag - *data;
  468               scan -= lasttag - *data;
  469               tagwordstart -= lasttag - *data;
  470               lasttag = *data;
  471               lasttagend = NULL;
  472             }
  473           } else if (strcmp(currtagname, "postscript") == 0) {
  474             inpostscript = nestinglevel + 1;
  475             if (inlang) {
  476               if (+tagtype == +TAG_OPEN) {
  477             if (debug)
  478               fprintf(stderr, 
  479                   "    Resetting Driver/PPD.\n");
  480             cid[0] = '\0';
  481             cppd[0] = '\0';
  482               }
  483             }
  484           } else if (strcmp(currtagname, "ppd") == 0) {
  485             inppd = nestinglevel + 1;
  486           } break;
  487           case 'l': if (strcmp(currtagname, "lang") == 0) {
  488             inlang = nestinglevel + 1;
  489           } break; 
  490         } break; 
  491         case (+PARSE_OP_DRIVER): { /* Driver XML file */
  492           if (strcmp(currtagname, "printer") == 0) {
  493             inprinter = nestinglevel + 1;
  494             if (+tagtype == +TAG_OPEN) lastprinter = (char*)lasttag;
  495           } else if (strcmp(currtagname, "execution") == 0) {
  496             inexecution = nestinglevel + 1;
  497           } else if (strcmp(currtagname, "nopjl") == 0) {
  498             innopjl = nestinglevel + 1;
  499             if (inexecution) {
  500               *nopjl = 1;
  501               if (debug)
  502             fprintf
  503               (stderr,
  504                "      <nopjl /> found, driver does not allow PJL options!\n");
  505             }
  506           } else if (strcmp(currtagname, "id") == 0) {
  507             inid = nestinglevel + 1;
  508           } else if (strcmp(currtagname, "printers") == 0) {
  509             inprinters = nestinglevel + 1;
  510             if (+tagtype == +TAG_OPEN) {
  511               /* Mark up to the end of the tag before, so that there do
  512              not remain empty lines or other whitespace after
  513              deleting this constraint */
  514               lastprinters = (char*)lasttagend + 1;
  515               printerentry[0] = '\0';
  516             }
  517           } else if (strcmp(currtagname, "driver") == 0) {
  518             indriver = nestinglevel + 1;
  519             if (+tagtype == +TAG_OPEN) {
  520               /* XML body of the file is starting here */
  521               inxmlheader = 0;
  522               nestinglevel = 1;
  523               /* Remove the whole header of the XML file */
  524               if (debug) 
  525             fprintf(stderr,
  526                 "    Removing XML file header\n");
  527               memmove(*data, lasttag, 
  528                   *data + datalength + 1 - lasttag);
  529               datalength -= lasttag - *data;
  530               scan -= lasttag - *data;
  531               tagwordstart -= lasttag - *data;
  532               lasttag = *data;
  533               lasttagend = NULL;
  534             }
  535           } 
  536         } break; 
  537         case (+PARSE_OP_OPTION): /* Option XML file */ 
  538         switch (+*currtagname) {
  539         case 'm':  if (strcmp (currtagname + 01, "make" + 01) == 0) {
  540             inmake = nestinglevel + 1;
  541           } else if (strcmp (currtagname + 01, "model" + 01) == 0) {
  542             inmodel = nestinglevel + 1;
  543           } break;
  544         case 'd': if (strcmp (currtagname + 01, "driver" + 01) == 0) {
  545             indriver = nestinglevel + 1;
  546           } break;
  547         case 'p': if (strcmp (currtagname + 01, "printer" + 01) == 0) {
  548             inprinter = nestinglevel + 1;
  549           } break;
  550         case 'a': 
  551         if (!strncmp (currtagname, "arg_", 04)) 
  552         switch (+currtagname [04]) {
  553             case 'd': if (strcmp(currtagname + 05, "defval" + 01) == 0) {
  554             inargdefault = nestinglevel + 1;
  555           } break;
  556           case 's': if (strcmp(currtagname + 05, "shortname" + 01) == 0) {
  557             inargshortname = nestinglevel + 1;
  558           } break;
  559           case 'e': if (strcmp (currtagname + 05, "execution" + 01) == 0) {
  560             inargexecution = nestinglevel + 1;
  561           } break;
  562           case 'p': 
  563           if (strcmp (currtagname + 05, "pjl" + 01) == 0) 
  564           /* ?ARG_PJL Y */ {
  565             inargpjl = nestinglevel + 1;
  566             if (inargexecution) /* ?IN_ARG_EXECUTION Y */ {
  567               /* We have a PJL option ... */
  568               if (*nopjl) /* ?NO_PJL Y */ {
  569             /* ... and the driver does not allow it. 
  570                So skip this option. */
  571             free((void *)(*data));
  572             *data = NULL;
  573             if (debug)
  574               fprintf
  575                 (stderr,
  576 "      Driver does not allow PJL options and this is a PJL option -->\n"
  577 "    Option does not apply!\n");
  578             return +comboconfirmed;
  579               } /* ?NO_PJL */
  580             } /* ?IN_ARG_EXECUTION */
  581           }  /* ?ARG_PJL */ break; 
  582           case 'm': if (strcmp(currtagname + 05, "max" + 01) == 0) {
  583             inargmax = nestinglevel + 1;
  584           } else if (strcmp(currtagname + 05, "min" + 01) == 0) {
  585             inargmin = nestinglevel + 1;
  586           } } break;
  587           case 'e': if (strcmp (currtagname + 01, "ev_shortname" + 01) == 0) {
  588             inevshortname = nestinglevel + 1;
  589           } else if (strcmp(currtagname + 01, "en" + 01) == 0) {
  590             inen = nestinglevel + 1;
  591           } else if (strcmp (currtagname + 01, "enum_val" + 01) == 0) {
  592             inenumval = nestinglevel + 1;
  593             if (+tagtype == +TAG_OPEN) {
  594               /* New enum value, enum values are qualified by default
  595              and can be disqualified by constraints */
  596               enumvalqualified = 1;
  597               enumvaltoberemoved = 0;
  598               /* Mark up to the end of the tag before, so that there do
  599              not remain empty lines or other whitespace after
  600              deleting this constraint */
  601               lastenumval = (char*)lasttagend + 1;
  602             }
  603           } break; 
  604           case 'c': if (strcmp (currtagname + 01, "constraints" + 01) == 0) {
  605             inconstraints = nestinglevel + 1;
  606             if (+tagtype == +TAG_OPEN) {
  607               /* Reset high scores */
  608               printerhiscore = 0;
  609               driverhiscore = 0;
  610               /* Mark up to the end of the tag before, so that there do
  611              not remain empty lines or other whitespace after
  612              deleting this constraint */
  613               lastconstraints = (char*)lasttagend + 1;
  614             }
  615           } else if (strcmp(currtagname, "constraint") == 0) {
  616             inconstraint = nestinglevel + 1;
  617             if (+tagtype == +TAG_OPEN) {
  618               /* Delete the fields of the old constraint */
  619               cprinter[0] = '\0';
  620               cmake[0] = '\0';
  621               cmodel[0] = '\0';
  622               cdriver[0] = '\0';
  623               cargdefault[0] = '\0';
  624               csense = 0;
  625             }
  626           } break;
  627           case 'o': if (strcmp(currtagname + 01, "option" + 01) == 0) {
  628             inoption = nestinglevel + 1;
  629             /* Mark up to the end of the tag before, to insert the
  630                definition of the default option setting */
  631             if (+tagtype == +TAG_CLOSE) lastoption = (char*)lasttagend + 1;
  632             if (+tagtype == +TAG_OPEN) {
  633               /* XML body of the file is starting here */
  634               inxmlheader = 0;
  635               nestinglevel = 1;
  636               argdefault[0] = '\0';
  637               /* Remove the whole header of the XML file */
  638               if (debug) 
  639             fprintf(stderr,
  640                 "    Removing XML file header\n");
  641               memmove(*data, lasttag, 
  642                   *data + datalength + 1 - lasttag);
  643               datalength -= lasttag - *data;
  644               scan -= lasttag - *data;
  645               tagwordstart -= lasttag - *data;
  646               lasttag = *data;
  647               lasttagend = NULL;
  648             }
  649           } break;
  650         } break; 
  651         case (+PARSE_OP_OV_DRIVER): { /* Driver XML file (Overview) */
  652           if (strcmp(currtagname, "printer") == 0) {
  653             inprinter = nestinglevel + 1;
  654             if (+tagtype == +TAG_OPEN) {
  655               cprinter[0] = '\0';
  656               dfunctionalityentry[0] = '\0';
  657             }
  658           } else if (strcmp(currtagname, "id") == 0) {
  659             inid = nestinglevel + 1;
  660           } else if (strcmp(currtagname, "functionality") == 0) {
  661             indfunctionality = nestinglevel + 1;
  662             if (+tagtype == +TAG_OPEN) lastdfunctionality = (char*)lasttag;
  663           } else if (strcmp(currtagname, "execution") == 0) {
  664             inexecution = nestinglevel + 1;
  665           } else if (strcmp(currtagname, "prototype") == 0) {
  666             inprototype = nestinglevel + 1;
  667             if (+tagtype == +TAG_OPEN) lastprototype = (char*)lasttagend + 1;
  668           } else if (strcmp(currtagname, "printers") == 0) {
  669             inprinters = nestinglevel + 1;
  670             if (+tagtype == +TAG_OPEN) lastprinters = (char*)lasttagend + 1;
  671           } else if (strcmp(currtagname, "comments") == 0) {
  672             incomments = nestinglevel + 1;
  673             if (+tagtype == +TAG_OPEN) lastcomments = (char*)lasttagend + 1;
  674           } else if (strcmp(currtagname, "driver") == 0) {
  675             indriver = nestinglevel + 1;
  676             if (+tagtype == +TAG_OPEN) {
  677               /* XML body of the file is starting here */
  678               inxmlheader = 0;
  679               nestinglevel = 1;
  680               /* Remove the whole header of the XML file */
  681               if (debug) 
  682             fprintf(stderr,
  683                 "    Removing XML file header\n");
  684               memmove(*data, lasttag, 
  685                   *data + datalength + 1 - lasttag);
  686               datalength -= lasttag - *data;
  687               scan -= lasttag - *data;
  688               tagwordstart -= lasttag - *data;
  689               lasttag = *data;
  690               lasttagend = NULL;
  691             }
  692           } 
  693         } break; 
  694         case (+PARSE_OP_OV_PRINTER): { /* Printer XML file (Overview)*/
  695           if (debug)
  696             fprintf(stderr,
  697                 "     Printer XML (Overview): Tag name: %s\n",
  698                 currtagname);
  699           if (strcmp(currtagname, "make") == 0) {
  700             inmake = nestinglevel + 1;
  701           } else if (strcmp(currtagname, "model") == 0) {
  702             inmodel = nestinglevel + 1;
  703           } else if (strcmp(currtagname, "functionality") == 0) {
  704             infunctionality = nestinglevel + 1;
  705           } else if (strcmp(currtagname, "unverified") == 0) {
  706             inunverified = nestinglevel + 1;
  707             cunverified = 1;
  708           } else if (strcmp(currtagname, "driver") == 0) {
  709             indriver = nestinglevel + 1;
  710             if (indrivers) {
  711               if (+tagtype == +TAG_OPEN) {
  712             if (debug)
  713               fprintf(stderr, 
  714                   "    Resetting Driver/PPD.\n");
  715             cid[0] = '\0';
  716             cppd[0] = '\0';
  717               }
  718             }
  719           } else if (strcmp(currtagname, "drivers") == 0) {
  720             indrivers = nestinglevel + 1;
  721           } else if (strcmp(currtagname, "id") == 0) {
  722             inid = nestinglevel + 1;
  723           } else if (strcmp(currtagname, "ppd") == 0) {
  724             inppd = nestinglevel + 1;
  725           } else if (strcmp(currtagname, "lang") == 0) {
  726             inlang = nestinglevel + 1;
  727           } else if (strcmp(currtagname, "postscript") == 0) {
  728             inpostscript = nestinglevel + 1;
  729             if (inlang) {
  730               if (+tagtype == +TAG_OPEN) {
  731             if (debug)
  732               fprintf(stderr, 
  733                   "    Resetting Driver/PPD.\n");
  734             cid[0] = '\0';
  735             cppd[0] = '\0';
  736               }
  737             }
  738           } else if (strcmp(currtagname, "autodetect") == 0) {
  739             inautodetect = nestinglevel + 1;
  740             if (+tagtype == +TAG_OPEN) lastautodetect = (char*)lasttag;
  741           } else if (strcmp(currtagname, "printer") == 0) {
  742             inprinter = nestinglevel + 1;
  743             if (+tagtype == +TAG_OPEN) {
  744               /* XML body of the file is starting here */
  745               inxmlheader = 0;
  746               nestinglevel = 1;
  747               /* Remove the whole header of the XML file */
  748               if (debug) 
  749             fprintf(stderr,
  750                 "    Removing XML file header\n");
  751               memmove(*data, lasttag, 
  752                   *data + datalength + 1 - lasttag);
  753               datalength -= lasttag - *data;
  754               scan -= lasttag - *data;
  755               tagwordstart -= lasttag - *data;
  756               lasttag = *data;
  757               lasttagend = NULL;
  758               if (debug) fprintf(stderr, 
  759                      "    Initializing PPD list.\n");
  760               while(ppdlist != NULL) {
  761             ppdlistpointer = ppdlist;
  762             ppdlist = (ppdlist_t *)ppdlist->next;
  763             free(ppdlistpointer);
  764               }
  765               if (debug) fprintf(stderr, 
  766                      "    Initializing fields.\n");
  767               cprinter[0] = '\0';
  768               cmake[0] = '\0';
  769               cmodel[0] = '\0';
  770               cfunctionality[0] = '\0';
  771               cunverified = 0;
  772               cdriver[0] = '\0';
  773               cautodetectentry[0] = '\0';
  774             }
  775           }
  776         } break; }
  777           } else { /* additional word = parameter */
  778         memmove(currtagparam, tagwordstart, scan - tagwordstart);
  779         currtagparam[scan - tagwordstart] = '\0';
  780         if (debug) fprintf(stderr, 
  781                    "    Tag parameter: '%s'\n",
  782                    currtagparam); 
  783         if (+operation == +PARSE_OP_OPTION) { /* Option XML file */
  784           if (strcmp(currtagname, "constraint") == 0) {
  785             /* Set the sense of the constraint */
  786             if ((s = strstr(currtagparam, "sense")) != NULL) {
  787               if (strstr(s + 5, "true") != NULL) {
  788             csense = 1;
  789               } else if (strstr(s + 5, "false") != NULL) {
  790             csense = 0;
  791               }
  792             }
  793           } else if (strcmp(currtagname, "option") == 0) {
  794             if ((s = strstr(currtagparam, "type")) != NULL) {
  795               if (strstr(s + 4, "enum") != NULL) {
  796             /* Set the number of qualified enum values to 0 */
  797             /* If this value stays 0 we have no enum values */
  798             /* and the option disqualifies */
  799             numenumvals = 0;
  800             optiontype = +OPTION_TYPE_ENUM;
  801               } else if (strstr(s + 4, "bool") != NULL) {
  802             optiontype = +OPTION_TYPE_BOOL;
  803               } else if (strstr(s + 4, "int") != NULL) {
  804             optiontype = +OPTION_TYPE_INT;
  805               } else if (strstr(s + 4, "float") != NULL) {
  806             optiontype = +OPTION_TYPE_FLOAT;
  807               }
  808             }
  809           } else if (strcmp(currtagname, "enum_val") == 0) {
  810             if ((s = strstr(currtagparam, "id")) != NULL) {
  811               /* Extract the ID of this enum value */
  812               strcpy(currevid, s + 4);
  813               currevid[strlen(currevid) - 1] = '\0';
  814               if (debug)
  815             fprintf(stderr, 
  816                 "    Enum value ID: '%s'\n",
  817                 currevid);
  818             }
  819           }
  820         } else if (+operation == +PARSE_OP_OV_DRIVER) { /* Driver XML file (Overview) */
  821           if (strcmp(currtagname, "driver") == 0) {
  822             if ((s = strstr(currtagparam, "id")) != NULL) {
  823               /* Get the short driver name (w/o "driver/") */
  824               s = strstr(s + 2, "driver/") + 7;
  825               /* Cut off trailing '"' */
  826               s[strlen(s)-1] = '\0';
  827               strcpy(cdriver, s);
  828             }
  829           }
  830         } else if (+operation == +PARSE_OP_OV_PRINTER) { /* Printer XML file (Overview) */
  831           if (debug)
  832             fprintf(stderr, 
  833                 "    Printer XML file (overview): Tag name: %s, Tag param:%s\n", 
  834                 currtagname, currtagparam);
  835           if (strcmp(currtagname, "printer") == 0) {
  836             if ((s = strstr(currtagparam, "id")) != NULL) {
  837                       /* Get the short printer name (w/o "printer/") */
  838                       if ((s = strstr(s + 2, "printer/")) != NULL) {
  839                           s += 8;
  840                           /* Cut off trailing '"' */
  841                           s[strlen(s)-1] = '\0';
  842                           strcpy(cprinter, s);
  843                       }
  844             }
  845           }
  846         }
  847           }
  848         }
  849         if (*scan == '/') { /* tag closing */
  850           if (tagnamefound) { /* '/' after tag name, this tag has no 
  851                      body */
  852         tagtype = +TAG_EMPTY;
  853           } else { /* we are closing a tag */
  854         tagtype = +TAG_CLOSE;
  855           }
  856           if (debug)
  857         fprintf(stderr,
  858             "    End of tag, tag type %d (0: no body, -1: with body)\n", 
  859             tagtype);
  860         }
  861       }
  862       if (*scan == '>') { /* and of tag */
  863         if (incomment) {
  864           if ((*(scan - 2) == '-') && (*(scan - 1) == '-')) {
  865         incomment = 0;
  866         intag = 0;
  867         if (debug) fprintf(stderr, "    End comment\n");
  868           }
  869         } else {
  870           intag = 0;
  871           if (!inxmlheader && (tagnamefound == 0)) {
  872         fprintf(stderr, "XML error: Tag without name %s, line %d!\n",
  873             filename, linecount);
  874         exit(1);
  875           }
  876           if (lasttagend != NULL) {
  877         memmove(currtagbody, lasttagend + 1, lasttag - lasttagend - 1);
  878         currtagbody[lasttag - lasttagend - 1] = '\0';
  879         if (debug)
  880           fprintf(stderr,
  881               "    Contents of tag body: '%s'\n", currtagbody);
  882           }
  883           nestinglevel += tagtype;
  884           /* Important tags end, clear the marks and do appropriate 
  885          actions */
  886           if (+operation == +PARSE_OP_PRINTER) /* ?PARSE_OP_PRINTER Y */ 
  887           { /* Printer XML file */
  888         if (nestinglevel < inprinter) inprinter = 0;
  889         if (nestinglevel < inmake) /* ?OVER_IN_MAKE Y */ { /* Found printer manufacturer */
  890           inmake = 0;
  891           /* Only the <make> outside the <autodetect> tag is valid. */
  892           if (!inautodetect) strcat(make, currtagbody);
  893         } /* ?OVER_IN_MAKE */ 
  894         if (nestinglevel < inmodel) /* ?OVER_IN_MODEL Y */ { /* Found printer model */
  895           inmodel = 0;
  896           /* Only the <model> outside the <autodetect> tag is valid. */
  897           if (!inautodetect) strcat(model, currtagbody);
  898         } /* ?OVER_IN_MODEL */ 
  899         if (nestinglevel < inautodetect) inautodetect = 0;
  900         if (nestinglevel < indrivers) indrivers = 0;
  901         if (nestinglevel < indriver) /* ?OVER_IN_DRIVER Y */ {
  902           indriver = 0;
  903           if (indrivers) /* ?IN_DRIVERS Y */ {
  904             if (debug) fprintf(stderr, 
  905                        "    Printer/Driver: %s %s\n",
  906                        pid, cid);
  907             if (cid[0] != '\0') /* ?HAVE_CID Y */ {
  908               if (debug)
  909             fprintf(stderr,
  910                 "      Printer XML: Printer: %s Driver: %s\n",
  911                 pid, cid);
  912               if (!strcmp(cid, driver)) /* ?CID_IS_DRIVER Y */ {
  913             /* Printer/driver combo already confirmed by
  914                <drivers> section of printer XML file */
  915             if (debug)
  916               fprintf(stderr,
  917                   "      Printer XML: Printer/Driver combo confirmed by <drivers> section!\n");
  918             comboconfirmed = 1;
  919               } /* ?CID_IS_DRIVER */
  920             } /* ?HAVE_CID */ 
  921           } /* ?IN_DRIVERS */ 
  922         } /* ?OVER_IN_DRIVER */
  923         if (cppd[0]) strcpy(cid, "Postscript");
  924         if (nestinglevel < inid) /* ?OVER_IN_ID Y */ {
  925           inid = 0;
  926           strcpy(cid, currtagbody);
  927           if (debug) fprintf(stderr, 
  928                      "    Printer XML: Driver ID: %s\n", cid);
  929         } /* ?OVER_IN_ID */
  930         if (nestinglevel < inlang) inlang = 0;
  931         if (nestinglevel < inpostscript) /* ?OVER_IN_POSTSCRIPT Y */ {
  932           inpostscript = 0;
  933           if (inlang) /* ?IN_LANG Y */ {
  934             if (debug) fprintf(stderr, 
  935                        "    Printer/Driver/PPD: %s %s %s\n",
  936                        cprinter, cid, cppd);
  937           } /* ?IN_LANG */ 
  938           if (!strcmp(cid, driver)) /* ?CID_IS_DRIVER Y */ {
  939             /* Printer/driver combo already confirmed by
  940                <postscript> section of printer XML file */
  941             if (debug)
  942               fprintf(stderr,
  943                   "      Printer XML: Printer/Driver combo confirmed by <postscript> section!\n");
  944             comboconfirmed = 1;
  945           } /* ?CID_IS_DRIVER */ 
  946         } /* ?OVER_IN_POSTSCRIPT */
  947         if (nestinglevel < inppd) /* ?OVER_IN_PPD Y */ {
  948           inppd = 0;
  949           /* Get the PPD file location without leading 
  950              white space, is empty on empty PPD file location*/
  951           for (s = currtagbody;
  952                (*s != '\0') && (strchr(" \n\r\t", *s) != NULL);
  953                s ++);
  954           strcpy(cppd, s);
  955           if (debug) fprintf(stderr, 
  956                      "    PPD URL: %s\n", cppd);
  957         } /* ?OVER_IN_PPD */ 
  958           } else /* ?PARSE_OP_PRINTER N */ 
  959           if (+operation == +PARSE_OP_DRIVER) /* ?PARSE_OP_DRIVER Y */ 
  960           { /* Driver XML file */
  961         if (nestinglevel < inexecution) inexecution = 0;
  962         if (nestinglevel < innopjl) innopjl = 0;
  963         if (nestinglevel < indriver) indriver = 0;
  964         if (nestinglevel < inprinters) /* ?OVER_IN_PRINTERS Y */ {
  965           inprinters = 0;
  966           /* Remove the whole <printers> block */
  967           if (lastprinters != NULL) /* ?LAST_PRINTERS Y */ {
  968             if (debug) 
  969               fprintf(stderr, "    Removing <printers> block\n");
  970             memmove(lastprinters, scan + 1, 
  971                 *data + datalength - scan);
  972             datalength -= scan + 1 - lastprinters;
  973             scan = lastprinters - 1;
  974             if (debug) 
  975               fprintf(stderr, "    Inserting saved printer\n");
  976             l = strlen(printerentry);
  977             if (l != 0) /* ?PRINTER_ENTRY Y */ {
  978               memmove(lastprinters + l, lastprinters, 
  979                   *data + datalength - lastprinters + 1);
  980               memmove(lastprinters, printerentry, l);
  981               datalength += l;
  982               scan += l;
  983             } /* ?PRINTER_ENTRY */ 
  984           } /* ?LAST_PRINTERS */
  985         } /* ?OVER_IN_PRINTERS */
  986         if (nestinglevel < inprinter) /* ?OVER_IN_PRINTER Y */ {
  987           inprinter = 0;
  988           if (printertobesaved) /* ?PRINTER_TO_BE_SAVED Y */ {
  989             /* Save the printer entry in a buffer to reinsert it
  990                after deleting the <printers> block */
  991             printertobesaved = 0;
  992             if (lastprinter != NULL) /* ?LAST_PRINTER Y */ {
  993               if (debug) fprintf(stderr, "    Saving printer\n");
  994               strcat(printerentry,"\n <printers>\n  ");
  995               l = strlen(printerentry);
  996               memmove(printerentry + l, lastprinter,
  997                   scan + 1 - lastprinter);
  998               printerentry[l + scan + 1 - lastprinter] = '\0';
  999               strcat(printerentry,"\n </printers>");
 1000               if (debug) fprintf(stderr, "    Printer entry %s\n",
 1001                      printerentry);
 1002             } /* ?LAST_PRINTER */
 1003           } /* ?PRINTER_TO_BE_SAVED */
 1004         } /* ?OVER_IN_PRINTER */
 1005         if (nestinglevel < inid) /* ?OVER_IN_ID Y */ {
 1006           inid = 0;
 1007           /* printer ID after the "printer/" in currtagbody is 
 1008              used (to not compare the always equal "printer/" */
 1009           if (strcmp(trpid, 
 1010                  translateid(currtagbody + 8, idlist)) == 0) /* ?FOUND_PRINTER_IN_DRIVER Y */ {
 1011             /* Found printer entry in driver file */
 1012             printerentryfound = 1;
 1013             printertobesaved = 1;
 1014             if (debug) fprintf(stderr, "    Found printer\n");
 1015           } else /* ?FOUND_PRINTER_IN_DRIVER N */ {
 1016             if (debug) fprintf(stderr, "    Other printer\n");
 1017           }  /* ?FOUND_PRINTER_IN_DRIVER */
 1018         } /* ?OVER_IN_ID Y */
 1019           } else  /* ?PARSE_OP_DRIVER N */ 
 1020           if (+operation == +PARSE_OP_OPTION) /* ?PARSE_OP_OPTION Y */ 
 1021           { /* Option XML file */
 1022         if ((debug) && (strcmp(currtagname, "constraint") == 0) &&
 1023             (tagtype == +TAG_CLOSE)) /* ?CLOSE_CONSTRAINT Y */ {
 1024           j++;
 1025           fprintf(stderr, "    Constraint %d: %s\n", j, filename);
 1026         } /* ?CLOSE_CONSTRAINT */ 
 1027         if (nestinglevel < inen) /* ?OVER_IN_EN Y */ {
 1028           inen = 0;
 1029           if (inargshortname) /* ?IN_ARG_SHORT_NAME Y */ {
 1030             /* We have the short name of the option, check whether
 1031                the user has defined a default value for it */
 1032             if (debug)
 1033               fprintf
 1034             (stderr, "    Option short name: '%s'\n",
 1035              currtagbody);
 1036             for (k = 0; k < num_defaultsettings; k ++) /* @DEFAULT_SETTINGS */ 
 1037             {
 1038               if ((strstr(defaultsettings[k], currtagbody) == 
 1039                defaultsettings[k]) && 
 1040               (*(defaultsettings[k] + strlen(currtagbody))
 1041                == '=')) /* ?FOUND_DEFAULT_SETTING Y */ {
 1042             s = (char *)(defaultsettings[k] +
 1043                      strlen(currtagbody) + 1);
 1044             userdefault = 1;
 1045             switch (+optiontype)  /* *OPTION_TYPE */ {
 1046             case (+OPTION_TYPE_BOOL): {
 1047               /* Boolean options */
 1048               if ((strcasecmp(s, "true") == 0) ||
 1049                   (strcasecmp(s, "yes") == 0) ||
 1050                   (strcasecmp(s, "on") == 0)) 
 1051                 /* "True" */
 1052                 s = "1";
 1053               else if ((strcasecmp(s, "false") == 0) ||
 1054                      (strcasecmp(s, "no") == 0) ||
 1055                      (strcasecmp(s, "off") == 0)) 
 1056                 /* "False" */
 1057                 s = "0";
 1058               else if ((strcasecmp(s, "0") != 0) &&
 1059                      (strcasecmp(s, "1") != 0)) 
 1060                 /* No valid value for a bool option */
 1061                 userdefault = 0;
 1062             } break;
 1063             case (+OPTION_TYPE_INT): {
 1064               /* Integer options */
 1065               if (strspn(s, "+-0123456789") < strlen(s)) 
 1066                 userdefault = 0;
 1067             } break;
 1068             case (+OPTION_TYPE_FLOAT): {
 1069               /* Float options */
 1070               if (strspn(s, "+-0123456789.eE") < strlen(s)) 
 1071                 userdefault = 0;
 1072             } break; } /* *OPTION_TYPE X */ 
 1073             strcpy(userdefaultvalue, s);
 1074             if ((debug) && (userdefault))
 1075               fprintf
 1076                 (stderr,
 1077                  "      User default setting: '%s'\n",
 1078                  userdefaultvalue);
 1079               } else /* ?FOUND_DEFAULT_SETTING N */
 1080               if ((strcmp(defaultsettings[k], currtagbody) ==
 1081                   0) && (+optiontype == +OPTION_TYPE_BOOL)) 
 1082                   /* ?DEFAULT_TRUE Y */ {
 1083             /* "True" for boolean options */
 1084             strcpy(userdefaultvalue, "1");
 1085             userdefault = 1;
 1086             if (debug)
 1087               fprintf
 1088                 (stderr,
 1089                  "      User default setting: '%s'\n",
 1090                  userdefaultvalue);
 1091               } else /* ?DEFAULT_TRUE N */ 
 1092               if ((strcmp(defaultsettings[k] + 2,currtagbody)
 1093                   == 0) &&
 1094                  (strncasecmp(defaultsettings[k], "no", 2)
 1095                   == 0) && (+optiontype == +OPTION_TYPE_BOOL)) 
 1096                   /* ?DEFAULT_FALSE Y */ {
 1097             /* "False" for boolean options */
 1098             strcpy(userdefaultvalue, "0");
 1099             userdefault = 1;
 1100             if (debug)
 1101               fprintf
 1102                 (stderr,
 1103                  "      User default setting: '%s'\n",
 1104                  userdefaultvalue);
 1105               } /* ?DEFAULT_FALSE ?DEFAULT_TRUE ?FOUND_DEFAULT_SETTING */
 1106             } /* @DEFAULT_SETTINGS X */
 1107           } else  /* ?IN_ARG_SHORT_NAME N */ 
 1108           if (inevshortname) /* ?IN_EV_SHORT_NAME Y */ {
 1109             /* We have the short name of the enum value, check
 1110                whether the user chose this value as default,
 1111                extract the enum value ID then and mark the user's
 1112                default value as found */
 1113             if (debug)
 1114               fprintf
 1115             (stderr, "    Enum value short name: '%s'\n",
 1116              currtagbody);
 1117             if ((userdefault) &&
 1118             (strcmp(userdefaultvalue, currtagbody) == 0)) 
 1119             /* ?DEFAULT_IN_TAG_BODY Y */ {
 1120               strcpy(userdefaultid, currevid);
 1121               userdefaultfound = 1;
 1122               if (debug)
 1123             fprintf
 1124               (stderr,
 1125                "      User default setting found!\n");
 1126             } /* ?DEFAULT_IN_TAG_BODY Y */
 1127           } /* ?IN_EV_SHORT_NAME ?IN_ARG_SHORT_NAME */
 1128         } /* ?OVER_IN_EN */
 1129         if (nestinglevel < inargmax) /* ?OVER_IN_ARG_MAX Y */ {
 1130           inargmax = 0;
 1131           if 
 1132 ((optiontype == +OPTION_TYPE_INT) 
 1133 || (+optiontype == +OPTION_TYPE_FLOAT)) /* ?OPTION_NUMERIC Y */ {
 1134             maxnumvalue = atof(currtagbody);
 1135             if (userdefault &&
 1136               /* Range-check user default and make it invalid if
 1137              necessary */
 1138               +atof(userdefaultvalue) > +maxnumvalue) 
 1139             userdefault = 0;
 1140             if (debug)
 1141               fprintf
 1142             (stderr,
 1143              "    Maximum value: %s\n",
 1144              currtagbody);
 1145           } /* ?OPTION_NUMERIC */
 1146         } /* ?OVER_IN_ARG_MAX */
 1147         if (nestinglevel < inargmin) /* ?OVER_IN_ARG_MIN Y */ {
 1148           inargmin = 0;
 1149           if 
 1150 ((+optiontype == +OPTION_TYPE_INT) 
 1151 || (+optiontype == +OPTION_TYPE_FLOAT)) /* ?OPTION_NUMERIC Y */ {
 1152             minnumvalue = atof(currtagbody);
 1153             if (userdefault) /* ?USER_DEFAULT Y */ {
 1154               /* Range-check user default and make it invalid if
 1155              necessary */
 1156               if (atof(userdefaultvalue) < minnumvalue) {
 1157             userdefault = 0;
 1158               }
 1159             } /* ?USER_DEFAULT */
 1160             if (debug)
 1161               fprintf
 1162             (stderr,
 1163              "    Minimum value: %s\n",
 1164              currtagbody);
 1165           } /* ?OPTION_NUMERIC */
 1166         } /* ?OVER_IN_ARG_MIN */
 1167         if (nestinglevel < inargshortname) {
 1168           inargshortname = 0;
 1169         }
 1170         if (nestinglevel < inargexecution) {
 1171           inargexecution = 0;
 1172         }
 1173         if (nestinglevel < inargpjl) {
 1174           inargpjl = 0;
 1175         }
 1176         if (nestinglevel < inevshortname) {
 1177           inevshortname = 0;
 1178         }
 1179         if (nestinglevel < inprinter) /* ?OVER_IN_PRINTER Y */ {
 1180           inprinter = 0;
 1181           if (inconstraint) /* ?IN_CONSTRAINT Y */ {
 1182             /* Make always short printer IDs (w/o "printer/") */
 1183             if (currtagbody[0] == 'p') {
 1184               strcat(cprinter, currtagbody + 8);
 1185             } else {
 1186               strcat(cprinter, currtagbody);
 1187             }
 1188           } /* ?IN_CONSTRAINT */
 1189         } /* ?OVER_IN_PRINTER */
 1190         if (nestinglevel < inmake) /* ?OVER_IN_MAKE Y */ {
 1191           inmake = 0;
 1192           if (inconstraint) strcat(cmake, currtagbody);
 1193         } /* ?OVER_IN_MAKE */
 1194         if (nestinglevel < inmodel) /* ?OVER_IN_MODEL Y */ {
 1195           inmodel = 0;
 1196           if (inconstraint) strcat(cmodel, currtagbody);
 1197         } /* ?OVER_IN_MODEL */
 1198         if (nestinglevel < indriver) /* ?OVER_IN_DRIVER Y */ {
 1199           indriver = 0;
 1200           if (inconstraint) strcat(cdriver, currtagbody);
 1201         } /* ?OVER_IN_DRIVER */
 1202         if (nestinglevel < inargdefault) /* ?OVER_IN_ARG_DEFAULT Y */ {
 1203           inargdefault = 0;
 1204           if (inconstraint) strcat(cargdefault, currtagbody);
 1205         } /* ?OVER_IN_ARG_DEFAULT */
 1206         if (nestinglevel < inconstraint) /* ?OVER_IN_CONSTRAINT Y */ {
 1207           inconstraint = 0;
 1208           /* Constraint completely read, here we evaluate it */
 1209           if (debug) /* ?DEBUG Y */ {
 1210             fprintf(stderr,"    Evaluation of constraint\n");
 1211             fprintf(stderr,"      Values given in constraint:\n");
 1212             fprintf(stderr,"        make: |%s|, model: |%s|, printer: |%s|\n",
 1213                 cmake, cmodel, cprinter);
 1214             fprintf(stderr,"        driver: |%s|, argdefault: |%s|, sense: |%d|\n",
 1215                 cdriver, cargdefault, csense);
 1216             fprintf(stderr,"      Values of current printer/driver combo:\n");
 1217             fprintf(stderr,"        make: |%s|, model: |%s|\n",
 1218                 make, model);
 1219             fprintf(stderr,"        PID: |%s|, driver: |%s|\n",
 1220                 pid, driver);
 1221           } /* ?DEBUG */
 1222           if (!((cmake[0]) || (cmodel[0]) || 
 1223             (cprinter[0]) || (cdriver[0]))) /* ?NULL_CONSTRAINT Y */ {
 1224             fprintf(stderr, "WARNING: Illegal null constraint in %s, line %d!\n",
 1225                 filename, linecount);
 1226           } else  /* ?NULL_CONSTRAINT N */ 
 1227           if (((cmake[0]) || (cmodel[0])) && (cprinter[0])) 
 1228           /* ?PRINTER_AND_MAKE Y */ {
 1229             fprintf(stderr, "WARNING: Both printer id and make/model in constraint in %s, line %d!\n",
 1230                 filename, linecount);
 1231           } else /* ?PRINTER_AND_MAKE N */ {
 1232             if (debug) 
 1233               fprintf(stderr,
 1234                   "      Highest scores for printer: |%d|, driver: |%d|\n",
 1235                   printerhiscore, driverhiscore);
 1236                     /* if make matches, printerscore match grade 1 */
 1237                     /* if model matches, printerscore match grade 2 */
 1238                     /* no information, printerscore = 0 */
 1239             /* mismatch, printerscore = -1 */
 1240             printerscore = 0;
 1241             /* driverscore: -1 = mismatch, 1 = match, 0 = no info */
 1242             driverscore = 0;
 1243             /* The per-printer constraining can happen by poid or by
 1244                a make[/model] pair */
 1245             if (cprinter[0]) /* ?HAVE_PRINTER Y */ {
 1246               if (debug) fprintf(stderr,"        Checking PID\n");
 1247               if (strcmp(translateid(cprinter, idlist), trpid) == 0)
 1248             printerscore = 2;
 1249               else
 1250             printerscore = -1;
 1251             } else /* ?HAVE_PRINTER N */ if (cmake[0]) /* ?HAVE_MAKE Y */ {
 1252               if (debug) fprintf(stderr,"        Checking make\n");
 1253               /* We have a requested make, so it can't be zero.
 1254              You can't request or constraint by model only! */
 1255               if (strcmp(cmake, make) == 0) /* ?GOT_MAKE Y */ {
 1256             printerscore = 1; /* make matches */
 1257             if (cmodel[0]) /* ?HAVE_MODEL Y */ {
 1258               if (debug)
 1259                 fprintf(stderr,"        Checking model\n");
 1260               if (strcmp(cmodel, model) == 0)
 1261                 printerscore = 2; /* model matches, too */
 1262               else
 1263                 printerscore = -1; /* model mismatch */
 1264             } /* ?HAVE_MODEL */
 1265               } else /* ?GOT_MAKE N */ printerscore = -1; /* make mismatch */ 
 1266               /* ?GOT_MAKE */
 1267             }  /* ?HAVE_MAKE  ?HAVE_PRINTER */
 1268             /* Is a driver requested? */
 1269             if (cdriver[0]) /* ?HAVE_DRIVER Y */ {
 1270               if (debug) 
 1271             fprintf(stderr,"        Checking driver\n");
 1272               if ((strcmp(cdriver, driver) == 0) ||
 1273               (strcmp(cdriver + 7, driver) == 0)) 
 1274             driverscore = 1; /* driver matches */
 1275               else
 1276             driverscore = -1; /* driver mismatch */
 1277             } /* ?HAVE_DRIVER */
 1278             if (debug)
 1279               fprintf(stderr,
 1280                   "      Scores for this constraint: printer: |%d|, driver: |%d|\n",
 1281                   printerscore, driverscore);
 1282             /* Now compare the scores with the ones of the currently
 1283                best-matching constraint */
 1284             /* Any sort of match? */
 1285             if (((printerscore > 0) || (driverscore > 0)) &&
 1286             ((printerscore > -1) && (driverscore > -1))) /* ?GOT_MATCH Y */ {
 1287               if (debug) fprintf(stderr,
 1288                      "      Something matches\n");
 1289               /* Does this beat the best match to date? */
 1290               if (((printerscore >= printerhiscore) &&
 1291                (driverscore >= driverhiscore)) ||
 1292               /* They're equal or better in both categories */
 1293               (printerscore == 2)) /* ?BEST_PRINTER_DRIVER Y */ {
 1294               /* A specific printer always wins */
 1295             if (debug)
 1296               fprintf(stderr,"      This constraint wins\n");
 1297             /* Set the high scores */
 1298             if (printerscore > printerhiscore) {
 1299               printerhiscore = printerscore;
 1300             }
 1301             if (driverscore > driverhiscore) {
 1302               driverhiscore = driverscore;
 1303             }
 1304             /* Constraint applies */
 1305             if (inenumval) /* ?IN_ENUM_VAL Y */ {
 1306               /* The winning constraint determines with its
 1307                  sense whether the option/the enum value
 1308                  qualifies for our printer/driver combo */
 1309               enumvalqualified = csense;
 1310               if (debug) 
 1311                 fprintf(stderr,
 1312                     "      Enumeration choice qualifies? %d (0: No, 1: Yes)\n",
 1313                     enumvalqualified);
 1314             } else /* ?IN_ENUM_VAL N */ {
 1315               optionqualified = csense;
 1316               if (debug) 
 1317                 fprintf(stderr,
 1318                     "      Option qualifies? %d (0: No, 1: Yes)\n",
 1319                     optionqualified);
 1320               /* The winning constraint for the option
 1321                  determines the default setting for this
 1322                  option */
 1323               strcpy(argdefault, cargdefault);
 1324             } /* ?IN_ENUM_VAL */
 1325               } /* ?BEST_PRINTER_DRIVER */
 1326             } /* ?GOT_MATCH */
 1327           } /* ?PRINTER_AND_MAKE ?NULL_CONSTRAINT */
 1328         }
 1329         if (nestinglevel < inconstraints) /* ?OVER_IN_CONSTRAINTS Y */ {
 1330           inconstraints = 0;
 1331           /* End of <constraints> block, did the option/the enum
 1332              value qualify for our printer/driver combo? */
 1333           if (inenumval) /* ?IN_ENUM_VAL Y */ {
 1334             if (debug)
 1335               fprintf(stderr,
 1336                   "    This enumeration value finally qualified? %d (0: No, 1: Yes)\n",
 1337                   enumvalqualified);
 1338             if (!enumvalqualified) enumvaltoberemoved = 1;
 1339           } else  /* ?IN_ENUM_VAL N */ {
 1340             if (debug)
 1341               fprintf(stderr,
 1342                   "    This option finally qualified?  %d (0: No, 1: Yes)\n",
 1343                   optionqualified);
 1344             if (!optionqualified) /* ?OPTION_QUALIFIED Y */ {
 1345               /* We have reached the end of the <constraints> block
 1346              for this option, and the option's constraints 
 1347              didn't qualify the option for our printer/driver
 1348              combo =>
 1349              remove this option file from memory and return. */
 1350               free((void *)(*data));
 1351               *data = NULL;
 1352               if (debug)
 1353             fprintf(stderr, "    Option does not apply!\n");
 1354               return +comboconfirmed;
 1355             } /* ?OPTION_QUALIFIED */
 1356           } /* ?IN_ENUM_VAL */
 1357           if (debug)
 1358             fprintf(stderr,
 1359                 "    Constr. for enum. value? %d, enum value disqualified? %d (0: No, 1: Yes)\n",
 1360                 inenumval, enumvaltoberemoved);
 1361           if ((!inenumval) || (!enumvaltoberemoved)) /* ?REMOVE_CONSTRAINTS Y */ {
 1362             /* Remove the read <constraints> block, it will not
 1363                appear in the output, but don't remove it if the
 1364                current enum value will be removed anyway */
 1365             if (lastconstraints != NULL) /* ?LAST_CONSTRAINTS Y */ {
 1366               if (debug)
 1367             fprintf(stderr, "    Removing constraints block\n");
 1368               memmove(lastconstraints, scan + 1, 
 1369                   *data + datalength - scan);
 1370               datalength -= scan + 1 - lastconstraints;
 1371               scan = lastconstraints - 1;
 1372             } else /* ?LAST_CONSTRAINTS N */ {
 1373               if (debug)
 1374             fprintf(stderr, "    This enum value will be removed anyway, so constraints block does not  \n    need to be removed.\n");
 1375             } /* ?LAST_CONSTRAINTS */
 1376           } /* ?REMOVE_CONSTRAINTS */ 
 1377         } /* ?OVER_IN_CONSTRAINTS */
 1378         if (nestinglevel < inenumval) /* ?OVER_IN_ENUM_VAL Y */ {
 1379           inenumval = 0;
 1380           if (debug) 
 1381             fprintf(stderr,
 1382                 "    End of enumeration value block, to be removed? %d (0: No, 1: Yes)\n",
 1383                 enumvaltoberemoved);
 1384           if (enumvaltoberemoved) /* ?ENUM_VAL_TO_BE_REMOVED Y */ {
 1385             /* This enum value does not apply to our printer/driver
 1386                combo, remove it */
 1387             if (lastenumval != NULL) /* ?LAST_ENUM_VAL Y */ {
 1388               if (debug) fprintf(stderr, "    Removing enumeration value\n");
 1389               memmove(lastenumval, scan + 1, 
 1390                   *data + datalength - scan);
 1391               datalength -= scan + 1 - lastenumval;
 1392               scan = lastenumval - 1;
 1393             } else  /* ?LAST_ENUM_VAL N */ {
 1394               fprintf (stderr, "    Cannot remove this evaluation value.\n");
 1395             }  /* ?LAST_ENUM_VAL */
 1396           } else /* ?ENUM_VAL_TO_BE_REMOVED N */ 
 1397             /* This enum value applies to our printer/driver combo */
 1398             numenumvals++;
 1399         } /* ?ENUM_VAL_TO_BE_REMOVED */
 1400         if (nestinglevel < inoption) /* ?OVER_IN_OPTION Y */ {
 1401           inoption = 0;
 1402           if (debug)
 1403             fprintf(stderr,
 1404                 "End of option block:\n      No. of enum. values: %d, qualified by constraints? %d (0: No, 1: Yes)\n",
 1405                 numenumvals, optionqualified);
 1406           if ((!numenumvals) || (!optionqualified)) /* ?NO_VALUES_APPLY Y */ {
 1407             /* We have reached the end of the option file, but there
 1408                        are no enum values which qualified for our combo
 1409                or there were no constraints at all =>
 1410                remove this option file from memory and return. */
 1411             free((void *)(*data));
 1412             *data = NULL;
 1413             if (debug) fprintf (stderr, "    No enum. values, no constraints => Removing option!\n");
 1414             return +comboconfirmed;
 1415           } /* ?NO_VALUES_APPLY */
 1416           /* Insert the line determining the default setting */
 1417           if ((argdefault[0]) || (userdefault)) {
 1418             if (lastoption != NULL) {
 1419               if (debug) 
 1420             fprintf(stderr, 
 1421                 "    Inserting default value\n");
 1422               if (userdefault) {
 1423             /* There is a user-defined default setting */
 1424             if ((+optiontype == +OPTION_TYPE_ENUM)) {
 1425               /* enumerated option */
 1426               if (userdefaultfound) {
 1427                 strcpy(argdefault, userdefaultid);
 1428               }
 1429             } else {
 1430               /* Boolean or numerical option */
 1431                 assert 
 1432 (+optiontype == +OPTION_TYPE_BOOL || +optiontype == +OPTION_TYPE_INT 
 1433               || +optiontype == +OPTION_TYPE_FLOAT);
 1434               strcpy(argdefault, userdefaultvalue);
 1435             }  
 1436               }
 1437               sprintf(defaultline,
 1438                   "\n  <arg_defval>%s</arg_defval>",
 1439                   argdefault);
 1440               defaultlinelength = strlen(defaultline);
 1441               memmove(lastoption + defaultlinelength, lastoption, 
 1442                   *data + datalength - lastoption + 1);
 1443               memmove(lastoption, defaultline, defaultlinelength);
 1444               datalength += defaultlinelength;
 1445               scan += defaultlinelength;
 1446               if (debug) 
 1447             fprintf(stderr,
 1448                 "      Default value line: %s\n",
 1449                 defaultline);
 1450             }
 1451           }
 1452         }
 1453           } else if (+operation == +PARSE_OP_OV_DRIVER) 
 1454           { /* Driver XML file (Overview) */
 1455         if (nestinglevel < indriver) indriver = 0;
 1456         if (nestinglevel < inprinters) /* ?OVER_IN_PRINTERS Y */ {
 1457           inprinters = 0;
 1458           /* Remove the whole <printers> block */
 1459           if (lastprinters != NULL) /* ?LAST_PRINTERS Y */ {
 1460             if (debug) 
 1461               fprintf(stderr, "    Removing <printers> block\n");
 1462             memmove(lastprinters, scan + 1, 
 1463                 *data + datalength - scan);
 1464             datalength -= scan + 1 - lastprinters;
 1465             scan = lastprinters - 1;
 1466           } /* ?LAST_PRINTERS */
 1467         } /* ?OVER_IN_PRINTERS */
 1468         if (nestinglevel < incomments) /* ?OVER_IN_COMMENTS Y */ {
 1469           incomments = 0;
 1470           /* Remove the whole <comments> block */
 1471           if ((lastcomments != NULL) && (!inprinter)) 
 1472           /* ?COMMENTS_NOT_IN_PRINTER Y */ {
 1473             if (debug) 
 1474               fprintf(stderr, "    Removing <comments> block\n");
 1475             memmove(lastcomments, scan + 1, 
 1476                 *data + datalength - scan);
 1477             datalength -= scan + 1 - lastcomments;
 1478             scan = lastcomments - 1;
 1479           } /* ?COMMENTS_NOT_IN_PRINTER */
 1480         } /* ?OVER_IN_COMMENTS */
 1481         if (nestinglevel < inexecution) inexecution = 0;
 1482         if (nestinglevel < inid) /* ?OVER_IN_ID Y */ {
 1483           inid = 0;
 1484           /* Get the short printer ID (w/o "printer/") */
 1485           strcpy(cprinter, currtagbody + 8);
 1486           strcpy(cprinter, translateid(cprinter, idlist));
 1487           if (debug)
 1488             fprintf(stderr,
 1489                 "    Overview: Printer: %s Driver: %s\n",
 1490                 cprinter, cdriver);
 1491         } /* ?OVER_IN_ID */
 1492         if (nestinglevel < indfunctionality) 
 1493         /* ?OVER_IN_D_FUNCTIONALITY Y */ {
 1494           indfunctionality = 0;
 1495           /* Save the functionality entry in a buffer to insert it
 1496              into the overview */
 1497           if (lastdfunctionality != NULL) /* ?LAST_D_FUNCTIONALITY Y */ {
 1498             if (debug) fprintf(stderr,
 1499                        "    Saving <functionality> entry\n");
 1500             memmove(dfunctionalityentry, lastdfunctionality,
 1501                 scan + 1 - lastdfunctionality);
 1502             dfunctionalityentry[scan + 1 - lastdfunctionality] = '\0';
 1503             if (debug) fprintf(stderr,
 1504                        "    <functionality> entry: %s\n",
 1505                        dfunctionalityentry);
 1506           } /* ?LAST_D_FUNCTIONALITY */
 1507         }/* ?OVER_IN_D_FUNCTIONALITY */
 1508         if (nestinglevel < inprinter) /* ?OVER_IN_PRINTER Y */ {
 1509           inprinter = 0;
 1510           if (debug)
 1511             fprintf(stderr,
 1512                 "    Overview: Add driver %s to printer %s (%s)\n",
 1513                 cdriver, cprinter, dfunctionalityentry);
 1514           /* Add this driver to the current printer's entry in the
 1515              printer list, create the printer entry if necessary */
 1516           plistpointer = *printerlist;
 1517           plistpreventry = NULL;
 1518           /* Search printer in list */
 1519           while ((plistpointer != NULL) &&
 1520              (strcmp(plistpointer->id, cprinter) != 0)) 
 1521              /* @FIND_PRINTER */ {
 1522             plistpreventry = plistpointer;
 1523             plistpointer = (printerlist_t *)(plistpointer->next);
 1524           } /* @FIND_PRINTER X */ 
 1525           if (plistpointer == NULL) /* ?PRINTER_NOT_FOUND Y */ {
 1526             /* printer not found, create new entry */
 1527             plistpointer = 
 1528               (printerlist_t *)malloc(sizeof(printerlist_t));
 1529             strcpy(plistpointer->id, cprinter);
 1530             plistpointer->drivers = NULL;
 1531             plistpointer->next = NULL;
 1532             if (plistpreventry != NULL)
 1533               plistpreventry->next = 
 1534             (struct printerlist_t *)plistpointer;
 1535             else 
 1536               *printerlist = plistpointer;
 1537           } /* ?PRINTER_NOT_FOUND */
 1538           /* Add driver entry */
 1539           dlistpointer = plistpointer->drivers;
 1540           dlistpreventry = NULL;
 1541           while (dlistpointer != 0) /* @D_LIST_POINTER */ {
 1542             dlistpreventry = dlistpointer;
 1543             dlistpointer = (driverlist_t *)(dlistpointer->next);
 1544           } /* @D_LIST_POINTER X */
 1545           dlistpointer = 
 1546             (driverlist_t *)malloc(sizeof(driverlist_t));
 1547           strcpy(dlistpointer->name, cdriver);
 1548           if ((dfunctionalityentry != NULL) &&
 1549               (dfunctionalityentry[0]))
 1550             dlistpointer->functionality = strdup(dfunctionalityentry);
 1551           else dlistpointer->functionality = NULL;
 1552           dlistpointer->next = NULL;
 1553           if (dlistpreventry != NULL)
 1554             dlistpreventry->next =
 1555               (struct driverlist_t *)dlistpointer;
 1556           else 
 1557             plistpointer->drivers = dlistpointer;
 1558         } /* ?OVER_IN_PRINTER */
 1559         if (nestinglevel < inprototype) /* ?OVER_IN_PROTOTYPE Y */ {
 1560           inprototype = 0;
 1561           if (pid) /* ?PID Y */ 
 1562           { /* We abuse pid here to tell that we want
 1563                 to have an overview of available PPDs and
 1564                 not of all possible printer/driver combos.
 1565                     pid is never used for a printer ID when 
 1566                     building the overview XML file. */
 1567             /* Get the command line prototype without leading 
 1568                        white space, is empty on empty command line*/
 1569             for (s = currtagbody;
 1570              (*s != '\0') && (strchr(" \n\r\t", *s) != NULL);
 1571              s ++);
 1572             if (debug)
 1573               fprintf(stderr,
 1574                   "    Overview: Driver: %s Command line: |%s|\n",
 1575                   cdriver, s);
 1576             if (*s != '\0') /* ?FOUND_COMMAND_LINE_PROTOTYPE Y */ {
 1577               /* We have found a non-empty command line prototype, so
 1578              this driver produces PPD files */
 1579               driverhasproto = 1;
 1580               /* Add the driver to the first entry in the printer
 1581              list, the pseudo printer "proto" */
 1582               plistpointer = *printerlist;
 1583               plistpreventry = NULL;
 1584               dlistpointer = plistpointer->drivers;
 1585               dlistpreventry = NULL;
 1586               while ((dlistpointer != 0) &&
 1587 (strcasecmp(dlistpointer->name, cdriver))) /* @FIND_DRIVER */ {
 1588             dlistpreventry = dlistpointer;
 1589             dlistpointer = (driverlist_t *)(dlistpointer->next);
 1590               } /* @FIND_DRIVER X */
 1591               if (dlistpointer == 0) /* ?DRIVER_NOT_FOUND Y */ {
 1592             dlistpointer = 
 1593               (driverlist_t *)malloc(sizeof(driverlist_t));
 1594             strcpy(dlistpointer->name, cdriver);
 1595             dlistpointer->functionality = NULL;
 1596             dlistpointer->next = NULL;
 1597             if (dlistpreventry != NULL)
 1598               dlistpreventry->next =
 1599                 (struct driverlist_t *)dlistpointer;
 1600             else 
 1601               plistpointer->drivers = dlistpointer;
 1602               } /* ?DRIVER_NOT_FOUND */
 1603             } else /* ?FOUND_COMMAND_LINE_PROTOTYPE N */ {
 1604               /* We have found an empty command line prototype, so
 1605              this driver does not produce any PPD file, */
 1606               /* Renove the driver XML data from memory */
 1607               free((void *)(*data));
 1608               *data = NULL;
 1609               if (debug)
 1610             fprintf(stderr, "    Driver entry does not produce PPDs!\n");
 1611               return +comboconfirmed;
 1612             } /* ?FOUND_COMMAND_LINE_PROTOTYPE */
 1613           }
 1614           /* Remove the whole <prototype> block */
 1615           if (lastprototype != NULL) {
 1616             if (debug) 
 1617               fprintf(stderr, "    Removing <prototype> block\n");
 1618             memmove(lastprototype, scan + 1, 
 1619                 *data + datalength - scan);
 1620             datalength -= scan + 1 - lastprototype;
 1621             scan = lastprototype - 1;
 1622           }
 1623         }
 1624           } else if (operation == 4) { /* Printer XML file (Overview) */
 1625         if (debug)
 1626           fprintf(stderr,
 1627               "    Printer (Overview), tag name: %s, tag body: %s\n",
 1628               currtagname, currtagbody);
 1629         if (nestinglevel < inprinter) inprinter = 0;
 1630         if (nestinglevel < inmake) {
 1631           inmake = 0;
 1632           /* Only the <make> outside the <autodetect> tag is valid. */
 1633           if (!inautodetect) strcpy(cmake, currtagbody);
 1634         }
 1635         if (nestinglevel < inmodel) {
 1636           inmodel = 0;
 1637           /* Only the <model> outside the <autodetect> tag is valid. */
 1638           if (!inautodetect) strcpy(cmodel, currtagbody);
 1639         }
 1640         if (nestinglevel < infunctionality) {
 1641           infunctionality = 0;
 1642           strcpy(cfunctionality, currtagbody);
 1643         }
 1644         if (nestinglevel < inunverified) inunverified = 0;
 1645         if (nestinglevel < indrivers) indrivers = 0;
 1646         if (nestinglevel < inlang) inlang = 0;
 1647         if ((nestinglevel < indriver) ||
 1648             (nestinglevel < inpostscript)) {
 1649           if (nestinglevel < indriver) indriver = 0;
 1650           if (nestinglevel < inpostscript) inpostscript = 0;
 1651           if (indrivers || inlang) {
 1652             if (debug) fprintf(stderr, 
 1653                        "    Printer/Driver/PPD: %s %s %s\n",
 1654                        cprinter, cid, cppd);
 1655             driverhasproto = 0;
 1656             if ((cid[0] != '\0') && (pid)) {
 1657               /* Check if our driver has a command line prototype,
 1658              it should be driver of the pseudo-printer
 1659              "proto" (first item in the printer list) then */
 1660               plistpointer = *printerlist;
 1661               plistpreventry = NULL;
 1662               dlistpointer = plistpointer->drivers;
 1663               dlistpreventry = NULL;
 1664               while ((dlistpointer != 0) &&
 1665                  (strcasecmp(dlistpointer->name, cid))) {
 1666             dlistpreventry = dlistpointer;
 1667             dlistpointer = (driverlist_t *)(dlistpointer->next);
 1668               }
 1669               if (dlistpointer != 0) {
 1670             driverhasproto = 1;
 1671               }
 1672             }
 1673             if (debug)
 1674               fprintf(stderr,
 1675                 "    Overview: Printer: %s Driver: %s Output mode: %s Driver has prototype: %d PPD: %s\n",
 1676                 cprinter, cid, pid, driverhasproto, cppd);
 1677             if (cid[0] != '\0') { 
 1678               if ((!pid) || /* We want to see all combos, not only
 1679                        the ones which provide a PPD file
 1680                        If pid is set, we want only combos
 1681                        which provide PPDs and if we do
 1682                        not have a ready-made PPD we must */
 1683               (driverhasproto &&
 1684                ((cppd[0] == '\0') || (pid[0] == 'C'))) ||
 1685                   /* have a command line prototype, but do
 1686                  not show if there is also a ready-made PPD
 1687                      but we do not want to show ready-made PPDs */
 1688               ((cppd[0] != '\0') && (pid[0] == 'C')))  { 
 1689                  /* or a ready-made PPD file AND CUPS should
 1690                 show entries for ready-made PPD files in
 1691                 the Foomatic database */
 1692             /* Add this driver to the current printer's entry in 
 1693                the printer list, create the printer entry if
 1694                necessary */
 1695             if (debug)
 1696               fprintf(stderr,
 1697                   "    Overview: Printer: %s Driver: %s: Adding driver to list\n",
 1698                   cprinter, cid);
 1699             plistpointer = *printerlist;
 1700             plistpreventry = NULL;
 1701             /* Search printer in list */
 1702             while ((plistpointer != NULL) &&
 1703                    (strcmp(plistpointer->id, cprinter) != 0)) {
 1704               plistpreventry = plistpointer;
 1705               plistpointer = (printerlist_t *)(plistpointer->next);
 1706             }
 1707             if (plistpointer == NULL) {
 1708               /* printer not found, create new entry */
 1709               plistpointer = 
 1710                 (printerlist_t *)malloc(sizeof(printerlist_t));
 1711               strcpy(plistpointer->id, cprinter);
 1712               plistpointer->drivers = NULL;
 1713               plistpointer->next = NULL;
 1714               if (plistpreventry != NULL)
 1715                 plistpreventry->next = 
 1716                   (struct printerlist_t *)plistpointer;
 1717               else 
 1718                 *printerlist = plistpointer;
 1719             }
 1720             /* Add driver entry */
 1721             dlistpointer = plistpointer->drivers;
 1722             dlistpreventry = NULL;
 1723             while ((dlistpointer != 0) &&
 1724                    (strcasecmp(dlistpointer->name, cid))) {
 1725               dlistpreventry = dlistpointer;
 1726               dlistpointer = (driverlist_t *)(dlistpointer->next);
 1727             }
 1728             if (dlistpointer == 0) {
 1729               dlistpointer = 
 1730                 (driverlist_t *)malloc(sizeof(driverlist_t));
 1731               strcpy(dlistpointer->name, cid);
 1732               dlistpointer->functionality = NULL;
 1733               dlistpointer->next = NULL;
 1734               if (dlistpreventry != NULL)
 1735                 dlistpreventry->next =
 1736                   (struct driverlist_t *)dlistpointer;
 1737               else 
 1738                 plistpointer->drivers = dlistpointer;
 1739             if (debug)
 1740               fprintf(stderr,
 1741                   "    Overview: Driver successfully added to list.\n");
 1742             }
 1743               } else {
 1744             /* To suppress the printer/driver combo from the
 1745                output list we need to delete the appropriate
 1746                driver entry */
 1747             if (debug)
 1748               fprintf(stderr,
 1749                   "    Overview: Printer: %s Driver: %s: Removing driver from list\n",
 1750                   cprinter, cid);
 1751             plistpointer = *printerlist;
 1752             plistpreventry = NULL;
 1753             /* Search printer in list */
 1754             while ((plistpointer != NULL) &&
 1755                    (strcmp(plistpointer->id, cprinter) != 0)) {
 1756               plistpreventry = plistpointer;
 1757               plistpointer =
 1758                 (printerlist_t *)(plistpointer->next);
 1759             }
 1760             /* If the printer is there, search for the driver */
 1761             if (plistpointer != NULL) {
 1762               dlistpointer = plistpointer->drivers;
 1763               dlistpreventry = NULL;
 1764               while ((dlistpointer != NULL) &&
 1765                  (strcasecmp(dlistpointer->name, cid))) {
 1766                 dlistpreventry = dlistpointer;
 1767                 dlistpointer =
 1768                   (driverlist_t *)(dlistpointer->next);
 1769               }
 1770               if (dlistpointer != NULL) {
 1771                 dlistnextentry =
 1772                   (driverlist_t *)(dlistpointer->next);
 1773                 free(dlistpointer);
 1774                 if (dlistpreventry != NULL)
 1775                   dlistpreventry->next = 
 1776                 (struct driverlist_t *)dlistnextentry;
 1777                 else
 1778                   plistpointer->drivers = dlistnextentry;
 1779                 if (debug)
 1780                   fprintf(stderr,
 1781                       "    Overview: Driver successfully removed from list.\n");
 1782               }
 1783             }
 1784               }
 1785               if (cppd[0] != '\0') {
 1786             if ((pid == NULL) || (pid[0] == 'C')) {
 1787               /* CUPS should also show entries for ready-made 
 1788                  PPD files in the Foomatic database */
 1789               if (debug)
 1790                 fprintf(stderr, 
 1791                     "    Adding Driver/PPD to list.\n");
 1792               ppdlistpointer = ppdlist;
 1793               ppdlistpreventry = NULL;
 1794               if (debug)
 1795                 fprintf(stderr,
 1796                     "    Going through list: ");
 1797               while (ppdlistpointer != NULL) {
 1798                 ppdlistpreventry = ppdlistpointer;
 1799                 ppdlistpointer = (ppdlist_t *)ppdlistpointer->next;
 1800                 if (debug)
 1801                   fprintf(stderr,
 1802                       ".");
 1803               }
 1804               ppdlistpointer = 
 1805                 (ppdlist_t *)malloc(sizeof(ppdlist_t));
 1806               strcpy(ppdlistpointer->driver, cid);
 1807               strcpy(ppdlistpointer->ppd, cppd);
 1808               ppdlistpointer->next = NULL;
 1809               if (ppdlistpreventry != NULL)
 1810                 ppdlistpreventry->next =
 1811                   (struct ppdlist_t *)ppdlistpointer;
 1812               else 
 1813                 ppdlist = ppdlistpointer;
 1814               if (debug)
 1815                 fprintf(stderr,
 1816                     " Driver/PPD in list: %s %s\n",
 1817                     ppdlistpointer->driver,
 1818                     ppdlistpointer->ppd);
 1819             }
 1820               }
 1821             }
 1822           } else strcpy(cdriver, currtagbody);
 1823         }
 1824         if (nestinglevel < inid) {
 1825           inid = 0;
 1826           strcpy(cid, currtagbody);
 1827           if (debug) fprintf(stderr, 
 1828                      "    Driver ID for PPD: %s\n", cid);
 1829         }
 1830         if (nestinglevel < inppd) {
 1831           inppd = 0;
 1832           /* Get the PPD file location without leading 
 1833              white space, is empty on empty PPD file location*/
 1834           for (s = currtagbody;
 1835                (*s != '\0') && (strchr(" \n\r\t", *s) != NULL);
 1836                s ++);
 1837           strcpy(cppd, s);
 1838           if (debug) fprintf(stderr, 
 1839                      "    PPD URL: %s\n", cppd);
 1840         }
 1841         if (nestinglevel < inautodetect) {
 1842           inautodetect = 0;
 1843           /* Save the autodetect entry in a buffer to insert it
 1844              into the overview */
 1845           if (lastautodetect != NULL) {
 1846             if (debug) fprintf(stderr,
 1847                        "    Saving <autodetect> entry\n");
 1848             memmove(cautodetectentry, lastautodetect,
 1849                 scan + 1 - lastautodetect);
 1850             cautodetectentry[scan + 1 - lastautodetect] = '\0';
 1851             if (debug) fprintf(stderr,
 1852                        "    <autodetect> entry: %s\n",
 1853                        cautodetectentry);
 1854           }
 1855         }
 1856           }
 1857           lasttagend = scan;
 1858           if (debug) fprintf(stderr,
 1859                  "    XML tag nesting level: %d\n",
 1860                  nestinglevel);
 1861         }
 1862       }
 1863     } else {
 1864       if ((*scan == '>') && 0) { /* tag end without beginning */
 1865         fprintf(stderr, "XML error: '>' without '<' %s, line %d!\n",
 1866             filename, linecount);
 1867         exit(1);
 1868       }
 1869     }
 1870       }
 1871       break;
 1872     default:
 1873       /* other characters */
 1874       if ((intag) && (!incomment)) {
 1875     if (*scan == '\'') insinglequotes = 1 - insinglequotes;
 1876     if (*scan == '\"') indoublequotes = 1 - indoublequotes;
 1877     if ((insinglequotes) || (indoublequotes)) inquotes = 1;
 1878     else inquotes = 0; 
 1879     if (!incomment) {
 1880       if (!intagword) {
 1881         intagword = 1;
 1882         tagwordstart = scan;
 1883       }
 1884     }
 1885       }
 1886     }
 1887   }
 1888   if (debug) fprintf(stderr, 
 1889              "    XML tag nesting level: %d\n",nestinglevel);
 1890   if (debug) fprintf(stderr, "    Lines of input: %d\n", linecount);
 1891   switch (+operation) /* *OPERATION */
 1892   { 
 1893       case /* *OPERATION */ (+PARSE_OP_PRINTER): { /* Printer XML file */
 1894     if ((make[0] == 0) || (model[0] == 0)) /* ?NO_MODEL Y */ {
 1895       /* <make> or <model> tag not found */
 1896       fprintf(stderr, "Could not determine manufacturer or model name from the printer file %s!\n",
 1897           filename);
 1898       exit(1);
 1899     } /* ?NO_MODEL */
 1900     if (debug) fprintf(stderr, "    Driver in printer's driver list: %d\n", comboconfirmed); 
 1901   } break; 
 1902   case /* *OPERATION */ (+PARSE_OP_DRIVER): { /* Driver XML file */
 1903     if (debug) 
 1904       fprintf(stderr,
 1905           "    nopjl: %d (1: driver does not allow PJL options)\n",
 1906           *nopjl);
 1907     if (printerentryfound != 0) /* ?PRINTER_ENTRY_FOUND Y */ 
 1908     { /* the printer is in the listing of the 
 1909                      driver */
 1910       comboconfirmed = 1;
 1911     } /* ?PRINTER_ENTRY_FOUND */ 
 1912     if (debug) fprintf(stderr, "    Printer in driver's printer list: %d\n", comboconfirmed); 
 1913   } break; 
 1914   case /* *OPERATION */ (+PARSE_OP_OPTION): { /* Option XML file */
 1915     if (debug) fprintf(stderr, "    Resulting option XML:\n%s\n", *data); 
 1916   } break;
 1917   case /* *OPERATION */ (+PARSE_OP_OV_DRIVER): 
 1918   { /* Driver XML file (Overview) */
 1919     if (pid && (driverhasproto == 0)) /* ?PID_NO_DRIVER Y */ {
 1920       /* We did not find a command line prototype, so
 1921      this driver does not produce any PPD file, */
 1922       /* As we want to list only/printer/driver combos which produce
 1923      a PPD file, renove the driver XML data from memory */
 1924       free((void *)(*data));
 1925       *data = NULL;
 1926       /* Delete all occurences of the driver in the
 1927      printer/driver combo list */
 1928       plistpointer = *printerlist;
 1929       plistpreventry = NULL;
 1930       /* Go through all printers in the list */
 1931       while (plistpointer != NULL) /* @P_LIST_POINTER */ {
 1932     /* Search for the driver */
 1933     dlistpointer = plistpointer->drivers;
 1934     dlistpreventry = NULL;
 1935     while 
 1936     ((dlistpointer != NULL) && (strcasecmp(dlistpointer->name, cdriver))) 
 1937     /* @FIND_DRIVER */ {
 1938       dlistpreventry = dlistpointer;
 1939       dlistpointer =
 1940         (driverlist_t *)(dlistpointer->next);
 1941     } /* @FIND_DRIVER X */
 1942     if (dlistpointer != NULL) /* ?D_LIST_POINTER Y */ {
 1943       /* Delete it */
 1944       dlistnextentry =
 1945         (driverlist_t *)(dlistpointer->next);
 1946       free(dlistpointer);
 1947       if (dlistpreventry != NULL)
 1948         dlistpreventry->next = 
 1949           (struct driverlist_t *)dlistnextentry;
 1950       else
 1951         plistpointer->drivers = dlistnextentry;
 1952     } /* ?D_LIST_POINTER */
 1953     /* Next printer */
 1954     plistpreventry = plistpointer;
 1955     plistpointer =
 1956       (printerlist_t *)(plistpointer->next);
 1957       } /* @P_LIST_POINTER X */
 1958       if (debug)
 1959     fprintf(stderr, "    Driver entry does not produce PPDs!\n");
 1960     } /* ?PID_NO_DRIVER */ 
 1961   } break; 
 1962   case /* *OPERATION */ (+PARSE_OP_OV_PRINTER):
 1963   { /* Printer XML file (Overview) */
 1964     /* Remove the printer input data */
 1965     **data = '\0';
 1966     /* Build the printer entry for the overview in the memory which was used
 1967        for the former input data, the overview entry is always shorter than
 1968        the original printer XML file. */
 1969     if (debug) fprintf(stderr, "    Data for this printer entry in the overview:\n      Printer ID: |%s|\n      Make: |%s|\n      Model: |%s|\n      Functionality: |%s|\n      Rec. driver: |%s|\n      Auto detect entry: |%s|\n",
 1970         cprinter, cmake, cmodel, cfunctionality, cdriver,cautodetectentry);
 1971     if ((cprinter[0]) && (cmake[0]) && (cmodel[0]) && (cfunctionality[0])) 
 1972     /* ?PRINTER_ENTRY Y */ {
 1973       strcpy(cprinter, translateid(cprinter, idlist));
 1974       strcat((char *)(*data), "  <printer>\n    <id>");
 1975       strcat((char *)(*data), cprinter);
 1976       strcat((char *)(*data), "</id>\n    <make>");
 1977       strcat((char *)(*data), cmake);
 1978       strcat((char *)(*data), "</make>\n    <model>");
 1979       strcat((char *)(*data), cmodel);
 1980       strcat((char *)(*data), "</model>\n    <functionality>");
 1981       strcat((char *)(*data), cfunctionality);
 1982       strcat((char *)(*data), "</functionality>\n");
 1983       if (cunverified) /* ?UNVERIFIED Y */ {
 1984     strcat((char *)(*data), "    <unverified>");
 1985     strcat((char *)(*data), cfunctionality);
 1986     strcat((char *)(*data), "</unverified>\n");
 1987       } /* ?UNVERIFIED */
 1988       if (cdriver[0]) /* ?DRIVER Y */ {
 1989     strcat((char *)(*data), "    <driver>");
 1990     strcat((char *)(*data), cdriver);
 1991     strcat((char *)(*data), "</driver>\n");
 1992       } /* ?DRIVER */
 1993       if (cautodetectentry[0]) /* ?AUTO_DETECT_ENTRY Y */ {
 1994     strcat((char *)(*data), "    ");
 1995     strcat((char *)(*data), cautodetectentry);
 1996       } /* ?AUTO_DETECT_ENTRY */ 
 1997       plistpointer = *printerlist;
 1998       plistpreventry = NULL;
 1999       while ((plistpointer) &&
 2000              (strcmp(plistpointer->id, cprinter) != 0)) /* @LOCATE_CPRINTER */ {
 2001     plistpreventry = plistpointer;
 2002     plistpointer = (printerlist_t *)(plistpointer->next);
 2003       } /* @LOCATE_CPRINTER X */
 2004       if (plistpointer) /* ?P_LIST_POINTER Y */ {
 2005     strcat((char *)(*data), "\n    <drivers>\n");
 2006     dlistpointer = plistpointer->drivers;
 2007     exceptionfound = 0;
 2008     while (dlistpointer) /* @D_LIST_POINTER */ {
 2009       strcat((char *)(*data), "      <driver>");
 2010       strcat((char *)(*data), dlistpointer->name);
 2011       strcat((char *)(*data), "</driver>\n");
 2012       if (dlistpointer->functionality != NULL) exceptionfound = 1;
 2013       dlistpointer = (driverlist_t *)(dlistpointer->next);
 2014     } /* @D_LIST_POINTER X */
 2015     strcat((char *)(*data), "    </drivers>\n");
 2016     if (exceptionfound) /* ?EXCEPTION_FOUND Y */ {
 2017       strcat((char *)(*data), "    <driverfunctionalityexceptions>\n");
 2018       dlistpointer = plistpointer->drivers;
 2019       while (dlistpointer) /* @D_LIST_POINTER */ { 
 2020         if ((dlistpointer->name != NULL) &&
 2021         (dlistpointer->functionality != NULL)) /* ?D_LIST_PTR_FUNCT Y */ {
 2022           strcat((char *)(*data),
 2023              "      <driverfunctionalityexception>\n");
 2024           strcat((char *)(*data), "        <driver>");
 2025           strcat((char *)(*data), dlistpointer->name);
 2026           strcat((char *)(*data), "</driver>\n");
 2027           strcat((char *)(*data), dlistpointer->functionality);
 2028           strcat((char *)(*data),
 2029              "\n      </driverfunctionalityexception>\n");
 2030         } /* ?D_LIST_PTR_FUNCT */
 2031         dlistpointer = (driverlist_t *)(dlistpointer->next);
 2032       } /* @D_LIST_POINTER X */
 2033       strcat((char *)(*data), "    </driverfunctionalityexceptions>\n");
 2034     } /* ?EXCEPTION_FOUND */
 2035     /* We remove every printer entry in the list for which we have found
 2036        a printer XML file in the database, so all remaining entries are
 2037        of printers which are only mentioned in a driver's printer list
 2038        but do not have an XML file in the database. We will treat these
 2039        printers later */
 2040     dlistpointer = plistpointer->drivers;
 2041     while (dlistpointer) /* @D_LIST_POINTER */ {
 2042       dlistpreventry = dlistpointer;
 2043       dlistpointer = (driverlist_t *)(dlistpointer->next);
 2044       free(dlistpreventry);
 2045     } /* @D_LIST_POINTER X */
 2046     if (plistpreventry == NULL)
 2047       *printerlist = (printerlist_t *)(plistpointer->next);
 2048     else
 2049       plistpreventry->next = plistpointer->next;
 2050     free(plistpointer);
 2051       } /* ?P_LIST_POINTER */ 
 2052       if (ppdlist != NULL) /* ?PPD_LIST Y */ {
 2053     strcat((char *)(*data), "    <ppds>\n");
 2054     ppdlistpointer = ppdlist;
 2055     if (debug)
 2056       fprintf(stderr,
 2057           "    Going through list: ");
 2058     while (ppdlistpointer) /* @PPD_LIST_POINTER */ {
 2059     if (debug)
 2060       fprintf(stderr,
 2061           ".");
 2062       strcat((char *)(*data), "      <ppd>\n");
 2063       strcat((char *)(*data), "        <driver>");
 2064       strcat((char *)(*data), ppdlistpointer->driver);
 2065       strcat((char *)(*data), "</driver>\n        <ppdfile>");
 2066       strcat((char *)(*data), ppdlistpointer->ppd);
 2067       strcat((char *)(*data), "</ppdfile>\n");
 2068       strcat((char *)(*data), "      </ppd>\n");
 2069       ppdlistpointer = (ppdlist_t *)(ppdlistpointer->next);
 2070     } /* @PPD_LIST_POINTER X */
 2071     strcat((char *)(*data), "    </ppds>\n");
 2072       } /* ?PPD_LIST */
 2073       strcat((char *)(*data), "  </printer>\n");
 2074     } /* ?PRINTER_ENTRY */ 
 2075   } break; } /* *OPERATION X */
 2076   return(comboconfirmed);
 2077 }
 2078 
 2079 /*
 2080  *  Main function
 2081  */
 2082 
 2083 int                 /* O - Exit status of the program */
 2084 main(int  argc,     /* I - Number of command-line arguments */
 2085      char *argv[])  /* I - Command-line arguments */
 2086 {
 2087   int       i;      /* Looping vars */
 2088   char          *t;
 2089 
 2090   const char    *pid = NULL,
 2091                 *driver = NULL,
 2092                 *setting = NULL;/* User-supplied data */
 2093   const char    *make, *model;  /* For constraints */
 2094   const char    *libdir = NULL; /* Database location */
 2095   char          printerfilename[1024];/* Name of printer's XML file */
 2096   char          printerdirname[1024]; /* Name of the directory with the XML
 2097                      files for the printers */
 2098   char          driverfilename[1024]; /* Name of driver's XML file */
 2099   char          driverdirname[1024];  /* Name of the directory with the XML
 2100                      files for the drivers */
 2101   char          optionfilename[1024]; /* Name of current option XML file*/
 2102   char          optiondirname[1024];  /* Name of the directory with the XML
 2103                      files for the options */
 2104   char          oldidfilename[1024];  /* Name of the file with the
 2105                      translation table for old printer
 2106                      IDs */
 2107   char          *printerbuffer = NULL;
 2108   char          *driverbuffer = NULL;
 2109   char          **optbuffers = NULL;
 2110   int           num_optbuffers = 0;
 2111   char          **defaultsettings = NULL; /* User-supplied option settings*/
 2112   int           num_defaultsettings = 0;
 2113   int           overview = 0;
 2114   int           noreadymadeppds = 0;
 2115   int           nopjl = 0;
 2116   int           debug = 0;
 2117   int           debug2 = 0;
 2118   int           comboconfirmed = 0;
 2119   int           comboconfirmed2 = 0;
 2120   int           exceptionfound = 0;
 2121   DIR           *optiondir;
 2122   DIR           *driverdir;
 2123   DIR           *printerdir;
 2124   struct dirent *direntry;
 2125   printerlist_t *printerlist = NULL;
 2126   printerlist_t *plistpointer;  /* pointers to navigate through the 
 2127                    printer */
 2128   driverlist_t  *dlistpointer;  /* list for the overview */
 2129   printerlist_t *plistpreventry;
 2130   driverlist_t  *dlistpreventry;
 2131   idlist_t      *idlist;        /* I - ID translation table */
 2132   
 2133   /* Show the help message whem no command line arguments are given */
 2134 
 2135   if (argc < 2) {
 2136     fprintf(stderr, "Usage: foomatic-combo-xml [ -O ] [ -p printer -d driver ]\n                          [ -o option1=setting1 ] [ -o option2 ] [ -l dir ]\n                          [ -v | -vv ]\n");
 2137     fprintf(stderr, "\n");
 2138     fprintf(stderr, "   -p printer   Foomatic ID of the printer\n");
 2139     fprintf(stderr, "   -d driver    Name of the driver to use\n");
 2140     fprintf(stderr, "   -o option1=setting1\n");
 2141     fprintf(stderr, "   -o option2   Default option settings for the\n");
 2142     fprintf(stderr, "                generated file\n");
 2143     fprintf(stderr, "   -O           Generate overview XML file\n");
 2144     fprintf(stderr, "   -C           Generate overview XML file only\n");
 2145     fprintf(stderr, "                containing combos leading to a valid\n");
 2146     fprintf(stderr, "                PPD file (for CUPS PPD list)\n");
 2147     fprintf(stderr, "   -n           (used only with \"-C\") suppress the\n");
 2148     fprintf(stderr, "                printer/driver combos which point to \n");
 2149     fprintf(stderr, "                ready-made PPD file (CUPS usually \n");
 2150     fprintf(stderr, "                lists ready-made PPD files directly).\n");
 2151     fprintf(stderr, "   -l dir       Directory where the Foomatic database is located\n");
 2152     fprintf(stderr, "   -v           Verbose (debug) mode\n");
 2153     fprintf(stderr, "   -vv          Very Verbose (debug) mode\n");
 2154     fprintf(stderr, "\n");
 2155     exit(1);
 2156   }
 2157 
 2158   /* Read the command line arguments */
 2159   for (i = 1; i < argc; i ++) {
 2160     if (argv[i][0] == '-') {
 2161       switch (argv[i][1]) {
 2162         case 'P' :
 2163     case 'p' : /* printer */
 2164         if (argv[i][2] != '\0')
 2165           pid = argv[i] + 2;
 2166         else {
 2167           i ++;
 2168           pid = argv[i];
 2169         }
 2170         break;
 2171     case 'd' : /* driver */
 2172         if (argv[i][2] != '\0')
 2173           driver = argv[i] + 2;
 2174         else {
 2175           i ++;
 2176           driver = argv[i];
 2177         }
 2178         break;
 2179     case 'o' : /* option setting */
 2180         if (argv[i][2] != '\0')
 2181           setting = argv[i] + 2;
 2182         else {
 2183           i ++;
 2184           setting = argv[i];
 2185         }
 2186         num_defaultsettings ++;
 2187         defaultsettings = (char **)realloc((char **)defaultsettings, 
 2188                       sizeof(char *) * num_defaultsettings);
 2189         defaultsettings[num_defaultsettings-1] = strdup(setting);
 2190         break;
 2191     case 'O' : /* Overview */
 2192         overview = 1;
 2193         break;
 2194     case 'C' : /* Overview for CUPS PPD list */
 2195         overview = 2;
 2196         break;
 2197     case 'n' : /* suppress ready-made PPDs in overview for CUPS PPD list */
 2198         noreadymadeppds = 1;
 2199         break;
 2200         case 'l' : /* libdir */
 2201         if (argv[i][2] != '\0')
 2202           libdir = argv[i] + 2;
 2203         else {
 2204           i ++;
 2205           libdir = argv[i];
 2206         }
 2207         break;
 2208         case 'v' : /* verbose */
 2209         debug++;
 2210         if (argv[i][2] == 'v') debug++;
 2211         break;
 2212     default :
 2213         fprintf(stderr, "Unknown option \'%c\'!", argv[i][1]);
 2214             exit(1);
 2215       }
 2216     } else {
 2217       fprintf(stderr, "Unknown argument \'%s\'!", argv[i]);
 2218     }
 2219   }
 2220 
 2221   /* Very verbose debug mode */
 2222 
 2223   if (debug > 1) debug2 = 1;
 2224 
 2225   /* Set libdir to the default if empty */
 2226 
 2227   if (libdir == NULL)
 2228     libdir = "/usr/share/foomatic";
 2229 
 2230   /* Load translation table for old printer IDs */
 2231   sprintf(oldidfilename, "%s/db/oldprinterids",
 2232       libdir);
 2233   idlist = loadidlist(oldidfilename);
 2234   if (debug) {
 2235     if (idlist) {
 2236       fprintf(stderr, "Printer ID translation table loaded!\n");
 2237     } else {
 2238       fprintf(stderr,
 2239      "Printer ID translation table corrupted, missing, or not readable!\n");
 2240     }
 2241   }
 2242 
 2243   if (!overview) {
 2244 
 2245     /*
 2246      * Compute combo XML file for a given printer/driver combo
 2247      */
 2248 
 2249     /* Check user-supplied parameters */
 2250 
 2251     if (pid == NULL) {
 2252       fprintf(stderr, "A printer ID must be supplied!\n");
 2253       exit(1);
 2254     }
 2255     if (driver == NULL) {
 2256       fprintf(stderr, "A driver name must be supplied!\n");
 2257       exit(1);
 2258     }
 2259     
 2260     /* Set file/dir names */
 2261     
 2262     sprintf(printerfilename, "%s/db/source/printer/%s.xml",
 2263         libdir, pid);
 2264     sprintf(driverfilename, "%s/db/source/driver/%s.xml",
 2265         libdir, driver);
 2266     sprintf(optiondirname, "%s/db/source/opt",
 2267         libdir);
 2268     
 2269     /* Read the printer file and extract the printer manufacturer and 
 2270        model */
 2271 
 2272     if (debug) fprintf(stderr, "Printer file: %s\n", printerfilename);
 2273     printerbuffer = loadfile(printerfilename);
 2274     if (printerbuffer == NULL) {
 2275       pid = translateid(pid, idlist);
 2276       sprintf(printerfilename, "%s/db/source/printer/%s.xml",
 2277           libdir, pid);
 2278       printerbuffer = loadfile(printerfilename);
 2279       if (printerbuffer == NULL) {
 2280     printerbuffer = malloc(1024);
 2281     make = strdup(pid);
 2282     model = strchr(make, '-');
 2283     if (model) {
 2284       t = (char *)model;
 2285       *t = '\0';
 2286       model ++;
 2287     } else { 
 2288       model = "Unknown model";
 2289     }
 2290     t = (char *)make;
 2291     while (*t) {
 2292       if (*t == '_') *t = ' ';
 2293       t ++;
 2294     }
 2295     t = (char *)model;
 2296     while (*t) {
 2297       if (*t == '_') *t = ' ';
 2298       t ++;
 2299     }
 2300     sprintf((char *)printerbuffer, "<printer id=\"printer/%s\">\n <make>%s</make>\n <model>%s</model>\n <mechanism>\n  <color />\n </mechanism>\n <noxmlentry />\n</printer>\n", pid, make, model);
 2301       } else {
 2302     fprintf(stderr, 
 2303         "WARNING: Obsolete printer ID used, using %s instead!\n",
 2304         pid);
 2305       }
 2306     }
 2307     if (debug) fprintf(stderr, "  Printer file loaded!\n");
 2308     comboconfirmed =
 2309       parse(&printerbuffer, pid, driver, printerfilename, NULL, 0, 
 2310         (const char **)defaultsettings, num_defaultsettings, &nopjl,
 2311         idlist, debug2);
 2312     
 2313     /* Read the driver file and check whether the printer is present */
 2314     
 2315     if (debug) fprintf(stderr, "Driver file: %s\n", driverfilename);
 2316     driverbuffer = loadfile(driverfilename);
 2317     if (driverbuffer == NULL) {
 2318       if (!comboconfirmed) {
 2319     fprintf(stderr, 
 2320         "Driver file %s corrupted, missing, or not readable!\n",
 2321         driverfilename);
 2322     exit(1);
 2323       } else {
 2324     driverbuffer = malloc(4096);
 2325     sprintf((char *)driverbuffer, "<driver id=\"driver/%s\">\n <name>%s</name>\n <url></url>\n <execution>\n  <filter />\n  <prototype></prototype>\n </execution>\n <printers>\n  <printer>\n   <id>printer/%s</id>\n  </printer>\n </printers>\n</driver>", driver, driver, pid);
 2326       }
 2327     } else {
 2328       if (debug) fprintf(stderr, "  Driver file loaded!\n");
 2329       comboconfirmed2 =
 2330     parse(&driverbuffer, pid, driver, driverfilename, NULL, 1,
 2331           (const char **)defaultsettings, num_defaultsettings, &nopjl,
 2332           idlist, debug2);
 2333       if ((!comboconfirmed) && (!comboconfirmed2)) {
 2334     fprintf(stderr, "The printer %s is not supported by the driver %s!\n",
 2335         pid, driver);
 2336     exit(1);
 2337       }
 2338       if (debug) {
 2339     if (nopjl) {
 2340       fprintf(stderr, "  Driver forbids PJL options!\n");
 2341     } else {
 2342       fprintf(stderr, "  Driver allows PJL options!\n");
 2343     }
 2344       }
 2345     
 2346       /* Search the Foomatic option directory and read all xml files found
 2347      there. Check whether and how they apply to the given printer/driver
 2348        combo */
 2349     
 2350       optiondir = opendir(optiondirname);
 2351       if (optiondir == NULL) {
 2352     fprintf(stderr, "Cannot read directory %s!\n", optiondirname);
 2353     exit(1);
 2354       }
 2355     
 2356       while((direntry = readdir(optiondir)) != NULL) {
 2357     sprintf(optionfilename, "%s/db/source/opt/%s",
 2358         libdir, direntry->d_name);
 2359     if (debug) fprintf(stderr, "Option file: %s\n", 
 2360                optionfilename);
 2361     if (strcmp((optionfilename + strlen(optionfilename) - 4), ".xml") == 0) {
 2362       /* Process only XML files */
 2363       /* Make space for a pointer to the data */
 2364       num_optbuffers ++;
 2365       optbuffers = (char **)realloc((char **)optbuffers, 
 2366                     sizeof(char *) * num_optbuffers);
 2367       /* load the current option's XML file */
 2368       optbuffers[num_optbuffers-1] = loadfile(optionfilename);
 2369       if (optbuffers[num_optbuffers-1] == NULL) {
 2370         fprintf(stderr,
 2371             "Option file %s corrupted, missing, or not readable!\n",
 2372             optionfilename);
 2373         exit(1);
 2374       }
 2375       if (debug) fprintf(stderr, "  Option file loaded!\n");
 2376       /* process it */
 2377       parse((char **)&(optbuffers[num_optbuffers-1]), pid, driver,
 2378         optionfilename, NULL, 2,
 2379         (const char **)defaultsettings, num_defaultsettings, &nopjl, 
 2380         idlist, debug2);
 2381       /* If the parser discarded it (because it does not apply to our 
 2382          printer/driver combo) remove the space for the pointer to it */
 2383       if (optbuffers[num_optbuffers-1] == NULL) {
 2384         if (debug) fprintf(stderr, "  Option does not apply, removed!\n");
 2385         num_optbuffers --;
 2386       } else {
 2387         if (debug) fprintf(stderr, "  Option applies!\n");
 2388       }
 2389     }
 2390       }
 2391       closedir(optiondir);
 2392     }
 2393     
 2394     /* Output the result on STDOUT */
 2395     if (debug) fprintf(stderr, "Putting out result!\n");
 2396     printf("<foomatic>\n%s%s\n<options>\n", printerbuffer, driverbuffer);
 2397     for (i = 0; i < num_optbuffers; i++) {
 2398       printf("%s", optbuffers[i]);
 2399     }
 2400     printf("</options>\n</foomatic>\n");
 2401 
 2402   } else {
 2403 
 2404     /*
 2405      * Compute XML file for the printer overview list,
 2406      */
 2407 
 2408     /* Set file/dir names */
 2409     
 2410     sprintf(driverdirname, "%s/db/source/driver",
 2411         libdir);
 2412     sprintf(printerdirname, "%s/db/source/printer",
 2413         libdir);
 2414     
 2415     /* Mark overview mode */
 2416     if (overview == 2)
 2417       if (noreadymadeppds)
 2418     pid = "c";
 2419       else
 2420     pid = "C";
 2421     else
 2422       pid = NULL;
 2423 
 2424     /* Add a pseudo-printer to the printer list to which we assign all
 2425        drivers with a command line prototype, so we can determine
 2426        which printer/driver combos provide PPD files. */
 2427     if (pid) {
 2428       plistpointer = 
 2429     (printerlist_t *)malloc(sizeof(printerlist_t));
 2430       strcpy(plistpointer->id, "proto");
 2431       plistpointer->drivers = NULL;
 2432       plistpointer->next = NULL;
 2433       printerlist = plistpointer;
 2434     }
 2435 
 2436     printf("<overview>\n");
 2437 
 2438     /* Search the Foomatic driver directory and read all xml files found
 2439        there. Read out the printers which the driver supports and add them
 2440        to the printer's driver list */
 2441     
 2442     driverdir = opendir(driverdirname);
 2443     if (driverdir == NULL) {
 2444       fprintf(stderr, "Cannot read directory %s!\n", driverdirname);
 2445       exit(1);
 2446     }
 2447     
 2448     while((direntry = readdir(driverdir)) != NULL) {
 2449       sprintf(driverfilename, "%s/db/source/driver/%s",
 2450           libdir, direntry->d_name);
 2451       if (debug) fprintf(stderr, "Driver file: %s\n", driverfilename);
 2452       if (strcmp((driverfilename + strlen(driverfilename) - 4), ".xml") == 0) {
 2453     /* Process only XML files */
 2454     /* load the current driver's XML file */
 2455     driverbuffer = loadfile(driverfilename);
 2456     if (driverbuffer == NULL) {
 2457       fprintf(stderr,
 2458           "Driver file %s corrupted, missing, or not readable!\n",
 2459           driverfilename);
 2460       exit(1);
 2461     }
 2462     if (debug) fprintf(stderr, "  Driver file loaded!\n");
 2463     /* process it */
 2464     parse(&driverbuffer, pid, NULL, driverfilename, &printerlist, 3, 
 2465           (const char **)defaultsettings, num_defaultsettings, &nopjl, 
 2466           idlist, debug2);
 2467     if (driverbuffer != NULL) {
 2468       /* put it out */
 2469       printf("%s\n", driverbuffer);
 2470       /* Delete the driver file from memory */
 2471       free((void *)driverbuffer);
 2472       driverbuffer = NULL;
 2473     }
 2474       }
 2475     }
 2476     closedir(driverdir);
 2477 
 2478     if (debug) {
 2479       plistpointer = printerlist;
 2480       while (plistpointer) {
 2481     fprintf(stderr, "Printer: %s\n", plistpointer->id);
 2482     dlistpointer = plistpointer->drivers;
 2483     while (dlistpointer) {
 2484       fprintf(stderr, "   Driver: %s\n", dlistpointer->name);
 2485       if (dlistpointer->functionality != NULL)
 2486         fprintf(stderr, "    %s\n", dlistpointer->functionality);
 2487       dlistpointer = (driverlist_t *)(dlistpointer->next);
 2488     }
 2489     plistpointer = (printerlist_t *)(plistpointer->next);
 2490       }
 2491     }
 2492 
 2493     /* Search the Foomatic printer directory and read all xml files found
 2494        there. Read out the printer info and build the printer entries for the
 2495        overview with the printer/driver combo list obtained before */
 2496 
 2497     printerdir = opendir(printerdirname);
 2498     if (printerdir == NULL) {
 2499       fprintf(stderr, "Cannot read directory %s!\n", printerdirname);
 2500       exit(1);
 2501     }
 2502     
 2503     while((direntry = readdir(printerdir)) != NULL) {
 2504       sprintf(printerfilename, "%s/db/source/printer/%s",
 2505           libdir, direntry->d_name);
 2506       if (debug) fprintf(stderr, "Printer file: %s\n", printerfilename);
 2507       if (strcmp((printerfilename + strlen(printerfilename) - 4), ".xml") ==
 2508       0) {
 2509     /* Process only XML files */
 2510     /* load the current printer's XML file */
 2511     printerbuffer = loadfile(printerfilename);
 2512     if (printerbuffer == NULL) {
 2513       fprintf(stderr,
 2514           "Printer file %s corrupted, missing, or not readable!\n",
 2515           printerfilename);
 2516       exit(1);
 2517     }
 2518     if (debug) fprintf(stderr, "  Printer file loaded!\n");
 2519     /* process it */
 2520     parse(&printerbuffer, pid, NULL, printerfilename, &printerlist, 4,
 2521           (const char **)defaultsettings, num_defaultsettings, &nopjl, 
 2522           idlist, debug2);
 2523     /* put it out */
 2524     printf("%s", printerbuffer);
 2525     /* Delete the printer file from memory */
 2526     free((void *)printerbuffer);
 2527     printerbuffer = NULL;
 2528       }
 2529     }
 2530 
 2531     closedir(printerdir);
 2532 
 2533     if (debug) {
 2534       plistpointer = printerlist;
 2535       while (plistpointer) {
 2536     fprintf(stderr, "Printer: %s\n", plistpointer->id);
 2537     dlistpointer = plistpointer->drivers;
 2538     while (dlistpointer) {
 2539       fprintf(stderr, "   Driver: %s\n", dlistpointer->name);
 2540       if (dlistpointer->functionality != NULL)
 2541         fprintf(stderr, "    %s\n", dlistpointer->functionality);
 2542       dlistpointer = (driverlist_t *)(dlistpointer->next);
 2543     }
 2544     plistpointer = (printerlist_t *)(plistpointer->next);
 2545       }
 2546     }
 2547 
 2548     /* Now show all printers which are only mentioned in the lists of
 2549        supported prnters of the drivers and which not have a Foomatic
 2550        printer XML entry. */
 2551     while (printerlist) {
 2552       if (printerlist->id && strcmp(printerlist->id, "proto")) {
 2553     if (debug) fprintf(stderr, "    Printer only mentioned in driver XML files:\n      Printer ID: |%s|\n",
 2554                printerlist->id);
 2555     /*strcpy(printerlist->id, translateid(printerlist->id, idlist));*/
 2556     printf("  <printer>\n    <id>");
 2557     printf("%s", printerlist->id);
 2558     make = printerlist->id;
 2559     model = strchr(make, '-');
 2560     if (model) {
 2561       t = (char *)model;
 2562       *t = '\0';
 2563       model ++;
 2564     } else { 
 2565       model = "Unknown model";
 2566     }
 2567     t = (char *)make;
 2568     while (*t) {
 2569       if (*t == '_') *t = ' ';
 2570       t ++;
 2571     }
 2572     t = (char *)model;
 2573     while (*t) {
 2574       if (*t == '_') *t = ' ';
 2575       t ++;
 2576     }
 2577     printf("</id>\n    <make>");
 2578     printf("%s", make);
 2579     printf("</make>\n    <model>");
 2580     printf("%s", model);
 2581     printf("</model>\n    <noxmlentry />\n");
 2582     dlistpointer = printerlist->drivers;
 2583     exceptionfound = 0;
 2584     if (dlistpointer) {
 2585       printf("    <drivers>\n");
 2586       while (dlistpointer) {
 2587         if (dlistpointer->name) {
 2588           printf("      <driver>");
 2589           printf("%s", dlistpointer->name);
 2590           printf("</driver>\n");
 2591           if (dlistpointer->functionality != NULL) exceptionfound = 1;
 2592         }
 2593         dlistpointer = (driverlist_t *)(dlistpointer->next);
 2594       }
 2595       printf("    </drivers>\n");
 2596     }
 2597     if (exceptionfound) {
 2598       printf("    <driverfunctionalityexceptions>\n");
 2599       dlistpointer = printerlist->drivers;
 2600       while (dlistpointer) {
 2601         if ((dlistpointer->functionality != NULL) &&
 2602         (dlistpointer->name != NULL)) {
 2603           printf("      <driverfunctionalityexception>\n");
 2604           printf("        <driver>");
 2605           printf("%s", dlistpointer->name);
 2606           printf("</driver>\n");
 2607           printf("%s", dlistpointer->functionality);
 2608           printf("\n      </driverfunctionalityexception>\n");
 2609         }
 2610         dlistpointer = (driverlist_t *)(dlistpointer->next);
 2611       }
 2612       printf("    </driverfunctionalityexceptions>\n");
 2613     }
 2614     printf("  </printer>\n");
 2615       }
 2616       dlistpointer = printerlist->drivers;
 2617       while (dlistpointer) {
 2618     dlistpreventry = dlistpointer;
 2619     dlistpointer = (driverlist_t *)(dlistpointer->next);
 2620     free(dlistpreventry);
 2621       }
 2622       plistpreventry = printerlist;
 2623       printerlist = (printerlist_t *)(printerlist->next);
 2624       free(plistpreventry);
 2625     }
 2626 
 2627     printf("</overview>\n");
 2628       
 2629     if (debug) {
 2630       plistpointer = printerlist;
 2631       while (plistpointer) {
 2632     fprintf(stderr, "Printer: %s\n", plistpointer->id);
 2633     dlistpointer = plistpointer->drivers;
 2634     while (dlistpointer) {
 2635       fprintf(stderr, "   Driver: %s\n", dlistpointer->name);
 2636       if (dlistpointer->functionality != NULL)
 2637         fprintf(stderr, "    %s\n", dlistpointer->functionality);
 2638       dlistpointer = (driverlist_t *)(dlistpointer->next);
 2639     }
 2640     plistpointer = (printerlist_t *)(plistpointer->next);
 2641       }
 2642     }
 2643 
 2644   }
 2645     
 2646   /* Done */
 2647   exit(0);
 2648 }
 2649 
 2650 /*
 2651  * End of "$Id$".
 2652  */