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-private.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
, ...) __attribute((__format__ (__printf__
, 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 while ((ch
= cupsFileGetChar(f
->fp
)) != EOF
)
222 if (_cups_isspace(ch
))
237 while ((ch
= cupsFileGetChar(f
->fp
)) != EOF
)
254 DEBUG_puts("1_ippFileReadToken: EOF");
270 * End of quoted text...
274 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
277 else if (!quote
&& _cups_isspace(ch
))
280 * End of unquoted text...
284 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
287 else if (!quote
&& (ch
== '\'' || ch
== '\"'))
290 * Start of quoted text or regular expression...
295 else if (!quote
&& ch
== '#')
298 * Start of comment...
301 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
303 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
306 else if (!quote
&& (ch
== '{' || ch
== '}' || ch
== ','))
315 * Return the preceding token first...
318 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
323 * Return this delimiter by itself...
326 *tokptr
++ = (char)ch
;
330 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
338 * Quoted character...
341 if ((ch
= cupsFileGetChar(f
->fp
)) == EOF
)
344 DEBUG_puts("1_ippFileReadToken: EOF");
354 * Add to current token...
357 *tokptr
++ = (char)ch
;
366 DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token
));
372 * Get the next character...
375 ch
= cupsFileGetChar(f
->fp
);
379 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
381 return (tokptr
> token
);
386 * 'parse_collection()' - Parse an IPP collection value.
389 static ipp_t
* /* O - Collection value or @code NULL@ on error */
391 _ipp_file_t
*f
, /* I - IPP data file */
392 _ipp_vars_t
*v
, /* I - IPP variables */
393 void *user_data
) /* I - User data pointer */
395 ipp_t
*col
= ippNew(); /* Collection value */
396 ipp_attribute_t
*attr
= NULL
; /* Current member attribute */
397 char token
[1024]; /* Token string */
401 * Parse the collection value...
404 while (_ippFileReadToken(f
, token
, sizeof(token
)))
406 if (!_cups_strcasecmp(token
, "}"))
409 * End of collection value...
414 else if (!_cups_strcasecmp(token
, "MEMBER"))
417 * Member attribute definition...
420 char syntax
[128], /* Attribute syntax (value tag) */
421 name
[128]; /* Attribute name */
422 ipp_tag_t value_tag
; /* Value tag */
426 if (!_ippFileReadToken(f
, syntax
, sizeof(syntax
)))
428 report_error(f
, v
, user_data
, "Missing ATTR syntax on line %d of \"%s\".", f
->linenum
, f
->filename
);
433 else if ((value_tag
= ippTagValue(syntax
)) < IPP_TAG_UNSUPPORTED_VALUE
)
435 report_error(f
, v
, user_data
, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax
, f
->linenum
, f
->filename
);
441 if (!_ippFileReadToken(f
, name
, sizeof(name
)) || !name
[0])
443 report_error(f
, v
, user_data
, "Missing ATTR name on line %d of \"%s\".", f
->linenum
, f
->filename
);
449 if (value_tag
< IPP_TAG_INTEGER
)
452 * Add out-of-band attribute - no value string needed...
455 ippAddOutOfBand(col
, IPP_TAG_ZERO
, value_tag
, name
);
460 * Add attribute with one or more values...
463 attr
= ippAddString(col
, IPP_TAG_ZERO
, value_tag
, name
, NULL
, NULL
);
465 if (!parse_value(f
, v
, user_data
, col
, &attr
, 0))
474 else if (attr
&& !_cups_strcasecmp(token
, ","))
477 * Additional value...
480 if (!parse_value(f
, v
, user_data
, col
, &attr
, ippGetCount(attr
)))
493 report_error(f
, v
, user_data
, "Unknown directive \"%s\" on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
507 * 'parse_value()' - Parse an IPP value.
510 static int /* O - 1 on success or 0 on error */
511 parse_value(_ipp_file_t
*f
, /* I - IPP data file */
512 _ipp_vars_t
*v
, /* I - IPP variables */
513 void *user_data
,/* I - User data pointer */
514 ipp_t
*ipp
, /* I - IPP message */
515 ipp_attribute_t
**attr
, /* IO - IPP attribute */
516 int element
) /* I - Element number */
518 char value
[1024], /* Value string */
519 temp
[1024]; /* Temporary string */
522 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
524 report_error(f
, v
, user_data
, "Missing value on line %d of \"%s\".", f
->linenum
, f
->filename
);
528 _ippVarsExpand(v
, value
, temp
, sizeof(value
));
530 switch (ippGetValueTag(*attr
))
532 case IPP_TAG_BOOLEAN
:
533 return (ippSetBoolean(ipp
, attr
, element
, !_cups_strcasecmp(value
, "true")));
537 case IPP_TAG_INTEGER
:
538 return (ippSetInteger(ipp
, attr
, element
, (int)strtol(value
, NULL
, 0)));
545 day
, /* Day of month */
549 utc_offset
= 0; /* Timezone offset from UTC */
550 ipp_uchar_t date
[11]; /* dateTime value */
552 if (sscanf(value
, "%d-%d-%dT%d:%d:%d%d", &year
, &month
, &day
, &hour
, &minute
, &second
, &utc_offset
) < 6)
554 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
558 date
[0] = (ipp_uchar_t
)(year
>> 8);
559 date
[1] = (ipp_uchar_t
)(year
& 255);
560 date
[2] = (ipp_uchar_t
)month
;
561 date
[3] = (ipp_uchar_t
)day
;
562 date
[4] = (ipp_uchar_t
)hour
;
563 date
[5] = (ipp_uchar_t
)minute
;
564 date
[6] = (ipp_uchar_t
)second
;
568 utc_offset
= -utc_offset
;
569 date
[8] = (ipp_uchar_t
)'-';
573 date
[8] = (ipp_uchar_t
)'+';
576 date
[9] = (ipp_uchar_t
)(utc_offset
/ 100);
577 date
[10] = (ipp_uchar_t
)(utc_offset
% 100);
579 return (ippSetDate(ipp
, attr
, element
, date
));
583 case IPP_TAG_RESOLUTION
:
585 int xres
, /* X resolution */
586 yres
; /* Y resolution */
587 char *ptr
; /* Pointer into value */
589 xres
= yres
= (int)strtol(value
, (char **)&ptr
, 10);
590 if (ptr
> value
&& xres
> 0)
593 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
596 if (ptr
<= value
|| xres
<= 0 || yres
<= 0 || !ptr
|| (_cups_strcasecmp(ptr
, "dpi") && _cups_strcasecmp(ptr
, "dpc") && _cups_strcasecmp(ptr
, "dpcm") && _cups_strcasecmp(ptr
, "other")))
598 report_error(f
, v
, user_data
, "Bad resolution value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
602 if (!_cups_strcasecmp(ptr
, "dpi"))
603 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_INCH
, xres
, yres
));
604 else if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
605 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_CM
, xres
, yres
));
607 return (ippSetResolution(ipp
, attr
, element
, (ipp_res_t
)0, xres
, yres
));
613 int lower
, /* Lower value */
614 upper
; /* Upper value */
616 if (sscanf(value
, "%d-%d", &lower
, &upper
) != 2)
618 report_error(f
, v
, user_data
, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
622 return (ippSetRange(ipp
, attr
, element
, lower
, upper
));
626 case IPP_TAG_STRING
:
627 return (ippSetOctetString(ipp
, attr
, element
, value
, (int)strlen(value
)));
630 case IPP_TAG_TEXTLANG
:
631 case IPP_TAG_NAMELANG
:
634 case IPP_TAG_KEYWORD
:
636 case IPP_TAG_URISCHEME
:
637 case IPP_TAG_CHARSET
:
638 case IPP_TAG_LANGUAGE
:
639 case IPP_TAG_MIMETYPE
:
640 return (ippSetString(ipp
, attr
, element
, value
));
643 case IPP_TAG_BEGIN_COLLECTION
:
645 int status
; /* Add status */
646 ipp_t
*col
; /* Collection value */
648 if (strcmp(value
, "{"))
650 report_error(f
, v
, user_data
, "Bad ATTR collection value on line %d of \"%s\".", f
->linenum
, f
->filename
);
654 if ((col
= parse_collection(f
, v
, user_data
)) == NULL
)
657 status
= ippSetCollection(ipp
, attr
, element
, col
);
665 report_error(f
, v
, user_data
, "Unsupported ATTR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
674 * 'report_error()' - Report an error.
679 _ipp_file_t
*f
, /* I - IPP data file */
680 _ipp_vars_t
*v
, /* I - Error callback function, if any */
681 void *user_data
, /* I - User data pointer */
682 const char *message
, /* I - Printf-style message */
683 ...) /* I - Additional arguments as needed */
685 char buffer
[8192]; /* Formatted string */
686 va_list ap
; /* Argument pointer */
689 va_start(ap
, message
);
690 vsnprintf(buffer
, sizeof(buffer
), message
, ap
);
694 (*v
->errorcb
)(f
, user_data
, buffer
);
696 fprintf(stderr
, "%s\n", buffer
);