<NME> ipp-file.c
<BEF> /*
 * IPP data file parsing functions.
 *
 * Copyright © 2007-2018 by Apple Inc.
 * Copyright © 1997-2007 by Easy Software Products.
 *
 * Licensed under Apache License v2.0.  See the file "LICENSE" for more
 * information.
 */

/*
 * Include necessary headers...
 */

#include "ipp-private.h"
#include "string-private.h"
#include "debug-internal.h"


/*
 * Local functions...
 */

static ipp_t	*parse_collection(_ipp_file_t *f, _ipp_vars_t *v, void *user_data);
static int	parse_value(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, ipp_t *ipp, ipp_attribute_t **attr, int element);
static void	report_error(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, const char *message, ...) _CUPS_FORMAT(4, 5);


/*
 * '_ippFileParse()' - Parse an IPP data file.
 */

ipp_t *					/* O - IPP attributes or @code NULL@ on failure */
_ippFileParse(
    _ipp_vars_t      *v,		/* I - Variables */
    const char       *filename,		/* I - Name of file to parse */
    void             *user_data)	/* I - User data pointer */
{
  _ipp_file_t	f;			/* IPP data file information */
  ipp_t		*attrs = NULL;		/* Active IPP message */
  ipp_attribute_t *attr = NULL;		/* Current attribute */
  char		token[1024];		/* Token string */
  ipp_t		*ignored = NULL;	/* Ignored attributes */


  DEBUG_printf(("_ippFileParse(v=%p, filename=\"%s\", user_data=%p)", (void *)v, filename, user_data));

 /*
  * Initialize file info...
  */

  memset(&f, 0, sizeof(f));
  f.filename = filename;
  f.linenum  = 1;

  if ((f.fp = cupsFileOpen(filename, "r")) == NULL)
  {
    DEBUG_printf(("1_ippFileParse: Unable to open \"%s\": %s", filename, strerror(errno)));
    return (0);
  }

 /*
  * Do the callback with a NULL token to setup any initial state...
  */

  (*v->tokencb)(&f, v, user_data, NULL);

 /*
  * Read data file, using the callback function as needed...
  */

  while (_ippFileReadToken(&f, token, sizeof(token)))
  {
    if (!_cups_strcasecmp(token, "DEFINE") || !_cups_strcasecmp(token, "DEFINE-DEFAULT"))
    {
      char	name[128],		/* Variable name */
		value[1024],		/* Variable value */
		temp[1024];		/* Temporary string */

      attr = NULL;

      if (_ippFileReadToken(&f, name, sizeof(name)) && _ippFileReadToken(&f, temp, sizeof(temp)))
      {
        if (_cups_strcasecmp(token, "DEFINE-DEFAULT") || !_ippVarsGet(v, name))
        {
	  _ippVarsExpand(v, value, temp, sizeof(value));
	  _ippVarsSet(v, name, value);
	}
      }
      else
      {
        report_error(&f, v, user_data, "Missing %s name and/or value on line %d of \"%s\".", token, f.linenum, f.filename);
        break;
      }
    }
    else if (f.attrs && !_cups_strcasecmp(token, "ATTR"))
    {
     /*
      * Attribute definition...
      */

      char	syntax[128],		/* Attribute syntax (value tag) */
		name[128];		/* Attribute name */
      ipp_tag_t	value_tag;		/* Value tag */

      attr = NULL;

      if (!_ippFileReadToken(&f, syntax, sizeof(syntax)))
      {
        report_error(&f, v, user_data, "Missing ATTR syntax on line %d of \"%s\".", f.linenum, f.filename);
	break;
      }
      else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
      {
        report_error(&f, v, user_data, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax, f.linenum, f.filename);
	break;
      }

      if (!_ippFileReadToken(&f, name, sizeof(name)) || !name[0])
      {
        report_error(&f, v, user_data, "Missing ATTR name on line %d of \"%s\".", f.linenum, f.filename);
	break;
      }

      if (!v->attrcb || (*v->attrcb)(&f, user_data, name))
      {
       /*
        * Add this attribute...
        */

        attrs = f.attrs;
      }
      else
      {
       /*
        * Ignore this attribute...
        */

        if (!ignored)
          ignored = ippNew();

        attrs = ignored;
      }

      if (value_tag < IPP_TAG_INTEGER)
      {
       /*
	* Add out-of-band attribute - no value string needed...
	*/

        ippAddOutOfBand(attrs, f.group_tag, value_tag, name);
      }
      else
      {
       /*
        * Add attribute with one or more values...
        */

        attr = ippAddString(attrs, f.group_tag, value_tag, name, NULL, NULL);

        if (!parse_value(&f, v, user_data, attrs, &attr, 0))
          break;
      }

    }
    else if (attr && !_cups_strcasecmp(token, ","))
    {
     /*
      * Additional value...
      */

      if (!parse_value(&f, v, user_data, attrs, &attr, ippGetCount(attr)))
	break;
    }
    else
    {
     /*
      * Something else...
      */

      attr  = NULL;
      attrs = NULL;

      if (!(*v->tokencb)(&f, v, user_data, token))
        break;
    }
  }

 /*
  * Close the file and free ignored attributes, then return any attributes we
  * kept...
  */

  cupsFileClose(f.fp);
  ippDelete(ignored);

  return (f.attrs);
}


/*
 * '_ippFileReadToken()' - Read a token from an IPP data file.
 */

int					/* O - 1 on success, 0 on failure */
_ippFileReadToken(_ipp_file_t *f,	/* I - File to read from */
                  char        *token,	/* I - Token string buffer */
                  size_t      tokensize)/* I - Size of token string buffer */
{
  int	ch,				/* Character from file */
	quote = 0;			/* Quoting character */
  char	*tokptr = token,		/* Pointer into token buffer */
	*tokend = token + tokensize - 1;/* End of token buffer */


 /*
  * Skip whitespace and comments...
  */

  DEBUG_printf(("1_ippFileReadToken: linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));

  while ((ch = cupsFileGetChar(f->fp)) != EOF)
  {
    if (_cups_isspace(ch))
    {
     /*
      * Whitespace...
      */

      if (ch == '\n')
      {
        f->linenum ++;
        DEBUG_printf(("1_ippFileReadToken: LF in leading whitespace, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
      }
    }
    else if (ch == '#')
    {
     /*
      * Comment...
      */

      DEBUG_puts("1_ippFileReadToken: Skipping comment in leading whitespace...");

      while ((ch = cupsFileGetChar(f->fp)) != EOF)
      {
        if (ch == '\n')
          break;
      }

      if (ch == '\n')
      {
        f->linenum ++;
        DEBUG_printf(("1_ippFileReadToken: LF at end of comment, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
      }
      else
        break;
    }
    else
      break;
  }

  if (ch == EOF)
  {
    DEBUG_puts("1_ippFileReadToken: EOF");
    return (0);
  }

 /*
  * Read a token...
  */

  while (ch != EOF)
  {
    if (ch == '\n')
    {
      f->linenum ++;
      DEBUG_printf(("1_ippFileReadToken: LF in token, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
    }

    if (ch == quote)
    {
     /*
      * End of quoted text...
      */

      *tokptr = '\0';
      DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at closing quote.", token));
      return (1);
    }
    else if (!quote && _cups_isspace(ch))
    {
     /*
      * End of unquoted text...
      */

      *tokptr = '\0';
      DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before whitespace.", token));
      return (1);
    }
    else if (!quote && (ch == '\'' || ch == '\"'))
    {
     /*
      * Start of quoted text or regular expression...
      */

      quote = ch;

      DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote, (long)cupsFileTell(f->fp)));
    }
      * Start of comment...
      */

      cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
      *tokptr = '\0';
      DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before comment.", token));
      return (1);
    }
    else if (!quote && (ch == '{' || ch == '}' || ch == ','))
    {
     /*
      * Delimiter...
      */

      if (tokptr > token)
      {
       /*
        * Return the preceding token first...
        */

	cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
      }
      else
      {
       /*
        * Return this delimiter by itself...
        */

        *tokptr++ = (char)ch;
      }

      *tokptr = '\0';
      DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
      return (1);
    }
    else
    {
      if (ch == '\\')
      {
       /*
        * Quoted character...
        */

        DEBUG_printf(("1_ippFileReadToken: Quoted character at pos=%ld", (long)cupsFileTell(f->fp)));

        if ((ch = cupsFileGetChar(f->fp)) == EOF)
        {
	  *token = '\0';
	  DEBUG_puts("1_ippFileReadToken: EOF");
	  return (0);
	}
	else if (ch == '\n')
	{
	  f->linenum ++;
	  DEBUG_printf(("1_ippFileReadToken: quoted LF, linenum=%d, pos=%ld", f->linenum, (long)cupsFileTell(f->fp)));
	}
	else if (ch == 'a')
	  ch = '\a';
	else if (ch == 'b')
	  ch = '\b';
	else if (ch == 'f')
	  ch = '\f';
	else if (ch == 'n')
	  ch = '\n';
	else if (ch == 'r')
	  ch = '\r';
	else if (ch == 't')
	  ch = '\t';
	else if (ch == 'v')
	  ch = '\v';
      }

	  ch = '\v';
      }

      if (tokptr < tokend)
      {
       /*
	* Add to current token...
	*/

	*tokptr++ = (char)ch;
      }
      else
      {
       /*
	* Token too long...
	*/

	*tokptr = '\0';
	DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token));
	return (0);
      }
    }


  *tokptr = '\0';
  DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token));

  return (tokptr > token);
}


/*
 * 'parse_collection()' - Parse an IPP collection value.
 */

static ipp_t *				/* O - Collection value or @code NULL@ on error */
parse_collection(
    _ipp_file_t      *f,		/* I - IPP data file */
    _ipp_vars_t      *v,		/* I - IPP variables */
    void             *user_data)	/* I - User data pointer */
{
  ipp_t		*col = ippNew();	/* Collection value */
  ipp_attribute_t *attr = NULL;		/* Current member attribute */
  char		token[1024];		/* Token string */


 /*
  * Parse the collection value...
  */

  while (_ippFileReadToken(f, token, sizeof(token)))
  {
    if (!_cups_strcasecmp(token, "}"))
    {
     /*
      * End of collection value...
      */

      break;
    }
    else if (!_cups_strcasecmp(token, "MEMBER"))
    {
     /*
      * Member attribute definition...
      */

      char	syntax[128],		/* Attribute syntax (value tag) */
		name[128];		/* Attribute name */
      ipp_tag_t	value_tag;		/* Value tag */

      attr = NULL;

      if (!_ippFileReadToken(f, syntax, sizeof(syntax)))
      {
        report_error(f, v, user_data, "Missing MEMBER syntax on line %d of \"%s\".", f->linenum, f->filename);
	ippDelete(col);
	col = NULL;
	break;
      }
      else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
      {
        report_error(f, v, user_data, "Bad MEMBER syntax \"%s\" on line %d of \"%s\".", syntax, f->linenum, f->filename);
	ippDelete(col);
	col = NULL;
	break;
      }

      if (!_ippFileReadToken(f, name, sizeof(name)) || !name[0])
      {
        report_error(f, v, user_data, "Missing MEMBER name on line %d of \"%s\".", f->linenum, f->filename);
	ippDelete(col);
	col = NULL;
	break;
      }

      if (value_tag < IPP_TAG_INTEGER)
      {
       /*
	* Add out-of-band attribute - no value string needed...
	*/

        ippAddOutOfBand(col, IPP_TAG_ZERO, value_tag, name);
      }
      else
      {
       /*
        * Add attribute with one or more values...
        */

        attr = ippAddString(col, IPP_TAG_ZERO, value_tag, name, NULL, NULL);

        if (!parse_value(f, v, user_data, col, &attr, 0))
        {
	  ippDelete(col);
	  col = NULL;
          break;
	}
      }

    }
    else if (attr && !_cups_strcasecmp(token, ","))
    {
     /*
      * Additional value...
      */

      if (!parse_value(f, v, user_data, col, &attr, ippGetCount(attr)))
      {
	ippDelete(col);
	col = NULL;
	break;
      }
    }
    else
    {
     /*
      * Something else...
      */

      report_error(f, v, user_data, "Unknown directive \"%s\" on line %d of \"%s\".", token, f->linenum, f->filename);
      ippDelete(col);
      col  = NULL;
      attr = NULL;
      break;

    }
  }

  return (col);
}


/*
 * 'parse_value()' - Parse an IPP value.
 */

static int				/* O  - 1 on success or 0 on error */
parse_value(_ipp_file_t      *f,	/* I  - IPP data file */
            _ipp_vars_t      *v,	/* I  - IPP variables */
            void             *user_data,/* I  - User data pointer */
            ipp_t            *ipp,	/* I  - IPP message */
            ipp_attribute_t  **attr,	/* IO - IPP attribute */
            int              element)	/* I  - Element number */
{
  char		value[2049],		/* Value string */
		*valueptr,		/* Pointer into value string */
		temp[2049],		/* Temporary string */
            ipp_attribute_t  **attr,	/* IO - IPP attribute */
            int              element)	/* I  - Element number */
{
  char	value[1024],			/* Value string */
	temp[1024];			/* Temporary string */


  if (!_ippFileReadToken(f, temp, sizeof(temp)))

  switch (ippGetValueTag(*attr))
  {
    case IPP_TAG_BOOLEAN :
        return (ippSetBoolean(ipp, attr, element, !_cups_strcasecmp(value, "true")));

    case IPP_TAG_ENUM :
    case IPP_TAG_INTEGER :
        return (ippSetInteger(ipp, attr, element, (int)strtol(value, NULL, 0)));

    case IPP_TAG_DATE :
        {
          int	year,			/* Year */
		month,			/* Month */
		day,			/* Day of month */
		hour,			/* Hour */
		minute,			/* Minute */
		second,			/* Second */
		utc_offset = 0;		/* Timezone offset from UTC */
          ipp_uchar_t date[11];		/* dateTime value */

          if (*value == 'P')
          {
           /*
            * Time period...
            */

            time_t	curtime;	/* Current time in seconds */
            int		period = 0,	/* Current period value */
			saw_T = 0;	/* Saw time separator */

            curtime = time(NULL);

            for (valueptr = value + 1; *valueptr; valueptr ++)
            {
              if (isdigit(*valueptr & 255))
              {
                period = (int)strtol(valueptr, &valueptr, 10);

                if (!valueptr || period < 0)
                {
		  report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
		  return (0);
		}
              }

              if (*valueptr == 'Y')
              {
                curtime += 365 * 86400 * period;
                period  = 0;
              }
              else if (*valueptr == 'M')
              {
                if (saw_T)
                  curtime += 60 * period;
                else
                  curtime += 30 * 86400 * period;

                period = 0;
              }
              else if (*valueptr == 'D')
              {
                curtime += 86400 * period;
                period  = 0;
              }
              else if (*valueptr == 'H')
              {
                curtime += 3600 * period;
                period  = 0;
              }
              else if (*valueptr == 'S')
              {
                curtime += period;
                period = 0;
              }
              else if (*valueptr == 'T')
              {
                saw_T  = 1;
                period = 0;
              }
              else
	      {
		report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
		return (0);
	      }
	    }

	    return (ippSetDate(ipp, attr, element, ippTimeToDate(curtime)));
          }
          else if (sscanf(value, "%d-%d-%dT%d:%d:%d%d", &year, &month, &day, &hour, &minute, &second, &utc_offset) < 6)
          {
           /*
            * Date/time value did not parse...
            */

	    report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
	    return (0);
          }

          date[0] = (ipp_uchar_t)(year >> 8);
          date[1] = (ipp_uchar_t)(year & 255);
	break;

    case IPP_TAG_STRING :
        return (ippSetOctetString(ipp, attr, element, value, (int)strlen(value)));
        break;

    case IPP_TAG_TEXTLANG :
	    return (ippSetResolution(ipp, attr, element, IPP_RES_PER_CM, xres, yres));
	  else
	    return (ippSetResolution(ipp, attr, element, (ipp_res_t)0, xres, yres));
	}

    case IPP_TAG_RANGE :
	{
	  int	lower,			/* Lower value */
		upper;			/* Upper value */

          if (sscanf(value, "%d-%d", &lower, &upper) != 2)
          {
	    report_error(f, v, user_data, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
	    return (0);
	  }

	  return (ippSetRange(ipp, attr, element, lower, upper));
	}

    case IPP_TAG_STRING :
        valuelen = strlen(value);

        if (value[0] == '<' && value[strlen(value) - 1] == '>')
        {
          if (valuelen & 1)
          {
	    report_error(f, v, user_data, "Bad octetString value on line %d of \"%s\".", f->linenum, f->filename);
	    return (0);
          }

          valueptr = value + 1;
          tempptr  = temp;

          while (*valueptr && *valueptr != '>')
          {
	    if (!isxdigit(valueptr[0] & 255) || !isxdigit(valueptr[1] & 255))
	    {
	      report_error(f, v, user_data, "Bad octetString value on line %d of \"%s\".", f->linenum, f->filename);
	      return (0);
	    }

            if (valueptr[0] >= '0' && valueptr[0] <= '9')
              *tempptr = (char)((valueptr[0] - '0') << 4);
	    else
              *tempptr = (char)((tolower(valueptr[0]) - 'a' + 10) << 4);

            if (valueptr[1] >= '0' && valueptr[1] <= '9')
              *tempptr |= (valueptr[1] - '0');
	    else
              *tempptr |= (tolower(valueptr[1]) - 'a' + 10);

            tempptr ++;
          }

          return (ippSetOctetString(ipp, attr, element, temp, (int)(tempptr - temp)));
        }
        else
          return (ippSetOctetString(ipp, attr, element, value, (int)valuelen));

    case IPP_TAG_TEXTLANG :
    case IPP_TAG_NAMELANG :
    case IPP_TAG_TEXT :
    case IPP_TAG_NAME :
    case IPP_TAG_KEYWORD :
    case IPP_TAG_URI :
    case IPP_TAG_URISCHEME :
    case IPP_TAG_CHARSET :
    case IPP_TAG_LANGUAGE :
    case IPP_TAG_MIMETYPE :
        return (ippSetString(ipp, attr, element, value));

    case IPP_TAG_BEGIN_COLLECTION :
        {
          int	status;			/* Add status */
          ipp_t *col;			/* Collection value */

          if (strcmp(value, "{"))
          {
	    report_error(f, v, user_data, "Bad collection value on line %d of \"%s\".", f->linenum, f->filename);
	    return (0);
          }

          if ((col = parse_collection(f, v, user_data)) == NULL)
            return (0);

	  status = ippSetCollection(ipp, attr, element, col);
	  ippDelete(col);

	  return (status);
	}

    default :
        report_error(f, v, user_data, "Unsupported value on line %d of \"%s\".", f->linenum, f->filename);
        return (0);
  }
}


/*
 * 'report_error()' - Report an error.
 */

static void
report_error(
    _ipp_file_t *f,			/* I - IPP data file */
    _ipp_vars_t *v,			/* I - Error callback function, if any */
    void        *user_data,		/* I - User data pointer */
    const char  *message,		/* I - Printf-style message */
    ...)				/* I - Additional arguments as needed */
{
  char		buffer[8192];		/* Formatted string */
  va_list	ap;			/* Argument pointer */


  va_start(ap, message);
  vsnprintf(buffer, sizeof(buffer), message, ap);
  va_end(ap);

  if (v->errorcb)
    (*v->errorcb)(f, user_data, buffer);
  else
    fprintf(stderr, "%s\n", buffer);
}

<MSG> Support <hex> strings in IPP files.

<DFF> @@ -1,7 +1,7 @@
 /*
  * IPP data file parsing functions.
  *
- * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 2007-2019 by Apple Inc.
  * Copyright © 1997-2007 by Easy Software Products.
  *
  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
@@ -297,13 +297,16 @@ _ippFileReadToken(_ipp_file_t *f,	/* I - File to read from */
       DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before whitespace.", token));
       return (1);
     }
-    else if (!quote && (ch == '\'' || ch == '\"'))
+    else if (!quote && (ch == '\'' || ch == '\"' || ch == '<'))
     {
      /*
       * Start of quoted text or regular expression...
       */
 
-      quote = ch;
+      if (ch == '<')
+        quote = '>';
+      else
+        quote = ch;
 
       DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote, (long)cupsFileTell(f->fp)));
     }
@@ -382,23 +385,26 @@ _ippFileReadToken(_ipp_file_t *f,	/* I - File to read from */
 	  ch = '\v';
       }
 
-      if (tokptr < tokend)
+      if (quote != '>' || !isspace(ch & 255))
       {
-       /*
-	* Add to current token...
-	*/
+	if (tokptr < tokend)
+	{
+	 /*
+	  * Add to current token...
+	  */
 
-	*tokptr++ = (char)ch;
-      }
-      else
-      {
-       /*
-	* Token too long...
-	*/
+	  *tokptr++ = (char)ch;
+	}
+	else
+	{
+	 /*
+	  * Token too long...
+	  */
 
-	*tokptr = '\0';
-	DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token));
-	return (0);
+	  *tokptr = '\0';
+	  DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token));
+	  return (0);
+	}
       }
     }
 
@@ -549,8 +555,11 @@ parse_value(_ipp_file_t      *f,	/* I  - IPP data file */
             ipp_attribute_t  **attr,	/* IO - IPP attribute */
             int              element)	/* I  - Element number */
 {
-  char	value[1024],			/* Value string */
-	temp[1024];			/* Temporary string */
+  char		value[2049],		/* Value string */
+		*valueptr,		/* Pointer into value string */
+		temp[2049],		/* Temporary string */
+		*tempptr;		/* Pointer into temporary string */
+  size_t	valuelen;		/* Length of value */
 
 
   if (!_ippFileReadToken(f, temp, sizeof(temp)))
@@ -658,7 +667,44 @@ parse_value(_ipp_file_t      *f,	/* I  - IPP data file */
 	break;
 
     case IPP_TAG_STRING :
-        return (ippSetOctetString(ipp, attr, element, value, (int)strlen(value)));
+        valuelen = strlen(value);
+
+        if (value[0] == '<' && value[strlen(value) - 1] == '>')
+        {
+          if (valuelen & 1)
+          {
+	    report_error(f, v, user_data, "Bad ATTR octetString value on line %d of \"%s\".", f->linenum, f->filename);
+	    return (0);
+          }
+
+          valueptr = value + 1;
+          tempptr  = temp;
+
+          while (*valueptr && *valueptr != '>')
+          {
+	    if (!isxdigit(valueptr[0] & 255) || !isxdigit(valueptr[1] & 255))
+	    {
+	      report_error(f, v, user_data, "Bad ATTR octetString value on line %d of \"%s\".", f->linenum, f->filename);
+	      return (0);
+	    }
+
+            if (valueptr[0] >= '0' && valueptr[0] <= '9')
+              *tempptr = (char)((valueptr[0] - '0') << 4);
+	    else
+              *tempptr = (char)((tolower(valueptr[0]) - 'a' + 10) << 4);
+
+            if (valueptr[1] >= '0' && valueptr[1] <= '9')
+              *tempptr |= (valueptr[1] - '0');
+	    else
+              *tempptr |= (tolower(valueptr[1]) - 'a' + 10);
+
+            tempptr ++;
+          }
+
+          return (ippSetOctetString(ipp, attr, element, temp, (int)(tempptr - temp)));
+        }
+        else
+          return (ippSetOctetString(ipp, attr, element, value, (int)valuelen));
         break;
 
     case IPP_TAG_TEXTLANG :

{'addition_count': 66, 'commit_subject': 'Support <hex> strings in IPP files.', 'deletion_count': 20, 'file_extension': '.c', 'lang': 'c', 'license': 'apache-2.0', 'repo_name': 'apple/cups'}