]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/ipp-file.c
Greatly simplify the man page handling.
[thirdparty/cups.git] / cups / ipp-file.c
CommitLineData
fd96ad89
MS
1/*
2 * IPP data file parsing functions.
3 *
4 * Copyright © 2007-2018 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"
fb863569 17#include "debug-internal.h"
fd96ad89
MS
18
19
20/*
21 * Local functions...
22 */
23
a166e933
MS
24static ipp_t *parse_collection(_ipp_file_t *f, _ipp_vars_t *v, void *user_data);
25static int parse_value(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, ipp_t *ipp, ipp_attribute_t **attr, int element);
86206ccf 26static void report_error(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, const char *message, ...) _CUPS_FORMAT(4, 5);
fd96ad89
MS
27
28
29/*
30 * '_ippFileParse()' - Parse an IPP data file.
31 */
32
33ipp_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 */
fd96ad89
MS
37 void *user_data) /* I - User data pointer */
38{
39 _ipp_file_t f; /* IPP data file information */
a166e933 40 ipp_t *attrs = NULL; /* Active IPP message */
fd96ad89
MS
41 ipp_attribute_t *attr = NULL; /* Current attribute */
42 char token[1024]; /* Token string */
a166e933 43 ipp_t *ignored = NULL; /* Ignored attributes */
fd96ad89
MS
44
45
a166e933 46 DEBUG_printf(("_ippFileParse(v=%p, filename=\"%s\", user_data=%p)", (void *)v, filename, user_data));
fd96ad89
MS
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
a166e933 66 (*v->tokencb)(&f, v, user_data, NULL);
fd96ad89
MS
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 {
367068f3
MS
84 if (_cups_strcasecmp(token, "DEFINE-DEFAULT") || !_ippVarsGet(v, name))
85 {
86 _ippVarsExpand(v, value, temp, sizeof(value));
87 _ippVarsSet(v, name, value);
88 }
fd96ad89
MS
89 }
90 else
91 {
a166e933 92 report_error(&f, v, user_data, "Missing %s name and/or value on line %d of \"%s\".", token, f.linenum, f.filename);
fd96ad89
MS
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 {
a166e933 110 report_error(&f, v, user_data, "Missing ATTR syntax on line %d of \"%s\".", f.linenum, f.filename);
fd96ad89
MS
111 break;
112 }
113 else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
114 {
a166e933 115 report_error(&f, v, user_data, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax, f.linenum, f.filename);
fd96ad89
MS
116 break;
117 }
118
119 if (!_ippFileReadToken(&f, name, sizeof(name)) || !name[0])
120 {
a166e933 121 report_error(&f, v, user_data, "Missing ATTR name on line %d of \"%s\".", f.linenum, f.filename);
fd96ad89
MS
122 break;
123 }
124
a166e933
MS
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
fd96ad89
MS
145 if (value_tag < IPP_TAG_INTEGER)
146 {
147 /*
148 * Add out-of-band attribute - no value string needed...
149 */
150
a166e933 151 ippAddOutOfBand(attrs, f.group_tag, value_tag, name);
fd96ad89
MS
152 }
153 else
154 {
155 /*
156 * Add attribute with one or more values...
157 */
158
a166e933 159 attr = ippAddString(attrs, f.group_tag, value_tag, name, NULL, NULL);
fd96ad89 160
a166e933 161 if (!parse_value(&f, v, user_data, attrs, &attr, 0))
fd96ad89 162 break;
fd96ad89
MS
163 }
164
165 }
166 else if (attr && !_cups_strcasecmp(token, ","))
167 {
168 /*
169 * Additional value...
170 */
171
a166e933 172 if (!parse_value(&f, v, user_data, attrs, &attr, ippGetCount(attr)))
fd96ad89 173 break;
fd96ad89
MS
174 }
175 else
176 {
177 /*
178 * Something else...
179 */
180
a166e933
MS
181 attr = NULL;
182 attrs = NULL;
fd96ad89 183
a166e933 184 if (!(*v->tokencb)(&f, v, user_data, token))
fd96ad89
MS
185 break;
186 }
187 }
188
189 /*
a166e933
MS
190 * Close the file and free ignored attributes, then return any attributes we
191 * kept...
fd96ad89
MS
192 */
193
194 cupsFileClose(f.fp);
a166e933 195 ippDelete(ignored);
fd96ad89
MS
196
197 return (f.attrs);
198}
199
200
201/*
202 * '_ippFileReadToken()' - Read a token from an IPP data file.
203 */
204
205int /* 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
0b353e63
MS
220 DEBUG_printf(("1_ippFileReadToken: linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
221
fd96ad89
MS
222 while ((ch = cupsFileGetChar(f->fp)) != EOF)
223 {
224 if (_cups_isspace(ch))
225 {
226 /*
227 * Whitespace...
228 */
229
230 if (ch == '\n')
0b353e63 231 {
fd96ad89 232 f->linenum ++;
0b353e63
MS
233 DEBUG_printf(("1_ippFileReadToken: LF in leading whitespace, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
234 }
fd96ad89
MS
235 }
236 else if (ch == '#')
237 {
238 /*
239 * Comment...
240 */
241
0b353e63
MS
242 DEBUG_puts("1_ippFileReadToken: Skipping comment in leading whitespace...");
243
fd96ad89
MS
244 while ((ch = cupsFileGetChar(f->fp)) != EOF)
245 {
246 if (ch == '\n')
247 break;
248 }
249
250 if (ch == '\n')
0b353e63 251 {
fd96ad89 252 f->linenum ++;
0b353e63
MS
253 DEBUG_printf(("1_ippFileReadToken: LF at end of comment, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
254 }
fd96ad89
MS
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 {
8e2e09c1 274 if (ch == '\n')
0b353e63 275 {
8e2e09c1 276 f->linenum ++;
0b353e63
MS
277 DEBUG_printf(("1_ippFileReadToken: LF in token, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
278 }
8e2e09c1 279
fd96ad89
MS
280 if (ch == quote)
281 {
282 /*
283 * End of quoted text...
284 */
285
286 *tokptr = '\0';
0b353e63 287 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at closing quote.", token));
fd96ad89
MS
288 return (1);
289 }
290 else if (!quote && _cups_isspace(ch))
291 {
292 /*
293 * End of unquoted text...
294 */
295
296 *tokptr = '\0';
0b353e63 297 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before whitespace.", token));
fd96ad89
MS
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;
0b353e63
MS
307
308 DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote, (long)cupsFileTell(f->fp)));
fd96ad89
MS
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';
0b353e63 318 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before comment.", token));
fd96ad89
MS
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
0b353e63
MS
356 DEBUG_printf(("1_ippFileReadToken: Quoted character at pos=%ld", (long)cupsFileTell(f->fp)));
357
fd96ad89
MS
358 if ((ch = cupsFileGetChar(f->fp)) == EOF)
359 {
360 *token = '\0';
361 DEBUG_puts("1_ippFileReadToken: EOF");
362 return (0);
363 }
8e2e09c1 364 else if (ch == '\n')
0b353e63 365 {
8e2e09c1 366 f->linenum ++;
0b353e63
MS
367 DEBUG_printf(("1_ippFileReadToken: quoted LF, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
368 }
fd96ad89
MS
369 }
370
371 if (tokptr < tokend)
372 {
373 /*
374 * Add to current token...
375 */
376
377 *tokptr++ = (char)ch;
378 }
379 else
380 {
381 /*
382 * Token too long...
383 */
384
385 *tokptr = '\0';
386 DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token));
387 return (0);
388 }
389 }
390
391 /*
392 * Get the next character...
393 */
394
395 ch = cupsFileGetChar(f->fp);
396 }
397
398 *tokptr = '\0';
0b353e63 399 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token));
fd96ad89
MS
400
401 return (tokptr > token);
402}
403
404
405/*
406 * 'parse_collection()' - Parse an IPP collection value.
407 */
408
409static ipp_t * /* O - Collection value or @code NULL@ on error */
410parse_collection(
411 _ipp_file_t *f, /* I - IPP data file */
412 _ipp_vars_t *v, /* I - IPP variables */
fd96ad89
MS
413 void *user_data) /* I - User data pointer */
414{
415 ipp_t *col = ippNew(); /* Collection value */
416 ipp_attribute_t *attr = NULL; /* Current member attribute */
417 char token[1024]; /* Token string */
418
419
420 /*
421 * Parse the collection value...
422 */
423
424 while (_ippFileReadToken(f, token, sizeof(token)))
425 {
426 if (!_cups_strcasecmp(token, "}"))
427 {
428 /*
429 * End of collection value...
430 */
431
432 break;
433 }
434 else if (!_cups_strcasecmp(token, "MEMBER"))
435 {
436 /*
437 * Member attribute definition...
438 */
439
440 char syntax[128], /* Attribute syntax (value tag) */
441 name[128]; /* Attribute name */
442 ipp_tag_t value_tag; /* Value tag */
443
444 attr = NULL;
445
446 if (!_ippFileReadToken(f, syntax, sizeof(syntax)))
447 {
a166e933 448 report_error(f, v, user_data, "Missing ATTR syntax on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
449 ippDelete(col);
450 col = NULL;
451 break;
452 }
453 else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
454 {
a166e933 455 report_error(f, v, user_data, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax, f->linenum, f->filename);
fd96ad89
MS
456 ippDelete(col);
457 col = NULL;
458 break;
459 }
460
461 if (!_ippFileReadToken(f, name, sizeof(name)) || !name[0])
462 {
a166e933 463 report_error(f, v, user_data, "Missing ATTR name on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
464 ippDelete(col);
465 col = NULL;
466 break;
467 }
468
469 if (value_tag < IPP_TAG_INTEGER)
470 {
471 /*
472 * Add out-of-band attribute - no value string needed...
473 */
474
475 ippAddOutOfBand(col, IPP_TAG_ZERO, value_tag, name);
476 }
477 else
478 {
479 /*
480 * Add attribute with one or more values...
481 */
482
483 attr = ippAddString(col, IPP_TAG_ZERO, value_tag, name, NULL, NULL);
484
a166e933 485 if (!parse_value(f, v, user_data, col, &attr, 0))
fd96ad89
MS
486 {
487 ippDelete(col);
488 col = NULL;
489 break;
490 }
491 }
492
493 }
494 else if (attr && !_cups_strcasecmp(token, ","))
495 {
496 /*
497 * Additional value...
498 */
499
a166e933 500 if (!parse_value(f, v, user_data, col, &attr, ippGetCount(attr)))
fd96ad89
MS
501 {
502 ippDelete(col);
503 col = NULL;
504 break;
505 }
506 }
507 else
508 {
509 /*
510 * Something else...
511 */
512
a166e933 513 report_error(f, v, user_data, "Unknown directive \"%s\" on line %d of \"%s\".", token, f->linenum, f->filename);
fd96ad89
MS
514 ippDelete(col);
515 col = NULL;
516 attr = NULL;
517 break;
518
519 }
520 }
521
522 return (col);
523}
524
525
526/*
527 * 'parse_value()' - Parse an IPP value.
528 */
529
530static int /* O - 1 on success or 0 on error */
531parse_value(_ipp_file_t *f, /* I - IPP data file */
532 _ipp_vars_t *v, /* I - IPP variables */
fd96ad89
MS
533 void *user_data,/* I - User data pointer */
534 ipp_t *ipp, /* I - IPP message */
535 ipp_attribute_t **attr, /* IO - IPP attribute */
536 int element) /* I - Element number */
537{
538 char value[1024], /* Value string */
539 temp[1024]; /* Temporary string */
540
541
542 if (!_ippFileReadToken(f, temp, sizeof(temp)))
543 {
a166e933 544 report_error(f, v, user_data, "Missing value on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
545 return (0);
546 }
547
548 _ippVarsExpand(v, value, temp, sizeof(value));
549
550 switch (ippGetValueTag(*attr))
551 {
552 case IPP_TAG_BOOLEAN :
553 return (ippSetBoolean(ipp, attr, element, !_cups_strcasecmp(value, "true")));
554 break;
555
556 case IPP_TAG_ENUM :
557 case IPP_TAG_INTEGER :
564cd57d 558 return (ippSetInteger(ipp, attr, element, (int)strtol(value, NULL, 0)));
fd96ad89
MS
559 break;
560
06e1afc3
MS
561 case IPP_TAG_DATE :
562 {
563 int year, /* Year */
564 month, /* Month */
565 day, /* Day of month */
566 hour, /* Hour */
567 minute, /* Minute */
568 second, /* Second */
569 utc_offset = 0; /* Timezone offset from UTC */
570 ipp_uchar_t date[11]; /* dateTime value */
571
f17549fb 572 if (sscanf(value, "%d-%d-%dT%d:%d:%d%d", &year, &month, &day, &hour, &minute, &second, &utc_offset) < 6)
06e1afc3
MS
573 {
574 report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
575 return (0);
576 }
577
578 date[0] = (ipp_uchar_t)(year >> 8);
579 date[1] = (ipp_uchar_t)(year & 255);
580 date[2] = (ipp_uchar_t)month;
581 date[3] = (ipp_uchar_t)day;
582 date[4] = (ipp_uchar_t)hour;
583 date[5] = (ipp_uchar_t)minute;
584 date[6] = (ipp_uchar_t)second;
585 date[7] = 0;
586 if (utc_offset < 0)
587 {
588 utc_offset = -utc_offset;
589 date[8] = (ipp_uchar_t)'-';
590 }
591 else
592 {
593 date[8] = (ipp_uchar_t)'+';
594 }
595
596 date[9] = (ipp_uchar_t)(utc_offset / 100);
597 date[10] = (ipp_uchar_t)(utc_offset % 100);
598
599 return (ippSetDate(ipp, attr, element, date));
600 }
601 break;
602
fd96ad89
MS
603 case IPP_TAG_RESOLUTION :
604 {
605 int xres, /* X resolution */
606 yres; /* Y resolution */
607 char *ptr; /* Pointer into value */
608
609 xres = yres = (int)strtol(value, (char **)&ptr, 10);
610 if (ptr > value && xres > 0)
611 {
612 if (*ptr == 'x')
613 yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
614 }
615
616 if (ptr <= value || xres <= 0 || yres <= 0 || !ptr || (_cups_strcasecmp(ptr, "dpi") && _cups_strcasecmp(ptr, "dpc") && _cups_strcasecmp(ptr, "dpcm") && _cups_strcasecmp(ptr, "other")))
617 {
a166e933 618 report_error(f, v, user_data, "Bad resolution value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
fd96ad89
MS
619 return (0);
620 }
621
622 if (!_cups_strcasecmp(ptr, "dpi"))
623 return (ippSetResolution(ipp, attr, element, IPP_RES_PER_INCH, xres, yres));
624 else if (!_cups_strcasecmp(ptr, "dpc") || !_cups_strcasecmp(ptr, "dpcm"))
625 return (ippSetResolution(ipp, attr, element, IPP_RES_PER_CM, xres, yres));
626 else
627 return (ippSetResolution(ipp, attr, element, (ipp_res_t)0, xres, yres));
628 }
629 break;
630
631 case IPP_TAG_RANGE :
632 {
633 int lower, /* Lower value */
634 upper; /* Upper value */
635
636 if (sscanf(value, "%d-%d", &lower, &upper) != 2)
637 {
a166e933 638 report_error(f, v, user_data, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
fd96ad89
MS
639 return (0);
640 }
641
642 return (ippSetRange(ipp, attr, element, lower, upper));
643 }
644 break;
645
646 case IPP_TAG_STRING :
647 return (ippSetOctetString(ipp, attr, element, value, (int)strlen(value)));
648 break;
649
650 case IPP_TAG_TEXTLANG :
651 case IPP_TAG_NAMELANG :
652 case IPP_TAG_TEXT :
653 case IPP_TAG_NAME :
654 case IPP_TAG_KEYWORD :
655 case IPP_TAG_URI :
656 case IPP_TAG_URISCHEME :
657 case IPP_TAG_CHARSET :
658 case IPP_TAG_LANGUAGE :
659 case IPP_TAG_MIMETYPE :
660 return (ippSetString(ipp, attr, element, value));
661 break;
662
663 case IPP_TAG_BEGIN_COLLECTION :
664 {
665 int status; /* Add status */
666 ipp_t *col; /* Collection value */
667
668 if (strcmp(value, "{"))
669 {
a166e933 670 report_error(f, v, user_data, "Bad ATTR collection value on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
671 return (0);
672 }
673
a166e933 674 if ((col = parse_collection(f, v, user_data)) == NULL)
fd96ad89
MS
675 return (0);
676
677 status = ippSetCollection(ipp, attr, element, col);
678 ippDelete(col);
679
680 return (status);
681 }
682 break;
683
684 default :
a166e933 685 report_error(f, v, user_data, "Unsupported ATTR value on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
686 return (0);
687 }
688
689 return (1);
690}
691
692
693/*
694 * 'report_error()' - Report an error.
695 */
696
697static void
698report_error(
a166e933
MS
699 _ipp_file_t *f, /* I - IPP data file */
700 _ipp_vars_t *v, /* I - Error callback function, if any */
701 void *user_data, /* I - User data pointer */
702 const char *message, /* I - Printf-style message */
fd96ad89
MS
703 ...) /* I - Additional arguments as needed */
704{
705 char buffer[8192]; /* Formatted string */
706 va_list ap; /* Argument pointer */
707
708
709 va_start(ap, message);
710 vsnprintf(buffer, sizeof(buffer), message, ap);
711 va_end(ap);
712
a166e933
MS
713 if (v->errorcb)
714 (*v->errorcb)(f, user_data, buffer);
fd96ad89
MS
715 else
716 fprintf(stderr, "%s\n", buffer);
717}