Load cups into easysw/current.
[thirdparty/cups.git] / filter / form-tree.c
1 /*
2  * "$Id: form-tree.c 4494 2005-02-18 02:18:11Z mike $"
3  *
4  *   CUPS form document tree routines for the Common UNIX Printing
5  *   System (CUPS).
6  *
7  *   Copyright 1997-2005 by Easy Software Products.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Easy Software Products and are protected by Federal
11  *   copyright law.  Distribution and use rights are outlined in the file
12  *   "LICENSE.txt" which should have been included with this file.  If this
13  *   file is missing or damaged please contact Easy Software Products
14  *   at:
15  *
16  *       Attn: CUPS Licensing Information
17  *       Easy Software Products
18  *       44141 Airport View Drive, Suite 204
19  *       Hollywood, Maryland 20636 USA
20  *
21  *       Voice: (301) 373-9600
22  *       EMail: cups-info@cups.org
23  *         WWW: http://www.cups.org
24  *
25  *   This file is subject to the Apple OS-Developed Software exception.
26  *
27  * Contents:
28  *
29  */
30
31 /*
32  * Include necessary headers...
33  */
34
35 #include "form.h"
36
37
38 /*
39  * Local functions...
40  */
41
42 static int      compare_attr(attr_t *a0, attr_t *a1);
43 static int      compare_elements(char **e0, char **e1);
44 static int      parse_attr(tree_t *t, FILE *fp);
45 static int      parse_element(tree_t *t, FILE *fp);
46
47
48 /*
49  * Local globals...
50  */
51
52 static char     *elements[] =
53                 {
54                   "",
55                   "!--",
56                   "ARC",
57                   "BOX",
58                   "BR",
59                   "B",
60                   "CUPSFORM",
61                   "DEFVAR",
62                   "FONT",
63                   "H1",
64                   "H2",
65                   "H3",
66                   "H4",
67                   "H5",
68                   "H6",
69                   "HEAD",
70                   "IMG",
71                   "I",
72                   "LINE",
73                   "PAGE",
74                   "PIE",
75                   "POLY",
76                   "PRE",
77                   "P",
78                   "RECT",
79                   "TEXT",
80                   "TT",
81                   "VAR"
82                 };
83
84
85 /*
86  * 'formDelete()' - Delete a node and its children.
87  */
88
89 void
90 formDelete(tree_t *t)                   /* I - Tree node */
91 {
92 }
93
94
95 /*
96  * 'formGetAttr()' - Get a node attribute value.
97  */
98
99 char *                                  /* O - Value or NULL */
100 formGetAttr(tree_t     *t,              /* I - Tree node */
101             const char *name)           /* I - Name of attribute */
102 {
103 }
104
105
106 /*
107  * 'formNew()' - Create a new form node.
108  */
109
110 tree_t *                                /* O - New tree node */
111 formNew(tree_t    *p)                   /* I - Parent node */
112 {
113   tree_t        *t;                     /* New tree node */
114
115
116  /*
117   * Allocate the new node...
118   */
119
120   if ((t = (tree_t *)calloc(sizeof(tree_t), 1)) == NULL)
121     return (NULL);
122
123  /*
124   * Set/copy attributes...
125   */
126
127   if (p == NULL)
128   {
129     t->bg[0]    = 1.0;
130     t->bg[1]    = 1.0;
131     t->bg[2]    = 1.0;
132     t->halign   = HALIGN_LEFT;
133     t->valign   = VALIGN_MIDDLE;
134     t->typeface = "Courier";
135     t->size     = 12.0;
136   }
137   else
138   {
139     memcpy(t, p, sizeof(tree_t));
140
141     t->prev       = NULL;
142     t->next       = NULL;
143     t->child      = NULL;
144     t->last_child = NULL;
145     t->parent     = NULL;
146     t->num_attrs  = 0;
147     t->attrs      = NULL;
148     t->data       = NULL;
149   }
150
151  /*
152   * Return the new node...
153   */
154
155   return (t);
156 }
157
158
159 /*
160  * 'formRead()' - Read a form tree from a file.
161  */
162
163 tree_t *                                /* O - New form tree */
164 formRead(FILE   *fp,                    /* I - File to read from */
165          tree_t *p)                     /* I - Parent node */
166 {
167   int           ch,                     /* Character from file */
168                 closech,                /* Closing character */
169                 have_whitespace;        /* Leading whitespace? */
170   static char   s[10240];               /* String from file */
171   uchar         *ptr,                   /* Pointer in string */
172                 glyph[16],              /* Glyph name (&#nnn;) */
173                 *glyphptr;              /* Pointer in glyph string */
174   tree_t        *tree,                  /* "top" of this tree */
175                 *t,                     /* New tree node */
176                 *prev,                  /* Previous tree node */
177                 *temp;                  /* Temporary looping var */
178   uchar         *face,                  /* Typeface for FONT tag */
179                 *color,                 /* Color for FONT tag */
180                 *size;                  /* Size for FONT tag */
181
182
183  /*
184   * Start off with no previous tree node...
185   */
186
187   prev = NULL;
188   tree = NULL;
189
190  /*
191   * Parse data until we hit end-of-file...
192   */
193
194   while ((ch = getc(fp)) != EOF)
195   {
196    /*
197     * Ignore leading whitespace...
198     */
199
200     have_whitespace = 0;
201     closech         = '/';
202
203     if (p == NULL || !p->preformatted)
204     {
205       while (isspace(ch & 255))
206       {
207         have_whitespace = 1;
208         ch              = getc(fp);
209       }
210
211       if (ch == EOF)
212         break;
213     }
214
215    /*
216     * Allocate a new tree node - use calloc() to get zeroed data...
217     */
218
219     t = formNew(p);
220
221    /*
222     * See what the character was...
223     */
224
225     if (ch == '<')
226     {
227      /*
228       * Markup char; grab the next char to see if this is a /...
229       */
230
231       ch = getc(fp);
232       if (ch == ' ')
233       {
234        /*
235         * Illegal lone "<"!  Ignore it...
236         */
237
238         free(t);
239         continue;
240       }
241       
242       if (ch != '/')
243         ungetc(ch, fp);
244
245       if (parse_element(t, fp) < 0)
246       {
247         free(t);
248         break;
249       }
250
251       if ((closech = getc(fp)) == '/')
252         getc(fp);
253
254      /*
255       * If this is the matching close mark, or if we are starting the same
256       * element, or if we've completed a list, we're done!
257       */
258
259       if (ch == '/')
260       {
261        /*
262         * Close element; find matching element...
263         */
264
265         for (temp = p; temp != NULL; temp = temp->p)
266           if (temp->element == t->element)
267             break;
268
269         free(t);
270
271         if (temp != NULL)
272           break;
273         else
274           continue;
275       }
276     }
277     else if (t->preformatted)
278     {
279      /*
280       * Read a pre-formatted string into the current tree node...
281       */
282
283       ptr = s;
284       while (ch != '<' && ch != EOF && ptr < (s + sizeof(s) - 1))
285       {
286         if (ch == '&')
287         {
288           for (glyphptr = glyph;
289                (ch = getc(fp)) != EOF && (glyphptr - glyph) < 15;
290                glyphptr ++)
291             if (!isalnum(ch & 255))
292               break;
293             else
294               *glyphptr = ch;
295
296           *glyphptr = '\0';
297           if (atoi(glyph) > 0)
298             ch = atoi(glyph);
299           else if (strcmp(glyph, "lt") == 0)
300             ch = '<';
301           else if (strcmp(glyph, "gt") == 0)
302             ch = '>';
303           else if (strcmp(glyph, "quot") == 0)
304             ch = '\'';
305           else if (strcmp(glyph, "nbsp") == 0)
306             ch = ' ';
307           else
308             ch = '&';
309         }
310
311         if (ch != 0)
312           *ptr++ = ch;
313
314         if (ch == '\n')
315           break;
316
317         ch = getc(fp);
318       }
319
320       *ptr = '\0';
321
322       if (ch == '<')
323         ungetc(ch, fp);
324
325       t->element = ELEMENT_FRAGMENT;
326       t->data    = strdup(s);
327     }
328     else
329     {
330      /*
331       * Read the next string fragment...
332       */
333
334       ptr = s;
335       if (have_whitespace)
336         *ptr++ = ' ';
337
338       while (!isspace(ch & 255) && ch != '<' && ch != EOF && ptr < (s + sizeof(s) - 1))
339       {
340         if (ch == '&')
341         {
342           for (glyphptr = glyph;
343                (ch = getc(fp)) != EOF && (glyphptr - glyph) < 15;
344                glyphptr ++)
345             if (!isalnum(ch & 255))
346               break;
347             else
348               *glyphptr = ch;
349
350           *glyphptr = '\0';
351           if (atoi(glyph) > 0)
352             ch = atoi(glyph);
353           else if (strcmp(glyph, "lt") == 0)
354             ch = '<';
355           else if (strcmp(glyph, "gt") == 0)
356             ch = '>';
357           else if (strcmp(glyph, "quot") == 0)
358             ch = '\'';
359           else if (strcmp(glyph, "nbsp") == 0)
360             ch = ' ';
361           else
362             ch = '&';
363         }
364
365         if (ch != 0)
366           *ptr++ = ch;
367
368         ch = getc(fp);
369       }
370
371       if (isspace(ch & 255))
372         *ptr++ = ' ';
373
374       *ptr = '\0';
375
376       if (ch == '<')
377         ungetc(ch, fp);
378
379       t->element = ELEMENT_FRAGMENT;
380       t->data    = strdup(s);
381     }
382
383    /*
384     * If the p tree pointer is not NULL and this is the first
385     * entry we've read, set the child pointer...
386     */
387
388     if (p != NULL && prev == NULL)
389       p->child = t;
390
391     if (p != NULL)
392       p->last_child = t;
393
394    /*
395     * Do the prev/next links...
396     */
397
398     t->parent = p;
399     t->prev   = prev;
400     if (prev != NULL)
401       prev->next = t;
402     else
403       tree = t;
404
405     prev = t;
406
407    /*
408     * Do child stuff as needed...
409     */
410
411     if (closech == '>')
412       t->child = formRead(t, fp);
413   }  
414
415   return (tree);
416 }
417
418
419 /*
420  * 'formSetAttr()' - Set a node attribute.
421  */
422
423 void
424 formSetAttr(tree_t     *t,              /* I - Tree node */
425             const char *name,           /* I - Attribute name */
426             const char *value)          /* I - Attribute value */
427 {
428 }
429
430
431 /*
432  * 'compare_attr()' - Compare two attributes.
433  */
434
435 static int                              /* O - -1 if a0 < a1, etc. */
436 compare_attr(attr_t *a0,                /* I - First attribute */
437              attr_t *a1)                /* I - Second attribute */
438 {
439   return (strcasecmp(a0->name, a1->name));
440 }
441
442
443 /*
444  * 'compare_elements()' - Compare two elements.
445  */
446
447 static int                              /* O - -1 if e0 < e1, etc. */
448 compare_elements(char **e0,             /* I - First element */
449                  char **e1)             /* I - Second element */
450 {
451   return (strcasecmp(*e0, *e1));
452 }
453
454
455 /*
456  * 'parse_attr()' - Parse an element attribute string.
457  */
458
459 static int                              /* O - -1 on error, 0 on success */
460 parse_attr(tree_t *t,                   /* I - Current tree node */
461            FILE   *fp)                  /* I - Input file */
462 {
463   char  name[1024],                     /* Name of attr */
464         value[10240],                   /* Value of attr */
465         *ptr;                           /* Temporary pointer */
466   int   ch;                             /* Character from file */
467
468
469   ptr = name;
470   while ((ch = getc(fp)) != EOF)
471     if (isalnum(ch & 255))
472     {
473       if (ptr < (name + sizeof(name) - 1))
474         *ptr++ = ch;
475     }
476     else
477       break;
478
479   *ptr = '\0';
480
481   while (isspace(ch & 255) || ch == '\r')
482     ch = getc(fp);
483
484   switch (ch)
485   {
486     default :
487         ungetc(ch, fp);
488         return (formSetAttr(t, name, NULL));
489     case EOF :
490         return (-1);
491     case '=' :
492         ptr = value;
493         ch  = getc(fp);
494
495         while (isspace(ch & 255) || ch == '\r')
496           ch = getc(fp);
497
498         if (ch == EOF)
499           return (-1);
500
501         if (ch == '\'')
502         {
503           while ((ch = getc(fp)) != EOF)
504             if (ch == '\'')
505               break;
506             else if (ptr < (value + sizeof(value) - 1))
507               *ptr++ = ch;
508
509           *ptr = '\0';
510         }
511         else if (ch == '\"')
512         {
513           while ((ch = getc(fp)) != EOF)
514             if (ch == '\"')
515               break;
516             else if (ptr < (value + sizeof(value) - 1))
517               *ptr++ = ch;
518
519           *ptr = '\0';
520         }
521         else
522         {
523           *ptr++ = ch;
524           while ((ch = getc(fp)) != EOF)
525             if (isspace(ch & 255) || ch == '>' || ch == '/' || ch == '\r')
526               break;
527             else if (ptr < (value + sizeof(value) - 1))
528               *ptr++ = ch;
529
530           *ptr = '\0';
531           if (ch == '>' || ch == '/')
532             ungetc(ch, fp);
533         }
534
535         return (formSetAttr(t, name, value));
536   }
537 }
538
539
540 /*
541  * 'parse_element()' - Parse an element.
542  */
543
544 static int                              /* O - -1 on error or ELEMENT_nnnn */
545 parse_element(tree_t *t,                /* I - Current tree node */
546               FILE   *fp)               /* I - Input file */
547 {
548   int   ch;                             /* Character from file */
549   char  element[255],                   /* Element string... */
550         *eptr,                          /* Current character... */
551         comment[10240],                 /* Comment string */
552         *cptr,                          /* Current char... */
553         **temp;                         /* Element variable entry */
554
555
556   eptr = element;
557
558   while ((ch = getc(fp)) != EOF && eptr < (element + sizeof(element) - 1))
559     if (ch == '>' || ch == '/' || isspace(ch & 255))
560       break;
561     else
562       *eptr++ = ch;
563
564   *eptr = '\0';
565
566   if (ch == EOF)
567     return (ELEMENT_ERROR);
568
569   eptr = element;
570   temp = bsearch(&mptr, elements, sizeof(elements) / sizeof(elements[0]),
571                  sizeof(elements[0]),
572                  (int (*)(const void *, const void *))compare_elements);
573
574   if (temp == NULL)
575   {
576    /*
577     * Unrecognized element stuff...
578     */
579
580     t->element = ELEMENT_COMMENT;
581     strcpy(comment, element);
582     cptr = comment + strlen(comment);
583   }
584   else
585   {
586     t->element = (element_t)((char **)temp - elements);
587     cptr       = comment;
588   }
589
590   if (t->element == ELEMENT_COMMENT)
591   {
592     while (ch != EOF && ch != '>' && cptr < (comment + sizeof(comment) - 1))
593     {
594       *cptr++ = ch;
595       ch = getc(fp);
596     }
597
598     *cptr   = '\0';
599     t->data = strdup(comment);
600   }
601   else
602   {
603     while (ch != EOF && ch != '>' && ch != '/')
604     {
605       if (!isspace(ch & 255))
606       {
607         ungetc(ch, fp);
608         parse_variable(t, fp);
609       }
610
611       ch = getc(fp);
612     }
613
614     if (ch != EOF)
615       ungetc(ch, fp);
616   }
617
618   return (t->element);
619 }
620
621
622 /*
623  * End of "$Id: form-tree.c 4494 2005-02-18 02:18:11Z mike $".
624  */