Пример #1
0
static int				/* O - 0 = success, -1 = error */
help_load_file(
    help_index_t *hi,			/* I - Index */
    const char   *filename,		/* I - Filename */
    const char   *relative,		/* I - Relative path */
    time_t       mtime)			/* I - Modification time */
{
  cups_file_t	*fp;			/* HTML file */
  help_node_t	*node;			/* Current node */
  char		line[1024],		/* Line from file */
		temp[1024],		/* Temporary word */
                section[1024],		/* Section */
		*ptr,			/* Pointer into line */
		*anchor,		/* Anchor name */
		*text;			/* Text for anchor */
  off_t		offset;			/* File offset */
  char		quote;			/* Quote character */
  help_word_t	*word;			/* Current word */
  int		wordlen;		/* Length of word */


  DEBUG_printf(("2help_load_file(hi=%p, filename=\"%s\", relative=\"%s\", "
                "mtime=%ld)", hi, filename, relative, mtime));

  if ((fp = cupsFileOpen(filename, "r")) == NULL)
    return (-1);

  node   = NULL;
  offset = 0;

  strlcpy(section, "Other", sizeof(section));

  while (cupsFileGets(fp, line, sizeof(line)))
  {
   /*
    * Look for "<TITLE>", "<A NAME", or "<!-- SECTION:" prefix...
    */

    if (!_cups_strncasecmp(line, "<!-- SECTION:", 13))
    {
     /*
      * Got section line, copy it!
      */

      for (ptr = line + 13; isspace(*ptr & 255); ptr ++);

      strlcpy(section, ptr, sizeof(section));
      if ((ptr = strstr(section, "-->")) != NULL)
      {
       /*
        * Strip comment stuff from end of line...
	*/

        for (*ptr-- = '\0'; ptr > line && isspace(*ptr & 255); *ptr-- = '\0');

	if (isspace(*ptr & 255))
	  *ptr = '\0';
      }
      continue;
    }

    for (ptr = line; (ptr = strchr(ptr, '<')) != NULL;)
    {
      ptr ++;

      if (!_cups_strncasecmp(ptr, "TITLE>", 6))
      {
       /*
        * Found the title...
	*/

	anchor = NULL;
	ptr += 6;
      }
      else if (!_cups_strncasecmp(ptr, "A NAME=", 7))
      {
       /*
        * Found an anchor...
	*/

        ptr += 7;

	if (*ptr == '\"' || *ptr == '\'')
	{
	 /*
	  * Get quoted anchor...
	  */

	  quote  = *ptr;
          anchor = ptr + 1;
	  if ((ptr = strchr(anchor, quote)) != NULL)
	    *ptr++ = '\0';
	  else
	    break;
	}
	else
	{
	 /*
	  * Get unquoted anchor...
	  */

          anchor = ptr + 1;

	  for (ptr = anchor; *ptr && *ptr != '>' && !isspace(*ptr & 255); ptr ++);

	  if (*ptr)
	    *ptr++ = '\0';
	  else
	    break;
	}

       /*
        * Got the anchor, now lets find the end...
	*/

        while (*ptr && *ptr != '>')
	  ptr ++;

        if (*ptr != '>')
	  break;

        ptr ++;
      }
      else
        continue;

     /*
      * Now collect text for the link...
      */

      text = ptr;
      while ((ptr = strchr(text, '<')) == NULL)
      {
	ptr = text + strlen(text);
	if (ptr >= (line + sizeof(line) - 2))
	  break;

        *ptr++ = ' ';

        if (!cupsFileGets(fp, ptr, sizeof(line) - (ptr - line) - 1))
	  break;
      }

      *ptr = '\0';

      if (node)
	node->length = offset - node->offset;

      if (!*text)
      {
        node = NULL;
        break;
      }

      if ((node = helpFindNode(hi, relative, anchor)) != NULL)
      {
       /*
	* Node already in the index, so replace the text and other
	* data...
	*/

        cupsArrayRemove(hi->nodes, node);

        if (node->section)
	  free(node->section);

	if (node->text)
	  free(node->text);

        if (node->words)
	{
	  for (word = (help_word_t *)cupsArrayFirst(node->words);
	       word;
	       word = (help_word_t *)cupsArrayNext(node->words))
	    help_delete_word(word);

	  cupsArrayDelete(node->words);
	  node->words = NULL;
	}

	node->section = section[0] ? strdup(section) : NULL;
	node->text    = strdup(text);
	node->mtime   = mtime;
	node->offset  = offset;
	node->score   = 0;
      }
      else
      {
       /*
	* New node...
	*/

        node = help_new_node(relative, anchor, section, text, mtime, offset, 0);
      }

     /*
      * Go through the text value and replace tabs and newlines with
      * whitespace and eliminate extra whitespace...
      */

      for (ptr = node->text, text = node->text; *ptr;)
	if (isspace(*ptr & 255))
	{
	  while (isspace(*ptr & 255))
	    ptr ++;

	  *text++ = ' ';
        }
	else if (text != ptr)
	  *text++ = *ptr++;
	else
	{
	  text ++;
	  ptr ++;
	}

      *text = '\0';

     /*
      * (Re)add the node to the array...
      */

      cupsArrayAdd(hi->nodes, node);

      if (!anchor)
        node = NULL;
      break;
    }

    if (node)
    {
     /*
      * Scan this line for words...
      */

      for (ptr = line; *ptr; ptr ++)
      {
       /*
	* Skip HTML stuff...
	*/

	if (*ptr == '<')
	{
          if (!strncmp(ptr, "<!--", 4))
	  {
	   /*
	    * Skip HTML comment...
	    */

            if ((text = strstr(ptr + 4, "-->")) == NULL)
	      ptr += strlen(ptr) - 1;
	    else
	      ptr = text + 2;
	  }
	  else
	  {
	   /*
            * Skip HTML element...
	    */

            for (ptr ++; *ptr && *ptr != '>'; ptr ++)
	    {
	      if (*ptr == '\"' || *ptr == '\'')
	      {
		for (quote = *ptr++; *ptr && *ptr != quote; ptr ++);

		if (!*ptr)
		  ptr --;
	      }
	    }

	    if (!*ptr)
	      ptr --;
          }

          continue;
	}
	else if (*ptr == '&')
	{
	 /*
	  * Skip HTML entity...
	  */

	  for (ptr ++; *ptr && *ptr != ';'; ptr ++);

	  if (!*ptr)
	    ptr --;

	  continue;
	}
	else if (!isalnum(*ptr & 255))
          continue;

       /*
	* Found the start of a word, search until we find the end...
	*/

	for (text = ptr, ptr ++; *ptr && isalnum(*ptr & 255); ptr ++);

	wordlen = ptr - text;

        memcpy(temp, text, wordlen);
	temp[wordlen] = '\0';

        ptr --;

	if (wordlen > 1 && !bsearch(temp, help_common_words,
	                            (sizeof(help_common_words) /
				     sizeof(help_common_words[0])),
				    sizeof(help_common_words[0]),
				    (int (*)(const void *, const void *))
				        _cups_strcasecmp))
          help_add_word(node, temp);
      }
    }

   /*
    * Get the offset of the next line...
    */

    offset = cupsFileTell(fp);
  }

  cupsFileClose(fp);

  if (node)
    node->length = offset - node->offset;

  return (0);
}
Пример #2
0
static int				/* O - Status */
read_write_tests(int compression)	/* I - Use compression? */
{
  int		i;			/* Looping var */
  cups_file_t	*fp;			/* File */
  int		status;			/* Exit status */
  char		line[1024],		/* Line from file */
		*value;			/* Directive value from line */
  int		linenum;		/* Line number */
  unsigned char	readbuf[8192],		/* Read buffer */
		writebuf[8192];		/* Write buffer */
  int		byte;			/* Byte from file */
  off_t		length;			/* Length of file */
  static const char *partial_line = "partial line";
					/* Partial line */


 /*
  * No errors so far...
  */

  status = 0;

 /*
  * Initialize the write buffer with random data...
  */

  CUPS_SRAND((unsigned)time(NULL));

  for (i = 0; i < (int)sizeof(writebuf); i ++)
    writebuf[i] = CUPS_RAND();

 /*
  * cupsFileOpen(write)
  */

  printf("cupsFileOpen(write%s): ", compression ? " compressed" : "");

  fp = cupsFileOpen(compression ? "testfile.dat.gz" : "testfile.dat",
                    compression ? "w9" : "w");
  if (fp)
  {
    puts("PASS");

   /*
    * cupsFileCompression()
    */

    fputs("cupsFileCompression(): ", stdout);

    if (cupsFileCompression(fp) == compression)
      puts("PASS");
    else
    {
      printf("FAIL (Got %d, expected %d)\n", cupsFileCompression(fp),
             compression);
      status ++;
    }

   /*
    * cupsFilePuts()
    */

    fputs("cupsFilePuts(): ", stdout);

    if (cupsFilePuts(fp, "# Hello, World\n") > 0)
      puts("PASS");
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFilePrintf()
    */

    fputs("cupsFilePrintf(): ", stdout);

    for (i = 0; i < 1000; i ++)
      if (cupsFilePrintf(fp, "TestLine %03d\n", i) < 0)
        break;

    if (i >= 1000)
      puts("PASS");
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFilePutChar()
    */

    fputs("cupsFilePutChar(): ", stdout);

    for (i = 0; i < 256; i ++)
      if (cupsFilePutChar(fp, i) < 0)
        break;

    if (i >= 256)
      puts("PASS");
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFileWrite()
    */

    fputs("cupsFileWrite(): ", stdout);

    for (i = 0; i < 10000; i ++)
      if (cupsFileWrite(fp, (char *)writebuf, sizeof(writebuf)) < 0)
        break;

    if (i >= 10000)
      puts("PASS");
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFilePuts() with partial line...
    */

    fputs("cupsFilePuts(\"partial line\"): ", stdout);

    if (cupsFilePuts(fp, partial_line) > 0)
      puts("PASS");
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFileTell()
    */

    fputs("cupsFileTell(): ", stdout);

    if ((length = cupsFileTell(fp)) == 81933283)
      puts("PASS");
    else
    {
      printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
      status ++;
    }

   /*
    * cupsFileClose()
    */

    fputs("cupsFileClose(): ", stdout);

    if (!cupsFileClose(fp))
      puts("PASS");
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }
  }
  else
  {
    printf("FAIL (%s)\n", strerror(errno));
    status ++;
  }

 /*
  * cupsFileOpen(read)
  */

  fputs("\ncupsFileOpen(read): ", stdout);

  fp = cupsFileOpen(compression ? "testfile.dat.gz" : "testfile.dat", "r");
  if (fp)
  {
    puts("PASS");

   /*
    * cupsFileGets()
    */

    fputs("cupsFileGets(): ", stdout);

    if (cupsFileGets(fp, line, sizeof(line)))
    {
      if (line[0] == '#')
        puts("PASS");
      else
      {
        printf("FAIL (Got line \"%s\", expected comment line)\n", line);
	status ++;
      }
    }
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFileCompression()
    */

    fputs("cupsFileCompression(): ", stdout);

    if (cupsFileCompression(fp) == compression)
      puts("PASS");
    else
    {
      printf("FAIL (Got %d, expected %d)\n", cupsFileCompression(fp),
             compression);
      status ++;
    }

   /*
    * cupsFileGetConf()
    */

    linenum = 1;

    fputs("cupsFileGetConf(): ", stdout);

    for (i = 0; i < 1000; i ++)
      if (!cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
        break;
      else if (_cups_strcasecmp(line, "TestLine") || !value || atoi(value) != i ||
               linenum != (i + 2))
        break;

    if (i >= 1000)
      puts("PASS");
    else if (line[0])
    {
      printf("FAIL (Line %d, directive \"%s\", value \"%s\")\n", linenum,
             line, value ? value : "(null)");
      status ++;
    }
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFileGetChar()
    */

    fputs("cupsFileGetChar(): ", stdout);

    for (i = 0; i < 256; i ++)
      if ((byte = cupsFileGetChar(fp)) != i)
        break;

    if (i >= 256)
      puts("PASS");
    else if (byte >= 0)
    {
      printf("FAIL (Got %d, expected %d)\n", byte, i);
      status ++;
    }
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFileRead()
    */

    fputs("cupsFileRead(): ", stdout);

    for (i = 0; i < 10000; i ++)
      if ((byte = cupsFileRead(fp, (char *)readbuf, sizeof(readbuf))) < 0)
        break;
      else if (memcmp(readbuf, writebuf, sizeof(readbuf)))
        break;

    if (i >= 10000)
      puts("PASS");
    else if (byte > 0)
    {
      printf("FAIL (Pass %d, ", i);

      for (i = 0; i < (int)sizeof(readbuf); i ++)
        if (readbuf[i] != writebuf[i])
	  break;

      printf("match failed at offset %d - got %02X, expected %02X)\n",
             i, readbuf[i], writebuf[i]);
    }
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }

   /*
    * cupsFileGetChar() with partial line...
    */

    fputs("cupsFileGetChar(partial line): ", stdout);

    for (i = 0; i < (int)strlen(partial_line); i ++)
      if ((byte = cupsFileGetChar(fp)) < 0)
        break;
      else if (byte != partial_line[i])
        break;

    if (!partial_line[i])
      puts("PASS");
    else
    {
      printf("FAIL (got '%c', expected '%c')\n", byte, partial_line[i]);
      status ++;
    }

   /*
    * cupsFileTell()
    */

    fputs("cupsFileTell(): ", stdout);

    if ((length = cupsFileTell(fp)) == 81933283)
      puts("PASS");
    else
    {
      printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
      status ++;
    }

   /*
    * cupsFileClose()
    */

    fputs("cupsFileClose(): ", stdout);

    if (!cupsFileClose(fp))
      puts("PASS");
    else
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
    }
  }
  else
  {
    printf("FAIL (%s)\n", strerror(errno));
    status ++;
  }

 /*
  * Remove the test file...
  */

  unlink(compression ? "testfile.dat.gz" : "testfile.dat");

 /*
  * Return the test status...
  */

  return (status);
}
Пример #3
0
Файл: log.c Проект: apple/cups
int					/* O  - 1 if log file open */
cupsdCheckLogFile(cups_file_t **lf,	/* IO - Log file */
	          const char  *logname)	/* I  - Log filename */
{
  char		backname[1024],		/* Backup log filename */
		filename[1024],		/* Formatted log filename */
		*ptr;			/* Pointer into filename */
  const char	*logptr;		/* Pointer into log filename */


 /*
  * See if we have a log file to check...
  */

  if (!lf || !logname || !logname[0])
    return (1);

 /*
  * Handle logging to stderr...
  */

  if (!strcmp(logname, "stderr"))
  {
    *lf = LogStderr;
    return (1);
  }

 /*
  * Format the filename as needed...
  */

  if (!*lf ||
      (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
       MaxLogSize > 0))
  {
   /*
    * Handle format strings...
    */

    filename[sizeof(filename) - 1] = '\0';

    if (logname[0] != '/')
    {
      strlcpy(filename, ServerRoot, sizeof(filename));
      strlcat(filename, "/", sizeof(filename));
    }
    else
      filename[0] = '\0';

    for (logptr = logname, ptr = filename + strlen(filename);
         *logptr && ptr < (filename + sizeof(filename) - 1);
	 logptr ++)
      if (*logptr == '%')
      {
       /*
        * Format spec...
	*/

        logptr ++;
	if (*logptr == 's')
	{
	 /*
	  * Insert the server name...
	  */

	  strlcpy(ptr, ServerName, sizeof(filename) - (size_t)(ptr - filename));
	  ptr += strlen(ptr);
	}
        else
	{
	 /*
	  * Otherwise just insert the character...
	  */

	  *ptr++ = *logptr;
	}
      }
      else
	*ptr++ = *logptr;

    *ptr = '\0';
  }

 /*
  * See if the log file is open...
  */

  if (!*lf)
  {
   /*
    * Nope, open the log file...
    */

    if ((*lf = cupsFileOpen(filename, "a")) == NULL)
    {
     /*
      * If the file is in CUPS_LOGDIR then try to create a missing directory...
      */

      if (!strncmp(filename, CUPS_LOGDIR, strlen(CUPS_LOGDIR)))
      {
       /*
        * Try updating the permissions of the containing log directory, using
	* the log file permissions as a basis...
	*/

        mode_t log_dir_perm = (mode_t)(0300 | LogFilePerm);
					/* LogFilePerm + owner write/search */
	if (log_dir_perm & 0040)
	  log_dir_perm |= 0010;		/* Add group search */
	if (log_dir_perm & 0004)
	  log_dir_perm |= 0001;		/* Add other search */

        cupsdCheckPermissions(CUPS_LOGDIR, NULL, log_dir_perm, RunUser, Group, 1, -1);

        *lf = cupsFileOpen(filename, "a");
      }

      if (*lf == NULL)
      {
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
        sd_journal_print(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));
#else
	syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */

        if (FatalErrors & CUPSD_FATAL_LOG)
	  cupsdEndProcess(getpid(), 0);

	return (0);
      }
    }

    if (strncmp(filename, "/dev/", 5))
    {
     /*
      * Change ownership and permissions of non-device logs...
      */

      fchown(cupsFileNumber(*lf), RunUser, Group);
      fchmod(cupsFileNumber(*lf), LogFilePerm);
    }
  }

 /*
  * Do we need to rotate the log?
  */

  if (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
      MaxLogSize > 0)
  {
   /*
    * Rotate log file...
    */

    cupsFileClose(*lf);

    strlcpy(backname, filename, sizeof(backname));
    strlcat(backname, ".O", sizeof(backname));

    unlink(backname);
    rename(filename, backname);

    if ((*lf = cupsFileOpen(filename, "a")) == NULL)
    {
#ifdef HAVE_SYSTEMD_SD_JOURNAL_H
      sd_journal_print(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));

#else
      syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno));
#endif /* HAVE_SYSTEMD_SD_JOURNAL_H */

      if (FatalErrors & CUPSD_FATAL_LOG)
	cupsdEndProcess(getpid(), 0);

      return (0);
    }

   /*
    * Change ownership and permissions of non-device logs...
    */

    fchown(cupsFileNumber(*lf), RunUser, Group);
    fchmod(cupsFileNumber(*lf), LogFilePerm);
  }

  return (1);
}
Пример #4
0
static int				/* O - Status */
random_tests(void)
{
  int		status,			/* Status of tests */
		pass,			/* Current pass */
		count,			/* Number of records read */
		record,			/* Current record */
		num_records;		/* Number of records */
  ssize_t	pos,			/* Position in file */
		expected;		/* Expected position in file */
  cups_file_t	*fp;			/* File */
  char		buffer[512];		/* Data buffer */


 /*
  * Run 4 passes, each time appending to a data file and then reopening the
  * file for reading to validate random records in the file.
  */

  for (status = 0, pass = 0; pass < 4; pass ++)
  {
   /*
    * cupsFileOpen(append)
    */

    printf("\ncupsFileOpen(append %d): ", pass);

    if ((fp = cupsFileOpen("testfile.dat", "a")) == NULL)
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
      break;
    }
    else
      puts("PASS");

   /*
    * cupsFileTell()
    */

    expected = 256 * sizeof(buffer) * pass;

    fputs("cupsFileTell(): ", stdout);
    if ((pos = cupsFileTell(fp)) != expected)
    {
      printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
	     CUPS_LLCAST pos, CUPS_LLCAST expected);
      status ++;
      break;
    }
    else
      puts("PASS");

   /*
    * cupsFileWrite()
    */

    fputs("cupsFileWrite(256 512-byte records): ", stdout);
    for (record = 0; record < 256; record ++)
    {
      memset(buffer, record, sizeof(buffer));
      if (cupsFileWrite(fp, buffer, sizeof(buffer)) < sizeof(buffer))
        break;
    }

    if (record < 256)
    {
      printf("FAIL (%d: %s)\n", record, strerror(errno));
      status ++;
      break;
    }
    else
      puts("PASS");

   /*
    * cupsFileTell()
    */

    expected += 256 * sizeof(buffer);

    fputs("cupsFileTell(): ", stdout);
    if ((pos = cupsFileTell(fp)) != expected)
    {
      printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
             CUPS_LLCAST pos, CUPS_LLCAST expected);
      status ++;
      break;
    }
    else
      puts("PASS");

    cupsFileClose(fp);

   /*
    * cupsFileOpen(read)
    */

    printf("\ncupsFileOpen(read %d): ", pass);

    if ((fp = cupsFileOpen("testfile.dat", "r")) == NULL)
    {
      printf("FAIL (%s)\n", strerror(errno));
      status ++;
      break;
    }
    else
      puts("PASS");

   /*
    * cupsFileSeek, cupsFileRead
    */

    fputs("cupsFileSeek(), cupsFileRead(): ", stdout);

    for (num_records = (pass + 1) * 256, count = (pass + 1) * 256,
             record = CUPS_RAND() % num_records;
         count > 0;
	 count --, record = (record + (CUPS_RAND() & 31) - 16 + num_records) %
	                    num_records)
    {
     /*
      * The last record is always the first...
      */

      if (count == 1)
        record = 0;

     /*
      * Try reading the data for the specified record, and validate the
      * contents...
      */

      expected = sizeof(buffer) * record;

      if ((pos = cupsFileSeek(fp, expected)) != expected)
      {
        printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
	       CUPS_LLCAST pos, CUPS_LLCAST expected);
        status ++;
	break;
      }
      else
      {
	if (cupsFileRead(fp, buffer, sizeof(buffer)) != sizeof(buffer))
	{
	  printf("FAIL (%s)\n", strerror(errno));
	  status ++;
	  break;
	}
	else if ((buffer[0] & 255) != (record & 255) ||
	         memcmp(buffer, buffer + 1, sizeof(buffer) - 1))
	{
	  printf("FAIL (Bad Data - %d instead of %d)\n", buffer[0] & 255,
	         record & 255);
	  status ++;
	  break;
	}
      }
    }

    if (count == 0)
      puts("PASS");

    cupsFileClose(fp);
  }

 /*
  * Remove the test file...
  */

  unlink("testfile.dat");

 /*
  * Return the test status...
  */

  return (status);
}
Пример #5
0
ipp_t *					/* O - Attributes */
serverLoadAttributes(
    const char *filename,		/* I - File to load */
    char       **authtype,		/* O - Authentication type, if any */
    char       **command,		/* O - Command to run, if any */
    char       **device_uri,		/* O - Device URI, if any */
    char       **make,			/* O - Manufacturer */
    char       **model,			/* O - Model */
    char       **proxy_user)		/* O - Proxy user, if any */
{
  ipp_t		*attrs;			/* Attributes to return */
  cups_file_t	*fp;			/* File */
  int		linenum = 0;		/* Current line number */
  char		attr[128],		/* Attribute name */
		token[1024],		/* Token from file */
		*tokenptr;		/* Pointer into token */
  ipp_tag_t	value;			/* Current value type */
  ipp_attribute_t *attrptr;		/* Attribute pointer */


  if ((fp = cupsFileOpen(filename, "r")) == NULL)
  {
    serverLog(SERVER_LOGLEVEL_ERROR, "Unable to open \"%s\": %s", filename, strerror(errno));
    return (NULL);
  }

  attrs = ippNew();

  while (get_token(fp, token, sizeof(token), &linenum) != NULL)
  {
    if (!_cups_strcasecmp(token, "ATTR"))
    {
     /*
      * Attribute...
      */

      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing ATTR value tag on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Bad ATTR value tag \"%s\" on line %d of \"%s\".", token, linenum, filename);
        goto load_error;
      }

      if (!get_token(fp, attr, sizeof(attr), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing ATTR name on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing ATTR value on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      attrptr = NULL;

      switch (value)
      {
	case IPP_TAG_BOOLEAN :
	    if (!_cups_strcasecmp(token, "true"))
	      attrptr = ippAddBoolean(attrs, IPP_TAG_PRINTER, attr, 1);
	    else
	      attrptr = ippAddBoolean(attrs, IPP_TAG_PRINTER, attr, (char)atoi(token));
	    break;

	case IPP_TAG_INTEGER :
	case IPP_TAG_ENUM :
	    if (!strchr(token, ','))
	      attrptr = ippAddInteger(attrs, IPP_TAG_PRINTER, value, attr, (int)strtol(token, &tokenptr, 0));
	    else
	    {
	      int	values[100],	/* Values */
			num_values = 1;	/* Number of values */

	      values[0] = (int)strtol(token, &tokenptr, 10);
	      while (tokenptr && *tokenptr &&
		     num_values < (int)(sizeof(values) / sizeof(values[0])))
	      {
		if (*tokenptr == ',')
		  tokenptr ++;
		else if (!isdigit(*tokenptr & 255) && *tokenptr != '-')
		  break;

		values[num_values] = (int)strtol(tokenptr, &tokenptr, 0);
		num_values ++;
	      }

	      attrptr = ippAddIntegers(attrs, IPP_TAG_PRINTER, value, attr, num_values, values);
	    }

	    if (!tokenptr || *tokenptr)
	    {
	      serverLog(SERVER_LOGLEVEL_ERROR, "Bad %s value \"%s\" on line %d of \"%s\".", ippTagString(value), token, linenum, filename);
              goto load_error;
	    }
	    break;

	case IPP_TAG_RESOLUTION :
	    {
	      int	xres,		/* X resolution */
			yres;		/* Y resolution */
	      ipp_res_t	units;		/* Units */
	      char	*start,		/* Start of value */
			*ptr,		/* Pointer into value */
			*next = NULL;	/* Next value */

	      for (start = token; start; start = next)
	      {
		xres = yres = (int)strtol(start, (char **)&ptr, 10);
		if (ptr > start && xres > 0)
		{
		  if (*ptr == 'x')
		    yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
		}

		if (ptr && (next = strchr(ptr, ',')) != NULL)
		  *next++ = '\0';

		if (ptr <= start || xres <= 0 || yres <= 0 || !ptr ||
		    (_cups_strcasecmp(ptr, "dpi") &&
		     _cups_strcasecmp(ptr, "dpc") &&
		     _cups_strcasecmp(ptr, "dpcm") &&
		     _cups_strcasecmp(ptr, "other")))
		{
		  serverLog(SERVER_LOGLEVEL_ERROR, "Bad resolution value \"%s\" on line %d of \"%s\".", token, linenum, filename);
                  goto load_error;
		}

		if (!_cups_strcasecmp(ptr, "dpc") || !_cups_strcasecmp(ptr, "dpcm"))
		  units = IPP_RES_PER_CM;
		else
		  units = IPP_RES_PER_INCH;

                if (attrptr)
		  ippSetResolution(attrs, &attrptr, ippGetCount(attrptr), units, xres, yres);
		else
		  attrptr = ippAddResolution(attrs, IPP_TAG_PRINTER, attr, units, xres, yres);
	      }
	    }
	    break;

	case IPP_TAG_RANGE :
	    {
	      int	lowers[4],	/* Lower value */
			uppers[4],	/* Upper values */
			num_vals;	/* Number of values */


	      num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
				lowers + 0, uppers + 0,
				lowers + 1, uppers + 1,
				lowers + 2, uppers + 2,
				lowers + 3, uppers + 3);

	      if ((num_vals & 1) || num_vals == 0)
	      {
		serverLog(SERVER_LOGLEVEL_ERROR, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token, linenum, filename);
                goto load_error;
	      }

	      attrptr = ippAddRanges(attrs, IPP_TAG_PRINTER, attr, num_vals / 2, lowers,
				     uppers);
	    }
	    break;

	case IPP_TAG_BEGIN_COLLECTION :
	    if (!strcmp(token, "{"))
	    {
	      ipp_t	*col = get_collection(fp, filename, &linenum);
				    /* Collection value */

	      if (col)
	      {
		attrptr = ippAddCollection(attrs, IPP_TAG_PRINTER, attr, col);
		ippDelete(col);
	      }
	      else
		exit(1);
	    }
	    else
	    {
	      serverLog(SERVER_LOGLEVEL_ERROR, "Bad ATTR collection value on line %d of \"%s\".", linenum, filename);
              goto load_error;
	    }

	    do
	    {
	      ipp_t	*col;			/* Collection value */
	      long	pos = cupsFileTell(fp);	/* Save position of file */

	      if (!get_token(fp, token, sizeof(token), &linenum))
		break;

	      if (strcmp(token, ","))
	      {
		cupsFileSeek(fp, pos);
		break;
	      }

	      if (!get_token(fp, token, sizeof(token), &linenum) || strcmp(token, "{"))
	      {
		serverLog(SERVER_LOGLEVEL_ERROR, "Unexpected \"%s\" on line %d of \"%s\".", token, linenum, filename);
                goto load_error;
	      }

	      if ((col = get_collection(fp, filename, &linenum)) == NULL)
		break;

	      ippSetCollection(attrs, &attrptr, ippGetCount(attrptr), col);
	    }
	    while (!strcmp(token, "{"));
	    break;

	case IPP_TAG_STRING :
	    attrptr = ippAddOctetString(attrs, IPP_TAG_PRINTER, attr, token, (int)strlen(token));
	    break;

	default :
	    serverLog(SERVER_LOGLEVEL_ERROR, "Unsupported ATTR value tag %s on line %d of \"%s\".", ippTagString(value), linenum, filename);
            goto load_error;

	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 :
	    if (!strchr(token, ','))
	      attrptr = ippAddString(attrs, IPP_TAG_PRINTER, value, attr, NULL, token);
	    else
	    {
	     /*
	      * Multiple string values...
	      */

	      int	num_values;	/* Number of values */
	      char	*values[100],	/* Values */
			*ptr;		/* Pointer to next value */


	      values[0]  = token;
	      num_values = 1;

	      for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
	      {
		if (ptr > token && ptr[-1] == '\\')
		  _cups_strcpy(ptr - 1, ptr);
		else
		{
		  *ptr++ = '\0';
		  values[num_values] = ptr;
		  num_values ++;
		  if (num_values >= (int)(sizeof(values) / sizeof(values[0])))
		    break;
		}
	      }

	      attrptr = ippAddStrings(attrs, IPP_TAG_PRINTER, value, attr, num_values, NULL, (const char **)values);
	    }
	    break;
      }

      if (!attrptr)
      {
        serverLog(SERVER_LOGLEVEL_ERROR, "Unable to add attribute on line %d of \"%s\": %s", linenum, filename, cupsLastErrorString());
        goto load_error;
      }
    }
    else if (!_cups_strcasecmp(token, "AUTHTYPE") && authtype)
    {
      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing AUTHTYPE value on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      *authtype = strdup(token);
    }
    else if (!_cups_strcasecmp(token, "COMMAND") && command)
    {
      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing COMMAND value on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      *command = strdup(token);
    }
    else if (!_cups_strcasecmp(token, "DEVICEURI") && device_uri)
    {
      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing DEVICE-URI value on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      *device_uri = strdup(token);
    }
    else if (!_cups_strcasecmp(token, "MAKE") && make)
    {
      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing MAKE value on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      *make = strdup(token);
    }
    else if (!_cups_strcasecmp(token, "MODEL") && model)
    {
      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing MODEL value on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      *model = strdup(token);
    }
    else if (!_cups_strcasecmp(token, "PROXYUSER") && proxy_user)
    {
      if (!get_token(fp, token, sizeof(token), &linenum))
      {
	serverLog(SERVER_LOGLEVEL_ERROR, "Missing PROXY-USER value on line %d of \"%s\".", linenum, filename);
        goto load_error;
      }

      *proxy_user = strdup(token);
    }
    else
    {
      serverLog(SERVER_LOGLEVEL_ERROR, "Unknown directive \"%s\" on line %d of \"%s\".", token, linenum, filename);
      goto load_error;
    }
  }

  cupsFileClose(fp);

  return (attrs);

 /*
  * If we get here something bad happened...
  */

  load_error:

  cupsFileClose(fp);

  ippDelete(attrs);

  return (NULL);
}