libgimpprint API Reference: src/main/mxml-file.c Source File

Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

# src/main/mxml-file.c

Go to the documentation of this file.

    00001 /*
    00002  * "$Id: mxml-file_8c-source.html,v 1.1.1.2 2004/12/22 23:49:05 jlovell 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_8c-source.html,v 1.1.1.2 2004/12/22 23:49:05 jlovell Exp $".
    01436  */