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