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