• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * IPP data file parsing functions.
3  *
4  * Copyright © 2007-2019 by Apple Inc.
5  * Copyright © 1997-2007 by Easy Software Products.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include "ipp-private.h"
16 #include "string-private.h"
17 #include "debug-internal.h"
18 
19 
20 /*
21  * Local functions...
22  */
23 
24 static ipp_t	*parse_collection(_ipp_file_t *f, _ipp_vars_t *v, void *user_data);
25 static int	parse_value(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, ipp_t *ipp, ipp_attribute_t **attr, int element);
26 static void	report_error(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, const char *message, ...) _CUPS_FORMAT(4, 5);
27 
28 
29 /*
30  * '_ippFileParse()' - Parse an IPP data file.
31  */
32 
33 ipp_t *					/* O - IPP attributes or @code NULL@ on failure */
_ippFileParse(_ipp_vars_t * v,const char * filename,void * user_data)34 _ippFileParse(
35     _ipp_vars_t      *v,		/* I - Variables */
36     const char       *filename,		/* I - Name of file to parse */
37     void             *user_data)	/* I - User data pointer */
38 {
39   _ipp_file_t	f;			/* IPP data file information */
40   ipp_t		*attrs = NULL;		/* Active IPP message */
41   ipp_attribute_t *attr = NULL;		/* Current attribute */
42   char		token[1024];		/* Token string */
43   ipp_t		*ignored = NULL;	/* Ignored attributes */
44 
45 
46   DEBUG_printf(("_ippFileParse(v=%p, filename=\"%s\", user_data=%p)", (void *)v, filename, user_data));
47 
48  /*
49   * Initialize file info...
50   */
51 
52   memset(&f, 0, sizeof(f));
53   f.filename = filename;
54   f.linenum  = 1;
55 
56   if ((f.fp = cupsFileOpen(filename, "r")) == NULL)
57   {
58     DEBUG_printf(("1_ippFileParse: Unable to open \"%s\": %s", filename, strerror(errno)));
59     return (0);
60   }
61 
62  /*
63   * Do the callback with a NULL token to setup any initial state...
64   */
65 
66   (*v->tokencb)(&f, v, user_data, NULL);
67 
68  /*
69   * Read data file, using the callback function as needed...
70   */
71 
72   while (_ippFileReadToken(&f, token, sizeof(token)))
73   {
74     if (!_cups_strcasecmp(token, "DEFINE") || !_cups_strcasecmp(token, "DEFINE-DEFAULT"))
75     {
76       char	name[128],		/* Variable name */
77 		value[1024],		/* Variable value */
78 		temp[1024];		/* Temporary string */
79 
80       attr = NULL;
81 
82       if (_ippFileReadToken(&f, name, sizeof(name)) && _ippFileReadToken(&f, temp, sizeof(temp)))
83       {
84         if (_cups_strcasecmp(token, "DEFINE-DEFAULT") || !_ippVarsGet(v, name))
85         {
86 	  _ippVarsExpand(v, value, temp, sizeof(value));
87 	  _ippVarsSet(v, name, value);
88 	}
89       }
90       else
91       {
92         report_error(&f, v, user_data, "Missing %s name and/or value on line %d of \"%s\".", token, f.linenum, f.filename);
93         break;
94       }
95     }
96     else if (f.attrs && !_cups_strcasecmp(token, "ATTR"))
97     {
98      /*
99       * Attribute definition...
100       */
101 
102       char	syntax[128],		/* Attribute syntax (value tag) */
103 		name[128];		/* Attribute name */
104       ipp_tag_t	value_tag;		/* Value tag */
105 
106       attr = NULL;
107 
108       if (!_ippFileReadToken(&f, syntax, sizeof(syntax)))
109       {
110         report_error(&f, v, user_data, "Missing ATTR syntax on line %d of \"%s\".", f.linenum, f.filename);
111 	break;
112       }
113       else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
114       {
115         report_error(&f, v, user_data, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax, f.linenum, f.filename);
116 	break;
117       }
118 
119       if (!_ippFileReadToken(&f, name, sizeof(name)) || !name[0])
120       {
121         report_error(&f, v, user_data, "Missing ATTR name on line %d of \"%s\".", f.linenum, f.filename);
122 	break;
123       }
124 
125       if (!v->attrcb || (*v->attrcb)(&f, user_data, name))
126       {
127        /*
128         * Add this attribute...
129         */
130 
131         attrs = f.attrs;
132       }
133       else
134       {
135        /*
136         * Ignore this attribute...
137         */
138 
139         if (!ignored)
140           ignored = ippNew();
141 
142         attrs = ignored;
143       }
144 
145       if (value_tag < IPP_TAG_INTEGER)
146       {
147        /*
148 	* Add out-of-band attribute - no value string needed...
149 	*/
150 
151         ippAddOutOfBand(attrs, f.group_tag, value_tag, name);
152       }
153       else
154       {
155        /*
156         * Add attribute with one or more values...
157         */
158 
159         attr = ippAddString(attrs, f.group_tag, value_tag, name, NULL, NULL);
160 
161         if (!parse_value(&f, v, user_data, attrs, &attr, 0))
162           break;
163       }
164 
165     }
166     else if (attr && !_cups_strcasecmp(token, ","))
167     {
168      /*
169       * Additional value...
170       */
171 
172       if (!parse_value(&f, v, user_data, attrs, &attr, ippGetCount(attr)))
173 	break;
174     }
175     else
176     {
177      /*
178       * Something else...
179       */
180 
181       attr  = NULL;
182       attrs = NULL;
183 
184       if (!(*v->tokencb)(&f, v, user_data, token))
185         break;
186     }
187   }
188 
189  /*
190   * Close the file and free ignored attributes, then return any attributes we
191   * kept...
192   */
193 
194   cupsFileClose(f.fp);
195   ippDelete(ignored);
196 
197   return (f.attrs);
198 }
199 
200 
201 /*
202  * '_ippFileReadToken()' - Read a token from an IPP data file.
203  */
204 
205 int					/* O - 1 on success, 0 on failure */
_ippFileReadToken(_ipp_file_t * f,char * token,size_t tokensize)206 _ippFileReadToken(_ipp_file_t *f,	/* I - File to read from */
207                   char        *token,	/* I - Token string buffer */
208                   size_t      tokensize)/* I - Size of token string buffer */
209 {
210   int	ch,				/* Character from file */
211 	quote = 0;			/* Quoting character */
212   char	*tokptr = token,		/* Pointer into token buffer */
213 	*tokend = token + tokensize - 1;/* End of token buffer */
214 
215 
216  /*
217   * Skip whitespace and comments...
218   */
219 
220   DEBUG_printf(("1_ippFileReadToken: linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
221 
222   while ((ch = cupsFileGetChar(f->fp)) != EOF)
223   {
224     if (_cups_isspace(ch))
225     {
226      /*
227       * Whitespace...
228       */
229 
230       if (ch == '\n')
231       {
232         f->linenum ++;
233         DEBUG_printf(("1_ippFileReadToken: LF in leading whitespace, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
234       }
235     }
236     else if (ch == '#')
237     {
238      /*
239       * Comment...
240       */
241 
242       DEBUG_puts("1_ippFileReadToken: Skipping comment in leading whitespace...");
243 
244       while ((ch = cupsFileGetChar(f->fp)) != EOF)
245       {
246         if (ch == '\n')
247           break;
248       }
249 
250       if (ch == '\n')
251       {
252         f->linenum ++;
253         DEBUG_printf(("1_ippFileReadToken: LF at end of comment, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
254       }
255       else
256         break;
257     }
258     else
259       break;
260   }
261 
262   if (ch == EOF)
263   {
264     DEBUG_puts("1_ippFileReadToken: EOF");
265     return (0);
266   }
267 
268  /*
269   * Read a token...
270   */
271 
272   while (ch != EOF)
273   {
274     if (ch == '\n')
275     {
276       f->linenum ++;
277       DEBUG_printf(("1_ippFileReadToken: LF in token, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
278     }
279 
280     if (ch == quote)
281     {
282      /*
283       * End of quoted text...
284       */
285 
286       *tokptr = '\0';
287       DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at closing quote.", token));
288       return (1);
289     }
290     else if (!quote && _cups_isspace(ch))
291     {
292      /*
293       * End of unquoted text...
294       */
295 
296       *tokptr = '\0';
297       DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before whitespace.", token));
298       return (1);
299     }
300     else if (!quote && (ch == '\'' || ch == '\"'))
301     {
302      /*
303       * Start of quoted text or regular expression...
304       */
305 
306       if (ch == '<')
307         quote = '>';
308       else
309         quote = ch;
310 
311       DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote, (long)cupsFileTell(f->fp)));
312     }
313     else if (!quote && ch == '#')
314     {
315      /*
316       * Start of comment...
317       */
318 
319       cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
320       *tokptr = '\0';
321       DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before comment.", token));
322       return (1);
323     }
324     else if (!quote && (ch == '{' || ch == '}' || ch == ','))
325     {
326      /*
327       * Delimiter...
328       */
329 
330       if (tokptr > token)
331       {
332        /*
333         * Return the preceding token first...
334         */
335 
336 	cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
337       }
338       else
339       {
340        /*
341         * Return this delimiter by itself...
342         */
343 
344         *tokptr++ = (char)ch;
345       }
346 
347       *tokptr = '\0';
348       DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
349       return (1);
350     }
351     else
352     {
353       if (ch == '\\')
354       {
355        /*
356         * Quoted character...
357         */
358 
359         DEBUG_printf(("1_ippFileReadToken: Quoted character at pos=%ld", (long)cupsFileTell(f->fp)));
360 
361         if ((ch = cupsFileGetChar(f->fp)) == EOF)
362         {
363 	  *token = '\0';
364 	  DEBUG_puts("1_ippFileReadToken: EOF");
365 	  return (0);
366 	}
367 	else if (ch == '\n')
368 	{
369 	  f->linenum ++;
370 	  DEBUG_printf(("1_ippFileReadToken: quoted LF, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
371 	}
372 	else if (ch == 'a')
373 	  ch = '\a';
374 	else if (ch == 'b')
375 	  ch = '\b';
376 	else if (ch == 'f')
377 	  ch = '\f';
378 	else if (ch == 'n')
379 	  ch = '\n';
380 	else if (ch == 'r')
381 	  ch = '\r';
382 	else if (ch == 't')
383 	  ch = '\t';
384 	else if (ch == 'v')
385 	  ch = '\v';
386       }
387 
388       if (tokptr < tokend)
389       {
390        /*
391 	* Add to current token...
392 	*/
393 
394 	*tokptr++ = (char)ch;
395       }
396       else
397       {
398        /*
399 	* Token too long...
400 	*/
401 
402 	*tokptr = '\0';
403 	DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token));
404 	return (0);
405       }
406     }
407 
408    /*
409     * Get the next character...
410     */
411 
412     ch = cupsFileGetChar(f->fp);
413   }
414 
415   *tokptr = '\0';
416   DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token));
417 
418   return (tokptr > token);
419 }
420 
421 
422 /*
423  * 'parse_collection()' - Parse an IPP collection value.
424  */
425 
426 static ipp_t *				/* O - Collection value or @code NULL@ on error */
parse_collection(_ipp_file_t * f,_ipp_vars_t * v,void * user_data)427 parse_collection(
428     _ipp_file_t      *f,		/* I - IPP data file */
429     _ipp_vars_t      *v,		/* I - IPP variables */
430     void             *user_data)	/* I - User data pointer */
431 {
432   ipp_t		*col = ippNew();	/* Collection value */
433   ipp_attribute_t *attr = NULL;		/* Current member attribute */
434   char		token[1024];		/* Token string */
435 
436 
437  /*
438   * Parse the collection value...
439   */
440 
441   while (_ippFileReadToken(f, token, sizeof(token)))
442   {
443     if (!_cups_strcasecmp(token, "}"))
444     {
445      /*
446       * End of collection value...
447       */
448 
449       break;
450     }
451     else if (!_cups_strcasecmp(token, "MEMBER"))
452     {
453      /*
454       * Member attribute definition...
455       */
456 
457       char	syntax[128],		/* Attribute syntax (value tag) */
458 		name[128];		/* Attribute name */
459       ipp_tag_t	value_tag;		/* Value tag */
460 
461       attr = NULL;
462 
463       if (!_ippFileReadToken(f, syntax, sizeof(syntax)))
464       {
465         report_error(f, v, user_data, "Missing MEMBER syntax on line %d of \"%s\".", f->linenum, f->filename);
466 	ippDelete(col);
467 	col = NULL;
468 	break;
469       }
470       else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
471       {
472         report_error(f, v, user_data, "Bad MEMBER syntax \"%s\" on line %d of \"%s\".", syntax, f->linenum, f->filename);
473 	ippDelete(col);
474 	col = NULL;
475 	break;
476       }
477 
478       if (!_ippFileReadToken(f, name, sizeof(name)) || !name[0])
479       {
480         report_error(f, v, user_data, "Missing MEMBER name on line %d of \"%s\".", f->linenum, f->filename);
481 	ippDelete(col);
482 	col = NULL;
483 	break;
484       }
485 
486       if (value_tag < IPP_TAG_INTEGER)
487       {
488        /*
489 	* Add out-of-band attribute - no value string needed...
490 	*/
491 
492         ippAddOutOfBand(col, IPP_TAG_ZERO, value_tag, name);
493       }
494       else
495       {
496        /*
497         * Add attribute with one or more values...
498         */
499 
500         attr = ippAddString(col, IPP_TAG_ZERO, value_tag, name, NULL, NULL);
501 
502         if (!parse_value(f, v, user_data, col, &attr, 0))
503         {
504 	  ippDelete(col);
505 	  col = NULL;
506           break;
507 	}
508       }
509 
510     }
511     else if (attr && !_cups_strcasecmp(token, ","))
512     {
513      /*
514       * Additional value...
515       */
516 
517       if (!parse_value(f, v, user_data, col, &attr, ippGetCount(attr)))
518       {
519 	ippDelete(col);
520 	col = NULL;
521 	break;
522       }
523     }
524     else
525     {
526      /*
527       * Something else...
528       */
529 
530       report_error(f, v, user_data, "Unknown directive \"%s\" on line %d of \"%s\".", token, f->linenum, f->filename);
531       ippDelete(col);
532       col  = NULL;
533       attr = NULL;
534       break;
535 
536     }
537   }
538 
539   return (col);
540 }
541 
542 
543 /*
544  * 'parse_value()' - Parse an IPP value.
545  */
546 
547 static int				/* O  - 1 on success or 0 on error */
parse_value(_ipp_file_t * f,_ipp_vars_t * v,void * user_data,ipp_t * ipp,ipp_attribute_t ** attr,int element)548 parse_value(_ipp_file_t      *f,	/* I  - IPP data file */
549             _ipp_vars_t      *v,	/* I  - IPP variables */
550             void             *user_data,/* I  - User data pointer */
551             ipp_t            *ipp,	/* I  - IPP message */
552             ipp_attribute_t  **attr,	/* IO - IPP attribute */
553             int              element)	/* I  - Element number */
554 {
555   char		value[2049],		/* Value string */
556 		*valueptr,		/* Pointer into value string */
557 		temp[2049],		/* Temporary string */
558 		*tempptr;		/* Pointer into temporary string */
559   size_t	valuelen;		/* Length of value */
560 
561 
562   if (!_ippFileReadToken(f, temp, sizeof(temp)))
563   {
564     report_error(f, v, user_data, "Missing value on line %d of \"%s\".", f->linenum, f->filename);
565     return (0);
566   }
567 
568   _ippVarsExpand(v, value, temp, sizeof(value));
569 
570   switch (ippGetValueTag(*attr))
571   {
572     case IPP_TAG_BOOLEAN :
573         return (ippSetBoolean(ipp, attr, element, !_cups_strcasecmp(value, "true")));
574         break;
575 
576     case IPP_TAG_ENUM :
577     case IPP_TAG_INTEGER :
578         return (ippSetInteger(ipp, attr, element, (int)strtol(value, NULL, 0)));
579         break;
580 
581     case IPP_TAG_DATE :
582         {
583           int	year,			/* Year */
584 		month,			/* Month */
585 		day,			/* Day of month */
586 		hour,			/* Hour */
587 		minute,			/* Minute */
588 		second,			/* Second */
589 		utc_offset = 0;		/* Timezone offset from UTC */
590           ipp_uchar_t date[11];		/* dateTime value */
591 
592           if (*value == 'P')
593           {
594            /*
595             * Time period...
596             */
597 
598             time_t	curtime;	/* Current time in seconds */
599             int		period = 0,	/* Current period value */
600 			saw_T = 0;	/* Saw time separator */
601 
602             curtime = time(NULL);
603 
604             for (valueptr = value + 1; *valueptr; valueptr ++)
605             {
606               if (isdigit(*valueptr & 255))
607               {
608                 period = (int)strtol(valueptr, &valueptr, 10);
609 
610                 if (!valueptr || period < 0)
611                 {
612 		  report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
613 		  return (0);
614 		}
615               }
616 
617               if (*valueptr == 'Y')
618               {
619                 curtime += 365 * 86400 * period;
620                 period  = 0;
621               }
622               else if (*valueptr == 'M')
623               {
624                 if (saw_T)
625                   curtime += 60 * period;
626                 else
627                   curtime += 30 * 86400 * period;
628 
629                 period = 0;
630               }
631               else if (*valueptr == 'D')
632               {
633                 curtime += 86400 * period;
634                 period  = 0;
635               }
636               else if (*valueptr == 'H')
637               {
638                 curtime += 3600 * period;
639                 period  = 0;
640               }
641               else if (*valueptr == 'S')
642               {
643                 curtime += period;
644                 period = 0;
645               }
646               else if (*valueptr == 'T')
647               {
648                 saw_T  = 1;
649                 period = 0;
650               }
651               else
652 	      {
653 		report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
654 		return (0);
655 	      }
656 	    }
657 
658 	    return (ippSetDate(ipp, attr, element, ippTimeToDate(curtime)));
659           }
660           else if (sscanf(value, "%d-%d-%dT%d:%d:%d%d", &year, &month, &day, &hour, &minute, &second, &utc_offset) < 6)
661           {
662            /*
663             * Date/time value did not parse...
664             */
665 
666 	    report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
667 	    return (0);
668           }
669 
670           date[0] = (ipp_uchar_t)(year >> 8);
671           date[1] = (ipp_uchar_t)(year & 255);
672           date[2] = (ipp_uchar_t)month;
673           date[3] = (ipp_uchar_t)day;
674           date[4] = (ipp_uchar_t)hour;
675           date[5] = (ipp_uchar_t)minute;
676           date[6] = (ipp_uchar_t)second;
677           date[7] = 0;
678           if (utc_offset < 0)
679           {
680             utc_offset = -utc_offset;
681             date[8]    = (ipp_uchar_t)'-';
682 	  }
683 	  else
684 	  {
685             date[8] = (ipp_uchar_t)'+';
686 	  }
687 
688           date[9]  = (ipp_uchar_t)(utc_offset / 100);
689           date[10] = (ipp_uchar_t)(utc_offset % 100);
690 
691           return (ippSetDate(ipp, attr, element, date));
692         }
693         break;
694 
695     case IPP_TAG_RESOLUTION :
696 	{
697 	  int	xres,		/* X resolution */
698 		yres;		/* Y resolution */
699 	  char	*ptr;		/* Pointer into value */
700 
701 	  xres = yres = (int)strtol(value, (char **)&ptr, 10);
702 	  if (ptr > value && xres > 0)
703 	  {
704 	    if (*ptr == 'x')
705 	      yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
706 	  }
707 
708 	  if (ptr <= value || xres <= 0 || yres <= 0 || !ptr || (_cups_strcasecmp(ptr, "dpi") && _cups_strcasecmp(ptr, "dpc") && _cups_strcasecmp(ptr, "dpcm") && _cups_strcasecmp(ptr, "other")))
709 	  {
710 	    report_error(f, v, user_data, "Bad resolution value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
711 	    return (0);
712 	  }
713 
714 	  if (!_cups_strcasecmp(ptr, "dpi"))
715 	    return (ippSetResolution(ipp, attr, element, IPP_RES_PER_INCH, xres, yres));
716 	  else if (!_cups_strcasecmp(ptr, "dpc") || !_cups_strcasecmp(ptr, "dpcm"))
717 	    return (ippSetResolution(ipp, attr, element, IPP_RES_PER_CM, xres, yres));
718 	  else
719 	    return (ippSetResolution(ipp, attr, element, (ipp_res_t)0, xres, yres));
720 	}
721 	break;
722 
723     case IPP_TAG_RANGE :
724 	{
725 	  int	lower,			/* Lower value */
726 		upper;			/* Upper value */
727 
728           if (sscanf(value, "%d-%d", &lower, &upper) != 2)
729           {
730 	    report_error(f, v, user_data, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
731 	    return (0);
732 	  }
733 
734 	  return (ippSetRange(ipp, attr, element, lower, upper));
735 	}
736 	break;
737 
738     case IPP_TAG_STRING :
739         valuelen = strlen(value);
740 
741         if (value[0] == '<' && value[strlen(value) - 1] == '>')
742         {
743           if (valuelen & 1)
744           {
745 	    report_error(f, v, user_data, "Bad octetString value on line %d of \"%s\".", f->linenum, f->filename);
746 	    return (0);
747           }
748 
749           valueptr = value + 1;
750           tempptr  = temp;
751 
752           while (*valueptr && *valueptr != '>')
753           {
754 	    if (!isxdigit(valueptr[0] & 255) || !isxdigit(valueptr[1] & 255))
755 	    {
756 	      report_error(f, v, user_data, "Bad octetString value on line %d of \"%s\".", f->linenum, f->filename);
757 	      return (0);
758 	    }
759 
760             if (valueptr[0] >= '0' && valueptr[0] <= '9')
761               *tempptr = (char)((valueptr[0] - '0') << 4);
762 	    else
763               *tempptr = (char)((tolower(valueptr[0]) - 'a' + 10) << 4);
764 
765             if (valueptr[1] >= '0' && valueptr[1] <= '9')
766               *tempptr |= (valueptr[1] - '0');
767 	    else
768               *tempptr |= (tolower(valueptr[1]) - 'a' + 10);
769 
770             tempptr ++;
771           }
772 
773           return (ippSetOctetString(ipp, attr, element, temp, (int)(tempptr - temp)));
774         }
775         else
776           return (ippSetOctetString(ipp, attr, element, value, (int)valuelen));
777         break;
778 
779     case IPP_TAG_TEXTLANG :
780     case IPP_TAG_NAMELANG :
781     case IPP_TAG_TEXT :
782     case IPP_TAG_NAME :
783     case IPP_TAG_KEYWORD :
784     case IPP_TAG_URI :
785     case IPP_TAG_URISCHEME :
786     case IPP_TAG_CHARSET :
787     case IPP_TAG_LANGUAGE :
788     case IPP_TAG_MIMETYPE :
789         return (ippSetString(ipp, attr, element, value));
790         break;
791 
792     case IPP_TAG_BEGIN_COLLECTION :
793         {
794           int	status;			/* Add status */
795           ipp_t *col;			/* Collection value */
796 
797           if (strcmp(value, "{"))
798           {
799 	    report_error(f, v, user_data, "Bad collection value on line %d of \"%s\".", f->linenum, f->filename);
800 	    return (0);
801           }
802 
803           if ((col = parse_collection(f, v, user_data)) == NULL)
804             return (0);
805 
806 	  status = ippSetCollection(ipp, attr, element, col);
807 	  ippDelete(col);
808 
809 	  return (status);
810 	}
811 	break;
812 
813     default :
814         report_error(f, v, user_data, "Unsupported value on line %d of \"%s\".", f->linenum, f->filename);
815         return (0);
816   }
817 
818   return (1);
819 }
820 
821 
822 /*
823  * 'report_error()' - Report an error.
824  */
825 
826 static void
report_error(_ipp_file_t * f,_ipp_vars_t * v,void * user_data,const char * message,...)827 report_error(
828     _ipp_file_t *f,			/* I - IPP data file */
829     _ipp_vars_t *v,			/* I - Error callback function, if any */
830     void        *user_data,		/* I - User data pointer */
831     const char  *message,		/* I - Printf-style message */
832     ...)				/* I - Additional arguments as needed */
833 {
834   char		buffer[8192];		/* Formatted string */
835   va_list	ap;			/* Argument pointer */
836 
837 
838   va_start(ap, message);
839   vsnprintf(buffer, sizeof(buffer), message, ap);
840   va_end(ap);
841 
842   if (v->errorcb)
843     (*v->errorcb)(f, user_data, buffer);
844   else
845     fprintf(stderr, "%s\n", buffer);
846 }
847