xref: /aosp_15_r20/external/libcups/cups/ipp-file.c (revision 5e7646d21f1134fb0638875d812ef646c12ab91e)
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       quote = ch;
307 
308       DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote, (long)cupsFileTell(f->fp)));
309     }
310     else if (!quote && ch == '#')
311     {
312      /*
313       * Start of comment...
314       */
315 
316       cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
317       *tokptr = '\0';
318       DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before comment.", token));
319       return (1);
320     }
321     else if (!quote && (ch == '{' || ch == '}' || ch == ','))
322     {
323      /*
324       * Delimiter...
325       */
326 
327       if (tokptr > token)
328       {
329        /*
330         * Return the preceding token first...
331         */
332 
333 	cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
334       }
335       else
336       {
337        /*
338         * Return this delimiter by itself...
339         */
340 
341         *tokptr++ = (char)ch;
342       }
343 
344       *tokptr = '\0';
345       DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
346       return (1);
347     }
348     else
349     {
350       if (ch == '\\')
351       {
352        /*
353         * Quoted character...
354         */
355 
356         DEBUG_printf(("1_ippFileReadToken: Quoted character at pos=%ld", (long)cupsFileTell(f->fp)));
357 
358         if ((ch = cupsFileGetChar(f->fp)) == EOF)
359         {
360 	  *token = '\0';
361 	  DEBUG_puts("1_ippFileReadToken: EOF");
362 	  return (0);
363 	}
364 	else if (ch == '\n')
365 	{
366 	  f->linenum ++;
367 	  DEBUG_printf(("1_ippFileReadToken: quoted LF, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
368 	}
369 	else if (ch == 'a')
370 	  ch = '\a';
371 	else if (ch == 'b')
372 	  ch = '\b';
373 	else if (ch == 'f')
374 	  ch = '\f';
375 	else if (ch == 'n')
376 	  ch = '\n';
377 	else if (ch == 'r')
378 	  ch = '\r';
379 	else if (ch == 't')
380 	  ch = '\t';
381 	else if (ch == 'v')
382 	  ch = '\v';
383       }
384 
385       if (tokptr < tokend)
386       {
387        /*
388 	* Add to current token...
389 	*/
390 
391 	*tokptr++ = (char)ch;
392       }
393       else
394       {
395        /*
396 	* Token too long...
397 	*/
398 
399 	*tokptr = '\0';
400 	DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token));
401 	return (0);
402       }
403     }
404 
405    /*
406     * Get the next character...
407     */
408 
409     ch = cupsFileGetChar(f->fp);
410   }
411 
412   *tokptr = '\0';
413   DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token));
414 
415   return (tokptr > token);
416 }
417 
418 
419 /*
420  * 'parse_collection()' - Parse an IPP collection value.
421  */
422 
423 static ipp_t *				/* O - Collection value or @code NULL@ on error */
parse_collection(_ipp_file_t * f,_ipp_vars_t * v,void * user_data)424 parse_collection(
425     _ipp_file_t      *f,		/* I - IPP data file */
426     _ipp_vars_t      *v,		/* I - IPP variables */
427     void             *user_data)	/* I - User data pointer */
428 {
429   ipp_t		*col = ippNew();	/* Collection value */
430   ipp_attribute_t *attr = NULL;		/* Current member attribute */
431   char		token[1024];		/* Token string */
432 
433 
434  /*
435   * Parse the collection value...
436   */
437 
438   while (_ippFileReadToken(f, token, sizeof(token)))
439   {
440     if (!_cups_strcasecmp(token, "}"))
441     {
442      /*
443       * End of collection value...
444       */
445 
446       break;
447     }
448     else if (!_cups_strcasecmp(token, "MEMBER"))
449     {
450      /*
451       * Member attribute definition...
452       */
453 
454       char	syntax[128],		/* Attribute syntax (value tag) */
455 		name[128];		/* Attribute name */
456       ipp_tag_t	value_tag;		/* Value tag */
457 
458       attr = NULL;
459 
460       if (!_ippFileReadToken(f, syntax, sizeof(syntax)))
461       {
462         report_error(f, v, user_data, "Missing MEMBER syntax on line %d of \"%s\".", f->linenum, f->filename);
463 	ippDelete(col);
464 	col = NULL;
465 	break;
466       }
467       else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
468       {
469         report_error(f, v, user_data, "Bad MEMBER syntax \"%s\" on line %d of \"%s\".", syntax, f->linenum, f->filename);
470 	ippDelete(col);
471 	col = NULL;
472 	break;
473       }
474 
475       if (!_ippFileReadToken(f, name, sizeof(name)) || !name[0])
476       {
477         report_error(f, v, user_data, "Missing MEMBER name on line %d of \"%s\".", f->linenum, f->filename);
478 	ippDelete(col);
479 	col = NULL;
480 	break;
481       }
482 
483       if (value_tag < IPP_TAG_INTEGER)
484       {
485        /*
486 	* Add out-of-band attribute - no value string needed...
487 	*/
488 
489         ippAddOutOfBand(col, IPP_TAG_ZERO, value_tag, name);
490       }
491       else
492       {
493        /*
494         * Add attribute with one or more values...
495         */
496 
497         attr = ippAddString(col, IPP_TAG_ZERO, value_tag, name, NULL, NULL);
498 
499         if (!parse_value(f, v, user_data, col, &attr, 0))
500         {
501 	  ippDelete(col);
502 	  col = NULL;
503           break;
504 	}
505       }
506 
507     }
508     else if (attr && !_cups_strcasecmp(token, ","))
509     {
510      /*
511       * Additional value...
512       */
513 
514       if (!parse_value(f, v, user_data, col, &attr, ippGetCount(attr)))
515       {
516 	ippDelete(col);
517 	col = NULL;
518 	break;
519       }
520     }
521     else
522     {
523      /*
524       * Something else...
525       */
526 
527       report_error(f, v, user_data, "Unknown directive \"%s\" on line %d of \"%s\".", token, f->linenum, f->filename);
528       ippDelete(col);
529       col  = NULL;
530       attr = NULL;
531       break;
532 
533     }
534   }
535 
536   return (col);
537 }
538 
539 
540 /*
541  * 'parse_value()' - Parse an IPP value.
542  */
543 
544 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)545 parse_value(_ipp_file_t      *f,	/* I  - IPP data file */
546             _ipp_vars_t      *v,	/* I  - IPP variables */
547             void             *user_data,/* I  - User data pointer */
548             ipp_t            *ipp,	/* I  - IPP message */
549             ipp_attribute_t  **attr,	/* IO - IPP attribute */
550             int              element)	/* I  - Element number */
551 {
552   char		value[2049],		/* Value string */
553 		*valueptr,		/* Pointer into value string */
554 		temp[2049],		/* Temporary string */
555 		*tempptr;		/* Pointer into temporary string */
556   size_t	valuelen;		/* Length of value */
557 
558 
559   if (!_ippFileReadToken(f, temp, sizeof(temp)))
560   {
561     report_error(f, v, user_data, "Missing value on line %d of \"%s\".", f->linenum, f->filename);
562     return (0);
563   }
564 
565   _ippVarsExpand(v, value, temp, sizeof(value));
566 
567   switch (ippGetValueTag(*attr))
568   {
569     case IPP_TAG_BOOLEAN :
570         return (ippSetBoolean(ipp, attr, element, !_cups_strcasecmp(value, "true")));
571 
572     case IPP_TAG_ENUM :
573     case IPP_TAG_INTEGER :
574         return (ippSetInteger(ipp, attr, element, (int)strtol(value, NULL, 0)));
575 
576     case IPP_TAG_DATE :
577         {
578           int	year,			/* Year */
579 		month,			/* Month */
580 		day,			/* Day of month */
581 		hour,			/* Hour */
582 		minute,			/* Minute */
583 		second,			/* Second */
584 		utc_offset = 0;		/* Timezone offset from UTC */
585           ipp_uchar_t date[11];		/* dateTime value */
586 
587           if (*value == 'P')
588           {
589            /*
590             * Time period...
591             */
592 
593             time_t	curtime;	/* Current time in seconds */
594             int		period = 0,	/* Current period value */
595 			saw_T = 0;	/* Saw time separator */
596 
597             curtime = time(NULL);
598 
599             for (valueptr = value + 1; *valueptr; valueptr ++)
600             {
601               if (isdigit(*valueptr & 255))
602               {
603                 period = (int)strtol(valueptr, &valueptr, 10);
604 
605                 if (!valueptr || period < 0)
606                 {
607 		  report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
608 		  return (0);
609 		}
610               }
611 
612               if (*valueptr == 'Y')
613               {
614                 curtime += 365 * 86400 * period;
615                 period  = 0;
616               }
617               else if (*valueptr == 'M')
618               {
619                 if (saw_T)
620                   curtime += 60 * period;
621                 else
622                   curtime += 30 * 86400 * period;
623 
624                 period = 0;
625               }
626               else if (*valueptr == 'D')
627               {
628                 curtime += 86400 * period;
629                 period  = 0;
630               }
631               else if (*valueptr == 'H')
632               {
633                 curtime += 3600 * period;
634                 period  = 0;
635               }
636               else if (*valueptr == 'S')
637               {
638                 curtime += period;
639                 period = 0;
640               }
641               else if (*valueptr == 'T')
642               {
643                 saw_T  = 1;
644                 period = 0;
645               }
646               else
647 	      {
648 		report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
649 		return (0);
650 	      }
651 	    }
652 
653 	    return (ippSetDate(ipp, attr, element, ippTimeToDate(curtime)));
654           }
655           else if (sscanf(value, "%d-%d-%dT%d:%d:%d%d", &year, &month, &day, &hour, &minute, &second, &utc_offset) < 6)
656           {
657            /*
658             * Date/time value did not parse...
659             */
660 
661 	    report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
662 	    return (0);
663           }
664 
665           date[0] = (ipp_uchar_t)(year >> 8);
666           date[1] = (ipp_uchar_t)(year & 255);
667           date[2] = (ipp_uchar_t)month;
668           date[3] = (ipp_uchar_t)day;
669           date[4] = (ipp_uchar_t)hour;
670           date[5] = (ipp_uchar_t)minute;
671           date[6] = (ipp_uchar_t)second;
672           date[7] = 0;
673           if (utc_offset < 0)
674           {
675             utc_offset = -utc_offset;
676             date[8]    = (ipp_uchar_t)'-';
677 	  }
678 	  else
679 	  {
680             date[8] = (ipp_uchar_t)'+';
681 	  }
682 
683           date[9]  = (ipp_uchar_t)(utc_offset / 100);
684           date[10] = (ipp_uchar_t)(utc_offset % 100);
685 
686           return (ippSetDate(ipp, attr, element, date));
687         }
688 
689     case IPP_TAG_RESOLUTION :
690 	{
691 	  int	xres,		/* X resolution */
692 		yres;		/* Y resolution */
693 	  char	*ptr;		/* Pointer into value */
694 
695 	  xres = yres = (int)strtol(value, (char **)&ptr, 10);
696 	  if (ptr > value && xres > 0)
697 	  {
698 	    if (*ptr == 'x')
699 	      yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
700 	  }
701 
702 	  if (ptr <= value || xres <= 0 || yres <= 0 || !ptr || (_cups_strcasecmp(ptr, "dpi") && _cups_strcasecmp(ptr, "dpc") && _cups_strcasecmp(ptr, "dpcm") && _cups_strcasecmp(ptr, "other")))
703 	  {
704 	    report_error(f, v, user_data, "Bad resolution value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
705 	    return (0);
706 	  }
707 
708 	  if (!_cups_strcasecmp(ptr, "dpi"))
709 	    return (ippSetResolution(ipp, attr, element, IPP_RES_PER_INCH, xres, yres));
710 	  else if (!_cups_strcasecmp(ptr, "dpc") || !_cups_strcasecmp(ptr, "dpcm"))
711 	    return (ippSetResolution(ipp, attr, element, IPP_RES_PER_CM, xres, yres));
712 	  else
713 	    return (ippSetResolution(ipp, attr, element, (ipp_res_t)0, xres, yres));
714 	}
715 
716     case IPP_TAG_RANGE :
717 	{
718 	  int	lower,			/* Lower value */
719 		upper;			/* Upper value */
720 
721           if (sscanf(value, "%d-%d", &lower, &upper) != 2)
722           {
723 	    report_error(f, v, user_data, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
724 	    return (0);
725 	  }
726 
727 	  return (ippSetRange(ipp, attr, element, lower, upper));
728 	}
729 
730     case IPP_TAG_STRING :
731         valuelen = strlen(value);
732 
733         if (value[0] == '<' && value[strlen(value) - 1] == '>')
734         {
735           if (valuelen & 1)
736           {
737 	    report_error(f, v, user_data, "Bad octetString value on line %d of \"%s\".", f->linenum, f->filename);
738 	    return (0);
739           }
740 
741           valueptr = value + 1;
742           tempptr  = temp;
743 
744           while (*valueptr && *valueptr != '>')
745           {
746 	    if (!isxdigit(valueptr[0] & 255) || !isxdigit(valueptr[1] & 255))
747 	    {
748 	      report_error(f, v, user_data, "Bad octetString value on line %d of \"%s\".", f->linenum, f->filename);
749 	      return (0);
750 	    }
751 
752             if (valueptr[0] >= '0' && valueptr[0] <= '9')
753               *tempptr = (char)((valueptr[0] - '0') << 4);
754 	    else
755               *tempptr = (char)((tolower(valueptr[0]) - 'a' + 10) << 4);
756 
757             if (valueptr[1] >= '0' && valueptr[1] <= '9')
758               *tempptr |= (valueptr[1] - '0');
759 	    else
760               *tempptr |= (tolower(valueptr[1]) - 'a' + 10);
761 
762             tempptr ++;
763           }
764 
765           return (ippSetOctetString(ipp, attr, element, temp, (int)(tempptr - temp)));
766         }
767         else
768           return (ippSetOctetString(ipp, attr, element, value, (int)valuelen));
769 
770     case IPP_TAG_TEXTLANG :
771     case IPP_TAG_NAMELANG :
772     case IPP_TAG_TEXT :
773     case IPP_TAG_NAME :
774     case IPP_TAG_KEYWORD :
775     case IPP_TAG_URI :
776     case IPP_TAG_URISCHEME :
777     case IPP_TAG_CHARSET :
778     case IPP_TAG_LANGUAGE :
779     case IPP_TAG_MIMETYPE :
780         return (ippSetString(ipp, attr, element, value));
781 
782     case IPP_TAG_BEGIN_COLLECTION :
783         {
784           int	status;			/* Add status */
785           ipp_t *col;			/* Collection value */
786 
787           if (strcmp(value, "{"))
788           {
789 	    report_error(f, v, user_data, "Bad collection value on line %d of \"%s\".", f->linenum, f->filename);
790 	    return (0);
791           }
792 
793           if ((col = parse_collection(f, v, user_data)) == NULL)
794             return (0);
795 
796 	  status = ippSetCollection(ipp, attr, element, col);
797 	  ippDelete(col);
798 
799 	  return (status);
800 	}
801 
802     default :
803         report_error(f, v, user_data, "Unsupported value on line %d of \"%s\".", f->linenum, f->filename);
804         return (0);
805   }
806 }
807 
808 
809 /*
810  * 'report_error()' - Report an error.
811  */
812 
813 static void
report_error(_ipp_file_t * f,_ipp_vars_t * v,void * user_data,const char * message,...)814 report_error(
815     _ipp_file_t *f,			/* I - IPP data file */
816     _ipp_vars_t *v,			/* I - Error callback function, if any */
817     void        *user_data,		/* I - User data pointer */
818     const char  *message,		/* I - Printf-style message */
819     ...)				/* I - Additional arguments as needed */
820 {
821   char		buffer[8192];		/* Formatted string */
822   va_list	ap;			/* Argument pointer */
823 
824 
825   va_start(ap, message);
826   vsnprintf(buffer, sizeof(buffer), message, ap);
827   va_end(ap);
828 
829   if (v->errorcb)
830     (*v->errorcb)(f, user_data, buffer);
831   else
832     fprintf(stderr, "%s\n", buffer);
833 }
834