Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals  

src/main/mxml-file.c

Go to the documentation of this file.
00001 /*
00002  * "$Id: mxml-file.c,v 1.6 2004/04/27 23:23:47 rlk Exp $"
00003  *
00004  * File loading code for mini-XML, a small XML-like file parsing library.
00005  *
00006  * Copyright 2003 by Michael Sweet.
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Library General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * Contents:
00019  *
00020  *   stp_mxmlLoadFile()        - Load a file into an XML node tree.
00021  *   stp_mxmlLoadString()      - Load a string into an XML node tree.
00022  *   stp_mxmlSaveAllocString() - Save an XML node tree to an allocated string.
00023  *   stp_mxmlSaveFile()        - Save an XML tree to a file.
00024  *   stp_mxmlSaveString()      - Save an XML node tree to a string.
00025  *   mxml_add_char()       - Add a character to a buffer, expanding as needed.
00026  *   mxml_file_getc()      - Get a character from a file.
00027  *   mxml_load_data()      - Load data into an XML node tree.
00028  *   mxml_parse_element()  - Parse an element for any attributes...
00029  *   mxml_string_getc()    - Get a character from a string.
00030  *   mxml_write_node()     - Save an XML node to a file.
00031  *   mxml_write_string()   - Write a string, escaping & and < as needed.
00032  *   mxml_write_ws()       - Do whitespace callback...
00033  */
00034 
00035 /*
00036  * Include necessary headers...
00037  */
00038 
00039 #include <gimp-print/mxml.h>
00040 #include "config.h"
00041 
00042 
00043 /*
00044  * Local functions...
00045  */
00046 
00047 static int              mxml_add_char(int ch, char **ptr, char **buffer,
00048                                       int *bufsize);
00049 static int              mxml_file_getc(void *p);
00050 static int              mxml_file_putc(int ch, void *p);
00051 static stp_mxml_node_t  *mxml_load_data(stp_mxml_node_t *top, void *p,
00052                                         stp_mxml_type_t (*cb)(stp_mxml_node_t *),
00053                                         int (*getc_cb)(void *));
00054 static int              mxml_parse_element(stp_mxml_node_t *node, void *p,
00055                                            int (*getc_cb)(void *));
00056 static int              mxml_string_getc(void *p);
00057 static int              mxml_string_putc(int ch, void *p);
00058 static int              mxml_write_node(stp_mxml_node_t *node, void *p,
00059                                         int (*cb)(stp_mxml_node_t *, int),
00060                                         int col,
00061                                         int (*putc_cb)(int, void *));
00062 static int              mxml_write_string(const char *s, void *p,
00063                                           int (*putc_cb)(int, void *));
00064 static int              mxml_write_ws(stp_mxml_node_t *node, void *p, 
00065                                       int (*cb)(stp_mxml_node_t *, int), int ws,
00066                                       int col, int (*putc_cb)(int, void *));
00067 
00068 
00069 /*
00070  * 'stp_mxmlLoadFile()' - Load a file into an XML node tree.
00071  *
00072  * The nodes in the specified file are added to the specified top node.
00073  * If no top node is provided, the XML file MUST be well-formed with a
00074  * single parent node like <?xml> for the entire file. The callback
00075  * function returns the value type that should be used for child nodes.
00076  * If STP_MXML_NO_CALLBACK is specified then all child nodes will be either
00077  * STP_MXML_ELEMENT or STP_MXML_TEXT nodes.
00078  */
00079 
00080 stp_mxml_node_t *                               /* O - First node or NULL if the file could not be read. */
00081 stp_mxmlLoadFile(stp_mxml_node_t *top,          /* I - Top node */
00082              FILE        *fp,           /* I - File to read from */
00083              stp_mxml_type_t (*cb)(stp_mxml_node_t *))
00084                                         /* I - Callback function or STP_MXML_NO_CALLBACK */
00085 {
00086   return (mxml_load_data(top, fp, cb, mxml_file_getc));
00087 }
00088 
00089 
00090 /*
00091  * 'stp_mxmlLoadString()' - Load a string into an XML node tree.
00092  *
00093  * The nodes in the specified string are added to the specified top node.
00094  * If no top node is provided, the XML string MUST be well-formed with a
00095  * single parent node like <?xml> for the entire string. The callback
00096  * function returns the value type that should be used for child nodes.
00097  * If STP_MXML_NO_CALLBACK is specified then all child nodes will be either
00098  * STP_MXML_ELEMENT or STP_MXML_TEXT nodes.
00099  */
00100 
00101 stp_mxml_node_t *                               /* O - First node or NULL if the string has errors. */
00102 stp_mxmlLoadString(stp_mxml_node_t *top,        /* I - Top node */
00103                const char  *s,          /* I - String to load */
00104                stp_mxml_type_t (*cb)(stp_mxml_node_t *))
00105                                         /* I - Callback function or STP_MXML_NO_CALLBACK */
00106 {
00107   return (mxml_load_data(top, &s, cb, mxml_string_getc));
00108 }
00109 
00110 
00111 /*
00112  * 'stp_mxmlSaveAllocString()' - Save an XML node tree to an allocated string.
00113  *
00114  * This function returns a pointer to a string containing the textual
00115  * representation of the XML node tree.  The string should be freed
00116  * using the free() function when you are done with it.  NULL is returned
00117  * if the node would produce an empty string or if the string cannot be
00118  * allocated.
00119  */
00120 
00121 char *                                  /* O - Allocated string or NULL */
00122 stp_mxmlSaveAllocString(stp_mxml_node_t *node,  /* I - Node to write */
00123                     int         (*cb)(stp_mxml_node_t *, int))
00124                                         /* I - Whitespace callback or STP_MXML_NO_CALLBACK */
00125 {
00126   int   bytes;                          /* Required bytes */
00127   char  buffer[8192];                   /* Temporary buffer */
00128   char  *s;                             /* Allocated string */
00129 
00130 
00131  /*
00132   * Write the node to the temporary buffer...
00133   */
00134 
00135   bytes = stp_mxmlSaveString(node, buffer, sizeof(buffer), cb);
00136 
00137   if (bytes <= 0)
00138     return (NULL);
00139 
00140   if (bytes < (int)(sizeof(buffer) - 1))
00141   {
00142    /*
00143     * Node fit inside the buffer, so just duplicate that string and
00144     * return...
00145     */
00146 
00147     return (strdup(buffer));
00148   }
00149 
00150  /*
00151   * Allocate a buffer of the required size and save the node to the
00152   * new buffer...
00153   */
00154 
00155   if ((s = malloc(bytes + 1)) == NULL)
00156     return (NULL);
00157 
00158   stp_mxmlSaveString(node, s, bytes + 1, cb);
00159 
00160  /*
00161   * Return the allocated string...
00162   */
00163 
00164   return (s);
00165 }
00166 
00167 
00168 /*
00169  * 'stp_mxmlSaveFile()' - Save an XML tree to a file.
00170  *
00171  * The callback argument specifies a function that returns a whitespace
00172  * character or nul (0) before and after each element. If STP_MXML_NO_CALLBACK
00173  * is specified, whitespace will only be added before STP_MXML_TEXT nodes
00174  * with leading whitespace and before attribute names inside opening
00175  * element tags.
00176  */
00177 
00178 int                                     /* O - 0 on success, -1 on error. */
00179 stp_mxmlSaveFile(stp_mxml_node_t *node,         /* I - Node to write */
00180              FILE        *fp,           /* I - File to write to */
00181              int         (*cb)(stp_mxml_node_t *, int))
00182                                         /* I - Whitespace callback or STP_MXML_NO_CALLBACK */
00183 {
00184   int   col;                            /* Final column */
00185 
00186 
00187  /*
00188   * Write the node...
00189   */
00190 
00191   if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc)) < 0)
00192     return (-1);
00193 
00194   if (col > 0)
00195     if (putc('\n', fp) < 0)
00196       return (-1);
00197 
00198  /*
00199   * Return 0 (success)...
00200   */
00201 
00202   return (0);
00203 }
00204 
00205 
00206 /*
00207  * 'stp_mxmlSaveString()' - Save an XML node tree to a string.
00208  *
00209  * This function returns the total number of bytes that would be
00210  * required for the string but only copies (bufsize - 1) characters
00211  * into the specified buffer.
00212  */
00213 
00214 int                                     /* O - Size of string */
00215 stp_mxmlSaveString(stp_mxml_node_t *node,       /* I - Node to write */
00216                char        *buffer,     /* I - String buffer */
00217                int         bufsize,     /* I - Size of string buffer */
00218                int         (*cb)(stp_mxml_node_t *, int))
00219                                         /* I - Whitespace callback or STP_MXML_NO_CALLBACK */
00220 {
00221   int   col;                            /* Final column */
00222   char  *ptr[2];                        /* Pointers for putc_cb */
00223 
00224 
00225  /*
00226   * Write the node...
00227   */
00228 
00229   ptr[0] = buffer;
00230   ptr[1] = buffer + bufsize;
00231 
00232   if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc)) < 0)
00233     return (-1);
00234 
00235   if (col > 0)
00236     mxml_string_putc('\n', ptr);
00237 
00238  /*
00239   * Nul-terminate the buffer...
00240   */
00241 
00242   if (ptr[0] >= ptr[1])
00243     buffer[bufsize - 1] = '\0';
00244   else
00245     ptr[0][0] = '\0';
00246 
00247  /*
00248   * Return the number of characters...
00249   */
00250 
00251   return (ptr[0] - buffer);
00252 }
00253 
00254 
00255 /*
00256  * 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
00257  */
00258 
00259 static int                              /* O  - 0 on success, -1 on error */
00260 mxml_add_char(int  ch,                  /* I  - Character to add */
00261               char **bufptr,            /* IO - Current position in buffer */
00262               char **buffer,            /* IO - Current buffer */
00263               int  *bufsize)            /* IO - Current buffer size */
00264 {
00265   char  *newbuffer;                     /* New buffer value */
00266 
00267 
00268   if (*bufptr >= (*buffer + *bufsize - 1))
00269   {
00270    /*
00271     * Increase the size of the buffer...
00272     */
00273 
00274     if (*bufsize < 1024)
00275       (*bufsize) *= 2;
00276     else
00277       (*bufsize) += 1024;
00278 
00279     if ((newbuffer = realloc(*buffer, *bufsize)) == NULL)
00280     {
00281       free(*buffer);
00282 
00283       fprintf(stderr, "Unable to expand string buffer to %d bytes!\n",
00284               *bufsize);
00285 
00286       return (-1);
00287     }
00288 
00289     *bufptr = newbuffer + (*bufptr - *buffer);
00290     *buffer = newbuffer;
00291   }
00292 
00293   *(*bufptr)++ = ch;
00294 
00295   return (0);
00296 }
00297 
00298 
00299 /*
00300  * 'mxml_file_getc()' - Get a character from a file.
00301  */
00302 
00303 static int                              /* O - Character or EOF */
00304 mxml_file_getc(void *p)                 /* I - Pointer to file */
00305 {
00306   return (getc((FILE *)p));
00307 }
00308 
00309 
00310 /*
00311  * 'mxml_file_putc()' - Write a character to a file.
00312  */
00313 
00314 static int                              /* O - 0 on success, -1 on failure */
00315 mxml_file_putc(int  ch,                 /* I - Character to write */
00316                void *p)                 /* I - Pointer to file */
00317 {
00318   return (putc(ch, (FILE *)p));
00319 }
00320 
00321 
00322 /*
00323  * 'mxml_load_data()' - Load data into an XML node tree.
00324  */
00325 
00326 static stp_mxml_node_t *                        /* O - First node or NULL if the file could not be read. */
00327 mxml_load_data(stp_mxml_node_t *top,    /* I - Top node */
00328                void        *p,          /* I - Pointer to data */
00329                stp_mxml_type_t (*cb)(stp_mxml_node_t *),
00330                                         /* I - Callback function or STP_MXML_NO_CALLBACK */
00331                int         (*getc_cb)(void *))
00332                                         /* I - Read function */
00333 {
00334   stp_mxml_node_t       *node,                  /* Current node */
00335                 *parent;                /* Current parent node */
00336   int           ch,                     /* Character from file */
00337                 whitespace;             /* Non-zero if whitespace seen */
00338   char          *buffer,                /* String buffer */
00339                 *bufptr;                /* Pointer into buffer */
00340   int           bufsize;                /* Size of buffer */
00341   stp_mxml_type_t       type;                   /* Current node type */
00342 
00343 
00344  /*
00345   * Read elements and other nodes from the file...
00346   */
00347 
00348   if ((buffer = malloc(64)) == NULL)
00349   {
00350     fputs("Unable to allocate string buffer!\n", stderr);
00351     return (NULL);
00352   }
00353 
00354   bufsize    = 64;
00355   bufptr     = buffer;
00356   parent     = top;
00357   whitespace = 0;
00358 
00359   if (cb && parent)
00360     type = (*cb)(parent);
00361   else
00362     type = STP_MXML_TEXT;
00363 
00364   while ((ch = (*getc_cb)(p)) != EOF)
00365   {
00366     if ((ch == '<' || (isspace(ch) && type != STP_MXML_OPAQUE)) && bufptr > buffer)
00367     {
00368      /*
00369       * Add a new value node...
00370       */
00371 
00372       *bufptr = '\0';
00373 
00374       switch (type)
00375       {
00376         case STP_MXML_INTEGER :
00377             node = stp_mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
00378             break;
00379 
00380         case STP_MXML_OPAQUE :
00381             node = stp_mxmlNewOpaque(parent, buffer);
00382             break;
00383 
00384         case STP_MXML_REAL :
00385             node = stp_mxmlNewReal(parent, strtod(buffer, &bufptr));
00386             break;
00387 
00388         case STP_MXML_TEXT :
00389             node = stp_mxmlNewText(parent, whitespace, buffer);
00390             break;
00391 
00392         default : /* Should never happen... */
00393             node = NULL;
00394             break;
00395       }   
00396 
00397       if (*bufptr)
00398       {
00399        /*
00400         * Bad integer/real number value...
00401         */
00402 
00403         fprintf(stderr, "Bad %s value '%s' in parent <%s>!\n",
00404                 type == STP_MXML_INTEGER ? "integer" : "real", buffer,
00405                 parent ? parent->value.element.name : "null");
00406         break;
00407       }
00408 
00409       bufptr     = buffer;
00410       whitespace = isspace(ch) && type == STP_MXML_TEXT;
00411 
00412       if (!node)
00413       {
00414        /*
00415         * Just print error for now...
00416         */
00417 
00418         fprintf(stderr, "Unable to add value node of type %d to parent <%s>!\n",
00419                 type, parent ? parent->value.element.name : "null");
00420         break;
00421       }
00422     }
00423     else if (isspace(ch) && type == STP_MXML_TEXT)
00424       whitespace = 1;
00425 
00426    /*
00427     * Add lone whitespace node if we have an element and existing
00428     * whitespace...
00429     */
00430 
00431     if (ch == '<' && whitespace && type == STP_MXML_TEXT)
00432     {
00433       stp_mxmlNewText(parent, whitespace, "");
00434       whitespace = 0;
00435     }
00436 
00437     if (ch == '<')
00438     {
00439      /*
00440       * Start of open/close tag...
00441       */
00442 
00443       bufptr = buffer;
00444 
00445       while ((ch = (*getc_cb)(p)) != EOF)
00446         if (isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
00447           break;
00448         else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
00449         {
00450           free(buffer);
00451           return (NULL);
00452         }
00453         else if ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3))
00454           break;
00455 
00456       *bufptr = '\0';
00457 
00458       if (!strcmp(buffer, "!--"))
00459       {
00460        /*
00461         * Gather rest of comment...
00462         */
00463 
00464         while ((ch = (*getc_cb)(p)) != EOF)
00465         {
00466           if (ch == '>' && bufptr > (buffer + 4) &&
00467               !strncmp(bufptr - 2, "--", 2))
00468             break;
00469           else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
00470           {
00471             free(buffer);
00472             return (NULL);
00473           }
00474         }
00475 
00476        /*
00477         * Error out if we didn't get the whole comment...
00478         */
00479 
00480         if (ch != '>')
00481           break;
00482 
00483        /*
00484         * Otherwise add this as an element under the current parent...
00485         */
00486 
00487         *bufptr = '\0';
00488 
00489         if (!stp_mxmlNewElement(parent, buffer))
00490         {
00491          /*
00492           * Just print error for now...
00493           */
00494 
00495           fprintf(stderr, "Unable to add comment node to parent <%s>!\n",
00496                   parent ? parent->value.element.name : "null");
00497           break;
00498         }
00499       }
00500       else if (buffer[0] == '!')
00501       {
00502        /*
00503         * Gather rest of declaration...
00504         */
00505 
00506         do
00507         {
00508           if (ch == '>')
00509             break;
00510           else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
00511           {
00512             free(buffer);
00513             return (NULL);
00514           }
00515         }
00516         while ((ch = (*getc_cb)(p)) != EOF);
00517 
00518        /*
00519         * Error out if we didn't get the whole declaration...
00520         */
00521 
00522         if (ch != '>')
00523           break;
00524 
00525        /*
00526         * Otherwise add this as an element under the current parent...
00527         */
00528 
00529         *bufptr = '\0';
00530 
00531         node = stp_mxmlNewElement(parent, buffer);
00532         if (!node)
00533         {
00534          /*
00535           * Just print error for now...
00536           */
00537 
00538           fprintf(stderr, "Unable to add declaration node to parent <%s>!\n",
00539                   parent ? parent->value.element.name : "null");
00540           break;
00541         }
00542 
00543        /*
00544         * Descend into this node, setting the value type as needed...
00545         */
00546 
00547         parent = node;
00548 
00549         if (cb && parent)
00550           type = (*cb)(parent);
00551       }
00552       else if (buffer[0] == '/')
00553       {
00554        /*
00555         * Handle close tag...
00556         */
00557 
00558         if (!parent || strcmp(buffer + 1, parent->value.element.name))
00559         {
00560          /*
00561           * Close tag doesn't match tree; print an error for now...
00562           */
00563 
00564           fprintf(stderr, "Mismatched close tag <%s> under parent <%s>!\n",
00565                   buffer, parent->value.element.name);
00566           break;
00567         }
00568 
00569        /*
00570         * Keep reading until we see >...
00571         */
00572 
00573         while (ch != '>' && ch != EOF)
00574           ch = (*getc_cb)(p);
00575 
00576        /*
00577         * Ascend into the parent and set the value type as needed...
00578         */
00579 
00580         parent = parent->parent;
00581 
00582         if (cb && parent)
00583           type = (*cb)(parent);
00584       }
00585       else
00586       {
00587        /*
00588         * Handle open tag...
00589         */
00590 
00591         node = stp_mxmlNewElement(parent, buffer);
00592 
00593         if (!node)
00594         {
00595          /*
00596           * Just print error for now...
00597           */
00598 
00599           fprintf(stderr, "Unable to add element node to parent <%s>!\n",
00600                   parent ? parent->value.element.name : "null");
00601           break;
00602         }
00603 
00604         if (isspace(ch))
00605           ch = mxml_parse_element(node, p, getc_cb);
00606         else if (ch == '/')
00607         {
00608           if ((ch = (*getc_cb)(p)) != '>')
00609           {
00610             fprintf(stderr, "Expected > but got '%c' instead for element <%s/>!\n",
00611                     ch, buffer);
00612             break;
00613           }
00614 
00615           ch = '/';
00616         }
00617 
00618         if (ch == EOF)
00619           break;
00620 
00621         if (ch != '/')
00622         {
00623          /*
00624           * Descend into this node, setting the value type as needed...
00625           */
00626 
00627           parent = node;
00628 
00629           if (cb && parent)
00630             type = (*cb)(parent);
00631         }
00632       }
00633 
00634       bufptr  = buffer;
00635     }
00636     else if (ch == '&')
00637     {
00638      /*
00639       * Add character entity to current buffer...  Currently we only
00640       * support &lt;, &amp;, &gt;, &nbsp;, &quot;, &#nnn;, and &#xXXXX;...
00641       */
00642 
00643       char      entity[64],             /* Entity string */
00644                 *entptr;                /* Pointer into entity */
00645 
00646 
00647       entity[0] = ch;
00648       entptr    = entity + 1;
00649 
00650       while ((ch = (*getc_cb)(p)) != EOF)
00651         if (!isalnum(ch) && ch != '#')
00652           break;
00653         else if (entptr < (entity + sizeof(entity) - 1))
00654           *entptr++ = ch;
00655         else
00656         {
00657           fprintf(stderr, "Entity name too long under parent <%s>!\n",
00658                   parent ? parent->value.element.name : "null");
00659           break;
00660         }
00661 
00662       *entptr = '\0';
00663 
00664       if (ch != ';')
00665       {
00666         fprintf(stderr, "Entity name \"%s\" not terminated under parent <%s>!\n",
00667                 entity, parent ? parent->value.element.name : "null");
00668         break;
00669       }
00670 
00671       if (entity[1] == '#')
00672       {
00673         if (entity[2] == 'x')
00674           ch = strtol(entity + 3, NULL, 16);
00675         else
00676           ch = strtol(entity + 2, NULL, 10);
00677       }
00678       else if (!strcmp(entity, "&amp"))
00679         ch = '&';
00680       else if (!strcmp(entity, "&gt"))
00681         ch = '>';
00682       else if (!strcmp(entity, "&lt"))
00683         ch = '<';
00684       else if (!strcmp(entity, "&nbsp"))
00685         ch = 0xa0;
00686       else if (!strcmp(entity, "&quot"))
00687         ch = '\"';
00688       else
00689       {
00690         fprintf(stderr, "Entity name \"%s;\" not supported under parent <%s>!\n",
00691                 entity, parent ? parent->value.element.name : "null");
00692         break;
00693       }
00694 
00695       if (ch < 128)
00696       {
00697        /*
00698         * Plain ASCII doesn't need special encoding...
00699         */
00700 
00701         if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
00702         {
00703           free(buffer);
00704           return (NULL);
00705         }
00706       }
00707       else
00708       {
00709        /*
00710         * Use UTF-8 encoding for the Unicode char...
00711         */
00712 
00713         if (ch < 2048)
00714         {
00715           if (mxml_add_char(0xc0 | (ch >> 6), &bufptr, &buffer, &bufsize))
00716           {
00717             free(buffer);
00718             return (NULL);
00719           }
00720           if (mxml_add_char(0x80 | (ch & 63), &bufptr, &buffer, &bufsize))
00721           {
00722             free(buffer);
00723             return (NULL);
00724           }
00725         }
00726         else if (ch < 65536)
00727         {
00728           if (mxml_add_char(0xe0 | (ch >> 12), &bufptr, &buffer, &bufsize))
00729           {
00730             free(buffer);
00731             return (NULL);
00732           }
00733           if (mxml_add_char(0x80 | ((ch >> 6) & 63), &bufptr, &buffer, &bufsize))
00734           {
00735             free(buffer);
00736             return (NULL);
00737           }
00738           if (mxml_add_char(0x80 | (ch & 63), &bufptr, &buffer, &bufsize))
00739           {
00740             free(buffer);
00741             return (NULL);
00742           }
00743         }
00744         else
00745         {
00746           if (mxml_add_char(0xf0 | (ch >> 18), &bufptr, &buffer, &bufsize))
00747           {
00748             free(buffer);
00749             return (NULL);
00750           }
00751           if (mxml_add_char(0x80 | ((ch >> 12) & 63), &bufptr, &buffer, &bufsize))
00752           {
00753             free(buffer);
00754             return (NULL);
00755           }
00756           if (mxml_add_char(0x80 | ((ch >> 6) & 63), &bufptr, &buffer, &bufsize))
00757           {
00758             free(buffer);
00759             return (NULL);
00760           }
00761           if (mxml_add_char(0x80 | (ch & 63), &bufptr, &buffer, &bufsize))
00762           {
00763             free(buffer);
00764             return (NULL);
00765           }
00766         }
00767       }
00768     }
00769     else if (type == STP_MXML_OPAQUE || !isspace(ch))
00770     {
00771      /*
00772       * Add character to current buffer...
00773       */
00774 
00775       if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
00776       {
00777         free(buffer);
00778         return (NULL);
00779       }
00780     }
00781   }
00782 
00783  /*
00784   * Free the string buffer - we don't need it anymore...
00785   */
00786 
00787   free(buffer);
00788 
00789  /*
00790   * Find the top element and return it...
00791   */
00792 
00793   if (parent)
00794   {
00795     while (parent->parent != top)
00796       parent = parent->parent;
00797   }
00798 
00799   return (parent);
00800 }
00801 
00802 
00803 /*
00804  * 'mxml_parse_element()' - Parse an element for any attributes...
00805  */
00806 
00807 static int                              /* O - Terminating character */
00808 mxml_parse_element(stp_mxml_node_t *node,       /* I - Element node */
00809                    void        *p,      /* I - Data to read from */
00810                    int         (*getc_cb)(void *))
00811                                         /* I - Data callback */
00812 {
00813   int   ch,                             /* Current character in file */
00814         quote;                          /* Quoting character */
00815   char  *name,                          /* Attribute name */
00816         *value,                         /* Attribute value */
00817         *ptr;                           /* Pointer into name/value */
00818   int   namesize,                       /* Size of name string */
00819         valsize;                        /* Size of value string */
00820 
00821 
00822  /*
00823   * Initialize the name and value buffers...
00824   */
00825 
00826   if ((name = malloc(64)) == NULL)
00827   {
00828     fputs("Unable to allocate memory for name!\n", stderr);
00829     return (EOF);
00830   }
00831 
00832   namesize = 64;
00833 
00834   if ((value = malloc(64)) == NULL)
00835   {
00836     free(name);
00837     fputs("Unable to allocate memory for value!\n", stderr);
00838     return (EOF);
00839   }
00840 
00841   valsize = 64;
00842 
00843  /*
00844   * Loop until we hit a >, /, ?, or EOF...
00845   */
00846 
00847   while ((ch = (*getc_cb)(p)) != EOF)
00848   {
00849 #ifdef DEBUG
00850     fprintf(stderr, "parse_element: ch='%c'\n", ch);
00851 #endif /* DEBUG */
00852 
00853    /*
00854     * Skip leading whitespace...
00855     */
00856 
00857     if (isspace(ch))
00858       continue;
00859 
00860    /*
00861     * Stop at /, ?, or >...
00862     */
00863 
00864     if (ch == '/' || ch == '?')
00865     {
00866      /*
00867       * Grab the > character and print an error if it isn't there...
00868       */
00869 
00870       quote = (*getc_cb)(p);
00871 
00872       if (quote != '>')
00873       {
00874         fprintf(stderr, "Expected '>' after '%c' for element %s, but got '%c'!\n",
00875                 ch, node->value.element.name, quote);
00876         ch = EOF;
00877       }
00878 
00879       break;
00880     }
00881     else if (ch == '>')
00882       break;
00883 
00884    /*
00885     * Read the attribute name...
00886     */
00887 
00888     name[0] = ch;
00889     ptr     = name + 1;
00890 
00891     while ((ch = (*getc_cb)(p)) != EOF)
00892       if (isspace(ch) || ch == '=' || ch == '/' || ch == '>' || ch == '?')
00893         break;
00894       else if (mxml_add_char(ch, &ptr, &name, &namesize))
00895       {
00896         free(name);
00897         free(value);
00898         return (EOF);
00899       }
00900 
00901     *ptr = '\0';
00902 
00903     if (ch == '=')
00904     {
00905      /*
00906       * Read the attribute value...
00907       */
00908 
00909       if ((ch = (*getc_cb)(p)) == EOF)
00910       {
00911         fprintf(stderr, "Missing value for attribute '%s' in element %s!\n",
00912                 name, node->value.element.name);
00913         return (EOF);
00914       }
00915 
00916       if (ch == '\'' || ch == '\"')
00917       {
00918        /*
00919         * Read quoted value...
00920         */
00921 
00922         quote = ch;
00923         ptr   = value;
00924 
00925         while ((ch = (*getc_cb)(p)) != EOF)
00926           if (ch == quote)
00927             break;
00928           else if (mxml_add_char(ch, &ptr, &value, &valsize))
00929           {
00930             free(name);
00931             free(value);
00932             return (EOF);
00933           }
00934 
00935         *ptr = '\0';
00936       }
00937       else
00938       {
00939        /*
00940         * Read unquoted value...
00941         */
00942 
00943         value[0] = ch;
00944         ptr      = value + 1;
00945 
00946         while ((ch = (*getc_cb)(p)) != EOF)
00947           if (isspace(ch) || ch == '=' || ch == '/' || ch == '>')
00948             break;
00949           else if (mxml_add_char(ch, &ptr, &value, &valsize))
00950           {
00951             free(name);
00952             free(value);
00953             return (EOF);
00954           }
00955 
00956         *ptr = '\0';
00957       }
00958     }
00959     else
00960       value[0] = '\0';
00961 
00962    /*
00963     * Save last character in case we need it...
00964     */
00965 
00966     if (ch == '/' || ch == '?')
00967     {
00968      /*
00969       * Grab the > character and print an error if it isn't there...
00970       */
00971 
00972       quote = (*getc_cb)(p);
00973 
00974       if (quote != '>')
00975       {
00976         fprintf(stderr, "Expected '>' after '%c' for element %s, but got '%c'!\n",
00977                 ch, node->value.element.name, quote);
00978         ch = EOF;
00979       }
00980 
00981       break;
00982     }
00983     else if (ch == '>')
00984       break;
00985 
00986    /*
00987     * Set the attribute...
00988     */
00989 
00990     stp_mxmlElementSetAttr(node, name, value);
00991   }
00992 
00993  /*
00994   * Free the name and value buffers and return...
00995   */
00996 
00997   free(name);
00998   free(value);
00999 
01000   return (ch);
01001 }
01002 
01003 
01004 /*
01005  * 'mxml_string_getc()' - Get a character from a string.
01006  */
01007 
01008 static int                              /* O - Character or EOF */
01009 mxml_string_getc(void *p)               /* I - Pointer to file */
01010 {
01011   int           ch;                     /* Character */
01012   const char    **s;                    /* Pointer to string pointer */
01013 
01014 
01015   s = (const char **)p;
01016 
01017   if ((ch = *s[0]) != 0)
01018   {
01019     (*s)++;
01020     return (ch);
01021   }
01022   else
01023     return (EOF);
01024 }
01025 
01026 
01027 /*
01028  * 'mxml_string_putc()' - Write a character to a string.
01029  */
01030 
01031 static int                              /* O - 0 on success, -1 on failure */
01032 mxml_string_putc(int  ch,               /* I - Character to write */
01033                  void *p)               /* I - Pointer to string pointers */
01034 {
01035   char  **pp;                           /* Pointer to string pointers */
01036 
01037 
01038   pp = (char **)p;
01039 
01040   if (pp[0] < pp[1])
01041     pp[0][0] = ch;
01042 
01043   pp[0] ++;
01044 
01045   return (0);
01046 }
01047 
01048 
01049 /*
01050  * 'mxml_write_node()' - Save an XML node to a file.
01051  */
01052 
01053 static int                              /* O - Column or -1 on error */
01054 mxml_write_node(stp_mxml_node_t *node,  /* I - Node to write */
01055                 void        *p,         /* I - File to write to */
01056                 int         (*cb)(stp_mxml_node_t *, int),
01057                                         /* I - Whitespace callback */
01058                 int         col,        /* I - Current column */
01059                 int         (*putc_cb)(int, void *))
01060 {
01061   int           i;                      /* Looping var */
01062   stp_mxml_attr_t       *attr;                  /* Current attribute */
01063   char          s[255];                 /* Temporary string */
01064 
01065 
01066   while (node != NULL)
01067   {
01068    /*
01069     * Print the node value...
01070     */
01071 
01072     switch (node->type)
01073     {
01074       case STP_MXML_ELEMENT :
01075           col = mxml_write_ws(node, p, cb, STP_MXML_WS_BEFORE_OPEN, col, putc_cb);
01076 
01077           if ((*putc_cb)('<', p) < 0)
01078             return (-1);
01079           if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
01080             return (-1);
01081 
01082           col += strlen(node->value.element.name) + 1;
01083 
01084           for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
01085                i > 0;
01086                i --, attr ++)
01087           {
01088             if ((col + strlen(attr->name) + strlen(attr->value) + 3) > STP_MXML_WRAP)
01089             {
01090               if ((*putc_cb)('\n', p) < 0)
01091                 return (-1);
01092 
01093               col = 0;
01094             }
01095             else
01096             {
01097               if ((*putc_cb)(' ', p) < 0)
01098                 return (-1);
01099 
01100               col ++;
01101             }
01102 
01103             if (mxml_write_string(attr->name, p, putc_cb) < 0)
01104               return (-1);
01105             if ((*putc_cb)('=', p) < 0)
01106               return (-1);
01107             if ((*putc_cb)('\"', p) < 0)
01108               return (-1);
01109             if (mxml_write_string(attr->value, p, putc_cb) < 0)
01110               return (-1);
01111             if ((*putc_cb)('\"', p) < 0)
01112               return (-1);
01113             
01114             col += strlen(attr->name) + strlen(attr->value) + 3;
01115           }
01116 
01117           if (node->child)
01118           {
01119            /*
01120             * The ? and ! elements are special-cases and have no end tags...
01121             */
01122 
01123             if (node->value.element.name[0] == '?')
01124             {
01125               if ((*putc_cb)('?', p) < 0)
01126                 return (-1);
01127               if ((*putc_cb)('>', p) < 0)
01128                 return (-1);
01129               if ((*putc_cb)('\n', p) < 0)
01130                 return (-1);
01131 
01132               col = 0;
01133             }
01134             else if ((*putc_cb)('>', p) < 0)
01135               return (-1);
01136             else
01137               col ++;
01138 
01139             col = mxml_write_ws(node, p, cb, STP_MXML_WS_AFTER_OPEN, col, putc_cb);
01140 
01141             if ((col = mxml_write_node(node->child, p, cb, col, putc_cb)) < 0)
01142               return (-1);
01143 
01144             if (node->value.element.name[0] != '?' &&
01145                 node->value.element.name[0] != '!')
01146             {
01147               col = mxml_write_ws(node, p, cb, STP_MXML_WS_BEFORE_CLOSE, col, putc_cb);
01148 
01149               if ((*putc_cb)('<', p) < 0)
01150                 return (-1);
01151               if ((*putc_cb)('/', p) < 0)
01152                 return (-1);
01153               if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
01154                 return (-1);
01155               if ((*putc_cb)('>', p) < 0)
01156                 return (-1);
01157 
01158               col += strlen(node->value.element.name) + 3;
01159 
01160               col = mxml_write_ws(node, p, cb, STP_MXML_WS_AFTER_CLOSE, col, putc_cb);
01161             }
01162           }
01163           else if (node->value.element.name[0] == '!')
01164           {
01165             if ((*putc_cb)('>', p) < 0)
01166               return (-1);
01167             else
01168               col ++;
01169 
01170             col = mxml_write_ws(node, p, cb, STP_MXML_WS_AFTER_OPEN, col, putc_cb);
01171           }
01172           else
01173           {
01174             if ((*putc_cb)('/', p) < 0)
01175               return (-1);
01176             if ((*putc_cb)('>', p) < 0)
01177               return (-1);
01178 
01179             col += 2;
01180 
01181             col = mxml_write_ws(node, p, cb, STP_MXML_WS_AFTER_OPEN, col, putc_cb);
01182           }
01183           break;
01184 
01185       case STP_MXML_INTEGER :
01186           if (node->prev)
01187           {
01188             if (col > STP_MXML_WRAP)
01189             {
01190               if ((*putc_cb)('\n', p) < 0)
01191                 return (-1);
01192 
01193               col = 0;
01194             }
01195             else if ((*putc_cb)(' ', p) < 0)
01196               return (-1);
01197             else
01198               col ++;
01199           }
01200 
01201           sprintf(s, "%d", node->value.integer);
01202           if (mxml_write_string(s, p, putc_cb) < 0)
01203             return (-1);
01204 
01205           col += strlen(s);
01206           break;
01207 
01208       case STP_MXML_OPAQUE :
01209           if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
01210             return (-1);
01211 
01212           col += strlen(node->value.opaque);
01213           break;
01214 
01215       case STP_MXML_REAL :
01216           if (node->prev)
01217           {
01218             if (col > STP_MXML_WRAP)
01219             {
01220               if ((*putc_cb)('\n', p) < 0)
01221                 return (-1);
01222 
01223               col = 0;
01224             }
01225             else if ((*putc_cb)(' ', p) < 0)
01226               return (-1);
01227             else
01228               col ++;
01229           }
01230 
01231           sprintf(s, "%f", node->value.real);
01232           if (mxml_write_string(s, p, putc_cb) < 0)
01233             return (-1);
01234 
01235           col += strlen(s);
01236           break;
01237 
01238       case STP_MXML_TEXT :
01239           if (node->value.text.whitespace && col > 0)
01240           {
01241             if (col > STP_MXML_WRAP)
01242             {
01243               if ((*putc_cb)('\n', p) < 0)
01244                 return (-1);
01245 
01246               col = 0;
01247             }
01248             else if ((*putc_cb)(' ', p) < 0)
01249               return (-1);
01250             else
01251               col ++;
01252           }
01253 
01254           if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
01255             return (-1);
01256 
01257           col += strlen(node->value.text.string);
01258           break;
01259     }
01260 
01261    /*
01262     * Next node...
01263     */
01264 
01265     node = node->next;
01266   }
01267 
01268   return (col);
01269 }
01270 
01271 
01272 /*
01273  * 'mxml_write_string()' - Write a string, escaping & and < as needed.
01274  */
01275 
01276 static int                              /* O - 0 on success, -1 on failure */
01277 mxml_write_string(const char *s,        /* I - String to write */
01278                   void       *p,        /* I - Write pointer */
01279                   int        (*putc_cb)(int, void *))
01280                                         /* I - Write callback */
01281 {
01282   char  buf[255],                       /* Buffer */
01283         *bufptr;                        /* Pointer into buffer */
01284 
01285 
01286   while (*s)
01287   {
01288     if (*s == '&')
01289     {
01290       if ((*putc_cb)('&', p) < 0)
01291         return (-1);
01292       if ((*putc_cb)('a', p) < 0)
01293         return (-1);
01294       if ((*putc_cb)('m', p) < 0)
01295         return (-1);
01296       if ((*putc_cb)('p', p) < 0)
01297         return (-1);
01298       if ((*putc_cb)(';', p) < 0)
01299         return (-1);
01300     }
01301     else if (*s == '<')
01302     {
01303       if ((*putc_cb)('&', p) < 0)
01304         return (-1);
01305       if ((*putc_cb)('l', p) < 0)
01306         return (-1);
01307       if ((*putc_cb)('t', p) < 0)
01308         return (-1);
01309       if ((*putc_cb)(';', p) < 0)
01310         return (-1);
01311     }
01312     else if (*s == '>')
01313     {
01314       if ((*putc_cb)('&', p) < 0)
01315         return (-1);
01316       if ((*putc_cb)('g', p) < 0)
01317         return (-1);
01318       if ((*putc_cb)('t', p) < 0)
01319         return (-1);
01320       if ((*putc_cb)(';', p) < 0)
01321         return (-1);
01322     }
01323     else if (*s == '\"')
01324     {
01325       if ((*putc_cb)('&', p) < 0)
01326         return (-1);
01327       if ((*putc_cb)('q', p) < 0)
01328         return (-1);
01329       if ((*putc_cb)('u', p) < 0)
01330         return (-1);
01331       if ((*putc_cb)('o', p) < 0)
01332         return (-1);
01333       if ((*putc_cb)('t', p) < 0)
01334         return (-1);
01335       if ((*putc_cb)(';', p) < 0)
01336         return (-1);
01337     }
01338     else if (*s & 128)
01339     {
01340      /*
01341       * Convert UTF-8 to Unicode constant...
01342       */
01343 
01344       int       ch;                     /* Unicode character */
01345 
01346 
01347       ch = *s & 255;
01348 
01349       if ((ch & 0xe0) == 0xc0)
01350       {
01351         ch = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
01352         s ++;
01353       }
01354       else if ((ch & 0xf0) == 0xe0)
01355       {
01356         ch = ((((ch * 0x0f) << 6) | (s[1] & 0x3f)) << 6) | (s[2] & 0x3f);
01357         s += 2;
01358       }
01359 
01360       if (ch == 0xa0)
01361       {
01362        /*
01363         * Handle non-breaking space as-is...
01364         */
01365 
01366         if ((*putc_cb)('&', p) < 0)
01367           return (-1);
01368         if ((*putc_cb)('n', p) < 0)
01369           return (-1);
01370         if ((*putc_cb)('b', p) < 0)
01371           return (-1);
01372         if ((*putc_cb)('s', p) < 0)
01373           return (-1);
01374         if ((*putc_cb)('p', p) < 0)
01375           return (-1);
01376         if ((*putc_cb)(';', p) < 0)
01377           return (-1);
01378       }
01379       else
01380       {
01381         sprintf(buf, "&#x%x;", ch);
01382 
01383         for (bufptr = buf; *bufptr; bufptr ++)
01384           if ((*putc_cb)(*bufptr, p) < 0)
01385             return (-1);
01386       }
01387     }
01388     else if ((*putc_cb)(*s, p) < 0)
01389       return (-1);
01390 
01391     s ++;
01392   }
01393 
01394   return (0);
01395 }
01396 
01397 
01398 /*
01399  * 'mxml_write_ws()' - Do whitespace callback...
01400  */
01401 
01402 static int                              /* O - New column */
01403 mxml_write_ws(stp_mxml_node_t *node,    /* I - Current node */
01404               void        *p,           /* I - Write pointer */
01405               int         (*cb)(stp_mxml_node_t *, int),
01406                                         /* I - Callback function */
01407               int         ws,           /* I - Where value */
01408               int         col,          /* I - Current column */
01409               int         (*putc_cb)(int, void *))
01410                                         /* I - Write callback */
01411 {
01412   int   ch;                             /* Whitespace character */
01413 
01414 
01415   if (cb && (ch = (*cb)(node, ws)) != 0)
01416   {
01417     if ((*putc_cb)(ch, p) < 0)
01418       return (-1);
01419     else if (ch == '\n')
01420       col = 0;
01421     else if (ch == '\t')
01422     {
01423       col += STP_MXML_TAB;
01424       col = col - (col % STP_MXML_TAB);
01425     }
01426     else
01427       col ++;
01428   }
01429 
01430   return (col);
01431 }
01432 
01433 
01434 /*
01435  * End of "$Id: mxml-file.c,v 1.6 2004/04/27 23:23:47 rlk Exp $".
01436  */

Generated on Wed May 12 20:21:29 2004 for libgimpprint API Reference by doxygen1.2.17