2 * IPP data file parsing functions.
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers...
15 #include "ipp-private.h"
16 #include "string-private.h"
17 #include "debug-internal.h"
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);
30 * '_ippFileParse()' - Parse an IPP data file.
33 ipp_t
* /* O - IPP attributes or @code NULL@ on failure */
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 */
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 */
46 DEBUG_printf(("_ippFileParse(v=%p, filename=\"%s\", user_data=%p)", (void *)v
, filename
, user_data
));
49 * Initialize file info...
52 memset(&f
, 0, sizeof(f
));
53 f
.filename
= filename
;
56 if ((f
.fp
= cupsFileOpen(filename
, "r")) == NULL
)
58 DEBUG_printf(("1_ippFileParse: Unable to open \"%s\": %s", filename
, strerror(errno
)));
63 * Do the callback with a NULL token to setup any initial state...
66 (*v
->tokencb
)(&f
, v
, user_data
, NULL
);
69 * Read data file, using the callback function as needed...
72 while (_ippFileReadToken(&f
, token
, sizeof(token
)))
74 if (!_cups_strcasecmp(token
, "DEFINE") || !_cups_strcasecmp(token
, "DEFINE-DEFAULT"))
76 char name
[128], /* Variable name */
77 value
[1024], /* Variable value */
78 temp
[1024]; /* Temporary string */
82 if (_ippFileReadToken(&f
, name
, sizeof(name
)) && _ippFileReadToken(&f
, temp
, sizeof(temp
)))
84 if (_cups_strcasecmp(token
, "DEFINE-DEFAULT") || !_ippVarsGet(v
, name
))
86 _ippVarsExpand(v
, value
, temp
, sizeof(value
));
87 _ippVarsSet(v
, name
, value
);
92 report_error(&f
, v
, user_data
, "Missing %s name and/or value on line %d of \"%s\".", token
, f
.linenum
, f
.filename
);
96 else if (f
.attrs
&& !_cups_strcasecmp(token
, "ATTR"))
99 * Attribute definition...
102 char syntax
[128], /* Attribute syntax (value tag) */
103 name
[128]; /* Attribute name */
104 ipp_tag_t value_tag
; /* Value tag */
108 if (!_ippFileReadToken(&f
, syntax
, sizeof(syntax
)))
110 report_error(&f
, v
, user_data
, "Missing ATTR syntax on line %d of \"%s\".", f
.linenum
, f
.filename
);
113 else if ((value_tag
= ippTagValue(syntax
)) < IPP_TAG_UNSUPPORTED_VALUE
)
115 report_error(&f
, v
, user_data
, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax
, f
.linenum
, f
.filename
);
119 if (!_ippFileReadToken(&f
, name
, sizeof(name
)) || !name
[0])
121 report_error(&f
, v
, user_data
, "Missing ATTR name on line %d of \"%s\".", f
.linenum
, f
.filename
);
125 if (!v
->attrcb
|| (*v
->attrcb
)(&f
, user_data
, name
))
128 * Add this attribute...
136 * Ignore this attribute...
145 if (value_tag
< IPP_TAG_INTEGER
)
148 * Add out-of-band attribute - no value string needed...
151 ippAddOutOfBand(attrs
, f
.group_tag
, value_tag
, name
);
156 * Add attribute with one or more values...
159 attr
= ippAddString(attrs
, f
.group_tag
, value_tag
, name
, NULL
, NULL
);
161 if (!parse_value(&f
, v
, user_data
, attrs
, &attr
, 0))
166 else if (attr
&& !_cups_strcasecmp(token
, ","))
169 * Additional value...
172 if (!parse_value(&f
, v
, user_data
, attrs
, &attr
, ippGetCount(attr
)))
184 if (!(*v
->tokencb
)(&f
, v
, user_data
, token
))
190 * Close the file and free ignored attributes, then return any attributes we
202 * '_ippFileReadToken()' - Read a token from an IPP data file.
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 */
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 */
217 * Skip whitespace and comments...
220 DEBUG_printf(("1_ippFileReadToken: linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
222 while ((ch
= cupsFileGetChar(f
->fp
)) != EOF
)
224 if (_cups_isspace(ch
))
233 DEBUG_printf(("1_ippFileReadToken: LF in leading whitespace, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
242 DEBUG_puts("1_ippFileReadToken: Skipping comment in leading whitespace...");
244 while ((ch
= cupsFileGetChar(f
->fp
)) != EOF
)
253 DEBUG_printf(("1_ippFileReadToken: LF at end of comment, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
264 DEBUG_puts("1_ippFileReadToken: EOF");
277 DEBUG_printf(("1_ippFileReadToken: LF in token, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
283 * End of quoted text...
287 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at closing quote.", token
));
290 else if (!quote
&& _cups_isspace(ch
))
293 * End of unquoted text...
297 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before whitespace.", token
));
300 else if (!quote
&& (ch
== '\'' || ch
== '\"'))
303 * Start of quoted text or regular expression...
308 DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote
, (long)cupsFileTell(f
->fp
)));
310 else if (!quote
&& ch
== '#')
313 * Start of comment...
316 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
318 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before comment.", token
));
321 else if (!quote
&& (ch
== '{' || ch
== '}' || ch
== ','))
330 * Return the preceding token first...
333 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
338 * Return this delimiter by itself...
341 *tokptr
++ = (char)ch
;
345 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
353 * Quoted character...
356 DEBUG_printf(("1_ippFileReadToken: Quoted character at pos=%ld", (long)cupsFileTell(f
->fp
)));
358 if ((ch
= cupsFileGetChar(f
->fp
)) == EOF
)
361 DEBUG_puts("1_ippFileReadToken: EOF");
367 DEBUG_printf(("1_ippFileReadToken: quoted LF, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
374 * Add to current token...
377 *tokptr
++ = (char)ch
;
386 DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token
));
392 * Get the next character...
395 ch
= cupsFileGetChar(f
->fp
);
399 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token
));
401 return (tokptr
> token
);
406 * 'parse_collection()' - Parse an IPP collection value.
409 static ipp_t
* /* O - Collection value or @code NULL@ on error */
411 _ipp_file_t
*f
, /* I - IPP data file */
412 _ipp_vars_t
*v
, /* I - IPP variables */
413 void *user_data
) /* I - User data pointer */
415 ipp_t
*col
= ippNew(); /* Collection value */
416 ipp_attribute_t
*attr
= NULL
; /* Current member attribute */
417 char token
[1024]; /* Token string */
421 * Parse the collection value...
424 while (_ippFileReadToken(f
, token
, sizeof(token
)))
426 if (!_cups_strcasecmp(token
, "}"))
429 * End of collection value...
434 else if (!_cups_strcasecmp(token
, "MEMBER"))
437 * Member attribute definition...
440 char syntax
[128], /* Attribute syntax (value tag) */
441 name
[128]; /* Attribute name */
442 ipp_tag_t value_tag
; /* Value tag */
446 if (!_ippFileReadToken(f
, syntax
, sizeof(syntax
)))
448 report_error(f
, v
, user_data
, "Missing ATTR syntax on line %d of \"%s\".", f
->linenum
, f
->filename
);
453 else if ((value_tag
= ippTagValue(syntax
)) < IPP_TAG_UNSUPPORTED_VALUE
)
455 report_error(f
, v
, user_data
, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax
, f
->linenum
, f
->filename
);
461 if (!_ippFileReadToken(f
, name
, sizeof(name
)) || !name
[0])
463 report_error(f
, v
, user_data
, "Missing ATTR name on line %d of \"%s\".", f
->linenum
, f
->filename
);
469 if (value_tag
< IPP_TAG_INTEGER
)
472 * Add out-of-band attribute - no value string needed...
475 ippAddOutOfBand(col
, IPP_TAG_ZERO
, value_tag
, name
);
480 * Add attribute with one or more values...
483 attr
= ippAddString(col
, IPP_TAG_ZERO
, value_tag
, name
, NULL
, NULL
);
485 if (!parse_value(f
, v
, user_data
, col
, &attr
, 0))
494 else if (attr
&& !_cups_strcasecmp(token
, ","))
497 * Additional value...
500 if (!parse_value(f
, v
, user_data
, col
, &attr
, ippGetCount(attr
)))
513 report_error(f
, v
, user_data
, "Unknown directive \"%s\" on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
527 * 'parse_value()' - Parse an IPP value.
530 static int /* O - 1 on success or 0 on error */
531 parse_value(_ipp_file_t
*f
, /* I - IPP data file */
532 _ipp_vars_t
*v
, /* I - IPP variables */
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 */
538 char value
[1024], /* Value string */
539 temp
[1024]; /* Temporary string */
542 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
544 report_error(f
, v
, user_data
, "Missing value on line %d of \"%s\".", f
->linenum
, f
->filename
);
548 _ippVarsExpand(v
, value
, temp
, sizeof(value
));
550 switch (ippGetValueTag(*attr
))
552 case IPP_TAG_BOOLEAN
:
553 return (ippSetBoolean(ipp
, attr
, element
, !_cups_strcasecmp(value
, "true")));
557 case IPP_TAG_INTEGER
:
558 return (ippSetInteger(ipp
, attr
, element
, (int)strtol(value
, NULL
, 0)));
565 day
, /* Day of month */
569 utc_offset
= 0; /* Timezone offset from UTC */
570 ipp_uchar_t date
[11]; /* dateTime value */
572 if (sscanf(value
, "%d-%d-%dT%d:%d:%d%d", &year
, &month
, &day
, &hour
, &minute
, &second
, &utc_offset
) < 6)
574 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
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
;
588 utc_offset
= -utc_offset
;
589 date
[8] = (ipp_uchar_t
)'-';
593 date
[8] = (ipp_uchar_t
)'+';
596 date
[9] = (ipp_uchar_t
)(utc_offset
/ 100);
597 date
[10] = (ipp_uchar_t
)(utc_offset
% 100);
599 return (ippSetDate(ipp
, attr
, element
, date
));
603 case IPP_TAG_RESOLUTION
:
605 int xres
, /* X resolution */
606 yres
; /* Y resolution */
607 char *ptr
; /* Pointer into value */
609 xres
= yres
= (int)strtol(value
, (char **)&ptr
, 10);
610 if (ptr
> value
&& xres
> 0)
613 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
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")))
618 report_error(f
, v
, user_data
, "Bad resolution value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
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
));
627 return (ippSetResolution(ipp
, attr
, element
, (ipp_res_t
)0, xres
, yres
));
633 int lower
, /* Lower value */
634 upper
; /* Upper value */
636 if (sscanf(value
, "%d-%d", &lower
, &upper
) != 2)
638 report_error(f
, v
, user_data
, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
642 return (ippSetRange(ipp
, attr
, element
, lower
, upper
));
646 case IPP_TAG_STRING
:
647 return (ippSetOctetString(ipp
, attr
, element
, value
, (int)strlen(value
)));
650 case IPP_TAG_TEXTLANG
:
651 case IPP_TAG_NAMELANG
:
654 case IPP_TAG_KEYWORD
:
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
));
663 case IPP_TAG_BEGIN_COLLECTION
:
665 int status
; /* Add status */
666 ipp_t
*col
; /* Collection value */
668 if (strcmp(value
, "{"))
670 report_error(f
, v
, user_data
, "Bad ATTR collection value on line %d of \"%s\".", f
->linenum
, f
->filename
);
674 if ((col
= parse_collection(f
, v
, user_data
)) == NULL
)
677 status
= ippSetCollection(ipp
, attr
, element
, col
);
685 report_error(f
, v
, user_data
, "Unsupported ATTR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
694 * 'report_error()' - Report an error.
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 */
703 ...) /* I - Additional arguments as needed */
705 char buffer
[8192]; /* Formatted string */
706 va_list ap
; /* Argument pointer */
709 va_start(ap
, message
);
710 vsnprintf(buffer
, sizeof(buffer
), message
, ap
);
714 (*v
->errorcb
)(f
, user_data
, buffer
);
716 fprintf(stderr
, "%s\n", buffer
);