]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/printers.c
Mirror 1.1.x changes.
[thirdparty/cups.git] / scheduler / printers.c
index 86084e270696b03898d1fb6485a3b39126caea96..24be5a0fdb197f53e6a2a18a303676da00190227 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: printers.c,v 1.93.2.13 2002/01/29 21:18:16 mike Exp $"
+ * "$Id: printers.c,v 1.93.2.46 2003/04/03 03:33:41 mike Exp $"
  *
  *   Printer routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2002 by Easy Software Products, all rights reserved.
+ *   Copyright 1997-2003 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Easy Software Products and are protected by Federal
@@ -25,6 +25,7 @@
  *
  *   AddPrinter()           - Add a printer to the system.
  *   AddPrinterFilter()     - Add a MIME filter for a printer.
+ *   AddPrinterHistory()    - Add the current printer state to the history.
  *   AddPrinterUser()       - Add a user to the ACL.
  *   DeleteAllPrinters()    - Delete all printers from the system.
  *   DeletePrinter()        - Delete a printer from the system.
  *   LoadAllPrinters()      - Load printers from the printers.conf file.
  *   SaveAllPrinters()      - Save all printer definitions to the printers.conf
  *   SetPrinterAttrs()      - Set printer attributes based upon the PPD file.
+ *   SetPrinterReasons()    - Set/update the reasons strings.
  *   SetPrinterState()      - Update the current state of a printer.
  *   SortPrinters()         - Sort the printer list when a printer name is
  *                            changed.
  *   StopPrinter()          - Stop a printer from printing any jobs...
  *   ValidateDest()         - Validate a printer/class destination.
+ *   WritePrintcap()        - Write a pseudo-printcap file for older
+ *                            applications that need it...
+ *   write_irix_config()    - Update the config files used by the IRIX
+ *                            desktop tools.
  *   write_irix_state()     - Update the status files used by IRIX printing
  *                            desktop tools.
- *   write_printcap()       - Write a pseudo-printcap file for older
- *                            applications that need it...
  */
 
 /*
@@ -56,7 +60,6 @@
  * Local functions...
  */
 
-static void    write_printcap(void);
 #ifdef __sgi
 static void    write_irix_state(printer_t *p);
 #endif /* __sgi */
@@ -74,12 +77,12 @@ AddPrinter(const char *name)        /* I - Name of printer */
                *prev;          /* Previous printer in list */
 
 
-  DEBUG_printf(("AddPrinter(\"%s\")\n", name));
-
  /*
   * Range check input...
   */
 
+  LogMessage(L_DEBUG2, "AddPrinter(\"%s\")", name ? name : "(null)");
+
   if (name == NULL)
     return (NULL);
 
@@ -88,32 +91,35 @@ AddPrinter(const char *name)        /* I - Name of printer */
   */
 
   if ((p = calloc(1, sizeof(printer_t))) == NULL)
+  {
+    LogMessage(L_ERROR, "Unable to allocate memory for printer - %s",
+               strerror(errno));
     return (NULL);
+  }
 
-  strncpy(p->name, name, sizeof(p->name) - 1);
-  strncpy(p->hostname, ServerName, sizeof(p->hostname) - 1);
+  SetString(&p->name, name);
+  SetString(&p->info, name);
+  SetString(&p->hostname, ServerName);
 
 #ifdef AF_INET6
   if (Listeners[0].address.addr.sa_family == AF_INET6)
-    snprintf(p->uri, sizeof(p->uri), "ipp://%s:%d/printers/%s", ServerName,
-             ntohs(Listeners[0].address.ipv6.sin6_port), name);
+    SetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName,
+               ntohs(Listeners[0].address.ipv6.sin6_port), name);
   else
 #endif /* AF_INET6 */
-  snprintf(p->uri, sizeof(p->uri), "ipp://%s:%d/printers/%s", ServerName,
-           ntohs(Listeners[0].address.ipv4.sin_port), name);
+  SetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName,
+             ntohs(Listeners[0].address.ipv4.sin_port), name);
+  SetStringf(&p->device_uri, "file:/dev/null");
 
   p->state     = IPP_PRINTER_STOPPED;
   p->accepting = 0;
   p->filetype  = mimeAddType(MimeDatabase, "printer", name);
 
-  strcpy(p->job_sheets[0], "none");
-  strcpy(p->job_sheets[1], "none");
-
- /*
-  * Setup required filters and IPP attributes...
-  */
-
-  SetPrinterAttrs(p);
+  SetString(&p->job_sheets[0], "none");
+  SetString(&p->job_sheets[1], "none");
+  if (MaxPrinterHistory)
+    p->history = calloc(MaxPrinterHistory, sizeof(ipp_t *));
 
  /*
   * Insert the printer in the printer list alphabetically...
@@ -140,7 +146,7 @@ AddPrinter(const char *name)        /* I - Name of printer */
   * Write a new /etc/printcap or /var/spool/lp/pstatus file.
   */
 
-  write_printcap();
+  WritePrintcap();
 
   return (p);
 }
@@ -166,7 +172,7 @@ AddPrinterFilter(printer_t  *p,             /* I - Printer to add to */
   * Range check input...
   */
 
-  if (p == NULL || filter == NULL)
+  if (p == NULL || p->filetype == NULL || filter == NULL)
     return;
 
  /*
@@ -189,9 +195,9 @@ AddPrinterFilter(printer_t  *p,             /* I - Printer to add to */
   for (temptype = MimeDatabase->types, i = MimeDatabase->num_types;
        i > 0;
        i --, temptype ++)
-    if (((super[0] == '*' && strcmp((*temptype)->super, "printer") != 0) ||
-         strcmp((*temptype)->super, super) == 0) &&
-        (type[0] == '*' || strcmp((*temptype)->type, type) == 0))
+    if (((super[0] == '*' && strcasecmp((*temptype)->super, "printer") != 0) ||
+         strcasecmp((*temptype)->super, super) == 0) &&
+        (type[0] == '*' || strcasecmp((*temptype)->type, type) == 0))
     {
       LogMessage(L_DEBUG2, "Adding filter %s/%s %s/%s %d %s",
                  (*temptype)->super, (*temptype)->type,
@@ -202,6 +208,60 @@ AddPrinterFilter(printer_t  *p,            /* I - Printer to add to */
 }
 
 
+/*
+ * 'AddPrinterHistory()' - Add the current printer state to the history.
+ */
+
+void
+AddPrinterHistory(printer_t *p)                /* I - Printer */
+{
+  ipp_t        *history;                       /* History collection */
+
+
+ /*
+  * Stop early if we aren't keeping history data...
+  */
+
+  if (MaxPrinterHistory <= 0)
+    return;
+
+ /*
+  * Retire old history data as needed...
+  */
+
+  if (p->num_history >= MaxPrinterHistory)
+  {
+    p->num_history --;
+    ippDelete(p->history[0]);
+    memmove(p->history, p->history + 1, p->num_history * sizeof(ipp_t *));
+  }
+
+ /*
+  * Create a collection containing the current printer-state, printer-up-time,
+  * printer-state-message, and printer-state-reasons attributes.
+  */
+
+  history = ippNew();
+  ippAddInteger(history, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+                p->state);
+  ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-state-message",
+               NULL, p->state_message);
+  if (p->num_reasons == 0)
+    ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                 "printer-state-reasons", NULL,
+                p->state == IPP_PRINTER_STOPPED ? "paused" : "none");
+  else
+    ippAddStrings(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "printer-state-reasons", p->num_reasons, NULL,
+                 (const char * const *)p->reasons);
+  ippAddInteger(history, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state-time",
+                p->state_time);
+
+  p->history[p->num_history] = history;
+  p->num_history ++;
+}
+
+
 /*
  * 'AddPrinterUser()' - Add a user to the ACL.
  */
@@ -250,6 +310,12 @@ DeleteAllPrinters(void)
     if (!(p->type & CUPS_PRINTER_CLASS))
       DeletePrinter(p);
   }
+
+  if (CommonData)
+  {
+    ippDelete(CommonData);
+    CommonData = NULL;
+  }
 }
 
 
@@ -260,6 +326,7 @@ DeleteAllPrinters(void)
 void
 DeletePrinter(printer_t *p)    /* I - Printer to delete */
 {
+  int          i;              /* Looping var */
   printer_t    *current,       /* Current printer in list */
                *prev;          /* Previous printer in list */
 #ifdef __sgi
@@ -337,10 +404,7 @@ DeletePrinter(printer_t *p)        /* I - Printer to delete */
   {
     DefaultPrinter = Printers;
 
-#ifdef __sgi
-    if (DefaultPrinter)
-      write_irix_state(DefaultPrinter);
-#endif /* __sgi */
+    WritePrintcap();
   }
 
  /*
@@ -357,6 +421,17 @@ DeletePrinter(printer_t *p)        /* I - Printer to delete */
   if (p->printers != NULL)
     free(p->printers);
 
+  if (MaxPrinterHistory)
+  {
+    for (i = 0; i < p->num_history; i ++)
+      ippDelete(p->history[i]);
+
+    free(p->history);
+  }
+
+  for (i = 0; i < p->num_reasons; i ++)
+    free(p->reasons[i]);
+
   ippDelete(p->attrs);
 
   DeletePrinterFilters(p);
@@ -364,13 +439,23 @@ DeletePrinter(printer_t *p)       /* I - Printer to delete */
   FreePrinterUsers(p);
   FreeQuotas(p);
 
+  ClearString(&p->uri);
+  ClearString(&p->hostname);
+  ClearString(&p->name);
+  ClearString(&p->location);
+  ClearString(&p->make_model);
+  ClearString(&p->info);
+  ClearString(&p->job_sheets[0]);
+  ClearString(&p->job_sheets[1]);
+  ClearString(&p->device_uri);
+
   free(p);
 
  /*
   * Write a new /etc/printcap file...
   */
 
-  write_printcap();
+  WritePrintcap();
 }
 
 
@@ -416,6 +501,27 @@ DeletePrinterFilters(printer_t *p) /* I - Printer to remove from */
 }
 
 
+/*
+ * 'FindDest()' - Find a destination in the list.
+ */
+
+printer_t *                    /* O - Destination in list */
+FindDest(const char *name)     /* I - Name of printer or class to find */
+{
+  printer_t    *p;             /* Current destination */
+  int          diff;           /* Difference */
+
+
+  for (p = Printers; p != NULL; p = p->next)
+    if ((diff = strcasecmp(name, p->name)) == 0)/* name == p->name */
+      return (p);
+    else if (diff < 0)                         /* name < p->name */
+      return (NULL);
+
+  return (NULL);
+}
+
+
 /*
  * 'FindPrinter()' - Find a printer in the list.
  */
@@ -424,19 +530,15 @@ printer_t *                       /* O - Printer in list */
 FindPrinter(const char *name)  /* I - Name of printer to find */
 {
   printer_t    *p;             /* Current printer */
+  int          diff;           /* Difference */
 
 
   for (p = Printers; p != NULL; p = p->next)
-    switch (strcasecmp(name, p->name))
-    {
-      case 0 : /* name == p->name */
-          if (!(p->type & CUPS_PRINTER_CLASS))
-           return (p);
-      case 1 : /* name > p->name */
-          break;
-      case -1 : /* name < p->name */
-          return (NULL);
-    }
+    if ((diff = strcasecmp(name, p->name)) == 0 &&
+        !(p->type & CUPS_PRINTER_CLASS))       /* name == p->name */
+      return (p);
+    else if (diff < 0)                         /* name < p->name */
+      return (NULL);
 
   return (NULL);
 }
@@ -472,7 +574,7 @@ FreePrinterUsers(printer_t *p)      /* I - Printer */
 void
 LoadAllPrinters(void)
 {
-  FILE         *fp;                    /* printers.conf file */
+  cups_file_t  *fp;                    /* printers.conf file */
   int          linenum;                /* Current line number */
   int          len;                    /* Length of line */
   char         line[1024],             /* Line from file */
@@ -488,8 +590,12 @@ LoadAllPrinters(void)
   */
 
   snprintf(line, sizeof(line), "%s/printers.conf", ServerRoot);
-  if ((fp = fopen(line, "r")) == NULL)
+  if ((fp = cupsFileOpen(line, "r")) == NULL)
+  {
+    LogMessage(L_ERROR, "LoadAllPrinters: Unable to open %s - %s", line,
+               strerror(errno));
     return;
+  }
 
  /*
   * Read printer configurations until we hit EOF...
@@ -498,7 +604,7 @@ LoadAllPrinters(void)
   linenum = 0;
   p       = NULL;
 
-  while (fgets(line, sizeof(line), fp) != NULL)
+  while (cupsFileGets(fp, line, sizeof(line)) != NULL)
   {
     linenum ++;
 
@@ -557,6 +663,8 @@ LoadAllPrinters(void)
 
         line[len - 1] = '\0';
 
+        LogMessage(L_DEBUG, "LoadAllPrinters: Loading printer %s...", value);
+
         p = AddPrinter(value);
        p->accepting = 1;
        p->state     = IPP_PRINTER_IDLE;
@@ -580,6 +688,7 @@ LoadAllPrinters(void)
       if (p != NULL)
       {
         SetPrinterAttrs(p);
+       AddPrinterHistory(p);
         p = NULL;
       }
       else
@@ -596,11 +705,11 @@ LoadAllPrinters(void)
       return;
     }
     else if (strcmp(name, "Info") == 0)
-      strncpy(p->info, value, sizeof(p->info) - 1);
+      SetString(&p->info, value);
     else if (strcmp(name, "Location") == 0)
-      strncpy(p->location, value, sizeof(p->location) - 1);
+      SetString(&p->location, value);
     else if (strcmp(name, "DeviceURI") == 0)
-      strncpy(p->device_uri, value, sizeof(p->device_uri) - 1);
+      SetString(&p->device_uri, value);
     else if (strcmp(name, "State") == 0)
     {
      /*
@@ -621,7 +730,7 @@ LoadAllPrinters(void)
       while (isspace(*value))
         value ++;
 
-      strncpy(p->state_message, value, sizeof(p->state_message) - 1);
+      strlcpy(p->state_message, value, sizeof(p->state_message));
     }
     else if (strcmp(name, "Accepting") == 0)
     {
@@ -645,7 +754,7 @@ LoadAllPrinters(void)
       if (*valueptr)
         *valueptr++ = '\0';
 
-      strncpy(p->job_sheets[0], value, sizeof(p->job_sheets[0]) - 1);
+      SetString(&p->job_sheets[0], value);
 
       while (isspace(*valueptr))
         valueptr ++;
@@ -657,7 +766,7 @@ LoadAllPrinters(void)
        if (*valueptr)
           *valueptr++ = '\0';
 
-       strncpy(p->job_sheets[1], value, sizeof(p->job_sheets[1]) - 1);
+       SetString(&p->job_sheets[1], value);
       }
     }
     else if (strcmp(name, "AllowUser") == 0)
@@ -687,7 +796,7 @@ LoadAllPrinters(void)
     }
   }
 
-  fclose(fp);
+  cupsFileClose(fp);
 }
 
 
@@ -700,7 +809,7 @@ void
 SaveAllPrinters(void)
 {
   int          i;                      /* Looping var */
-  FILE         *fp;                    /* printers.conf file */
+  cups_file_t  *fp;                    /* printers.conf file */
   char         temp[1024];             /* Temporary string */
   char         backup[1024];           /* printers.conf.O file */
   printer_t    *printer;               /* Current printer class */
@@ -718,7 +827,7 @@ SaveAllPrinters(void)
   if (rename(temp, backup))
     LogMessage(L_ERROR, "Unable to backup printers.conf - %s", strerror(errno));
 
-  if ((fp = fopen(temp, "w")) == NULL)
+  if ((fp = cupsFileOpen(temp, "w")) == NULL)
   {
     LogMessage(L_ERROR, "Unable to save printers.conf - %s", strerror(errno));
 
@@ -733,8 +842,8 @@ SaveAllPrinters(void)
   * Restrict access to the file...
   */
 
-  fchown(fileno(fp), User, Group);
-  fchmod(fileno(fp), 0600);
+  fchown(cupsFileNumber(fp), User, Group);
+  fchmod(cupsFileNumber(fp), ConfigFilePerm);
 
  /*
   * Write a small header to the file...
@@ -744,8 +853,8 @@ SaveAllPrinters(void)
   curdate = gmtime(&curtime);
   strftime(temp, sizeof(temp) - 1, CUPS_STRFTIME_FORMAT, curdate);
 
-  fputs("# Printer configuration file for " CUPS_SVERSION "\n", fp);
-  fprintf(fp, "# Written by cupsd on %s\n", temp);
+  cupsFilePuts(fp, "# Printer configuration file for " CUPS_SVERSION "\n");
+  cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
 
  /*
   * Write each local printer known to the system...
@@ -767,55 +876,55 @@ SaveAllPrinters(void)
     */
 
     if (printer == DefaultPrinter)
-      fprintf(fp, "<DefaultPrinter %s>\n", printer->name);
+      cupsFilePrintf(fp, "<DefaultPrinter %s>\n", printer->name);
     else
-      fprintf(fp, "<Printer %s>\n", printer->name);
+      cupsFilePrintf(fp, "<Printer %s>\n", printer->name);
 
-    if (printer->info[0])
-      fprintf(fp, "Info %s\n", printer->info);
+    if (printer->info)
+      cupsFilePrintf(fp, "Info %s\n", printer->info);
 
-    if (printer->location[0])
-      fprintf(fp, "Location %s\n", printer->location);
+    if (printer->location)
+      cupsFilePrintf(fp, "Location %s\n", printer->location);
 
-    if (printer->device_uri[0])
-      fprintf(fp, "DeviceURI %s\n", printer->device_uri);
+    if (printer->device_uri)
+      cupsFilePrintf(fp, "DeviceURI %s\n", printer->device_uri);
 
     if (printer->state == IPP_PRINTER_STOPPED)
     {
-      fputs("State Stopped\n", fp);
-      fprintf(fp, "StateMessage %s\n", printer->state_message);
+      cupsFilePuts(fp, "State Stopped\n");
+      cupsFilePrintf(fp, "StateMessage %s\n", printer->state_message);
     }
     else
-      fputs("State Idle\n", fp);
+      cupsFilePuts(fp, "State Idle\n");
 
     if (printer->accepting)
-      fputs("Accepting Yes\n", fp);
+      cupsFilePuts(fp, "Accepting Yes\n");
     else
-      fputs("Accepting No\n", fp);
+      cupsFilePuts(fp, "Accepting No\n");
 
-    fprintf(fp, "JobSheets %s %s\n", printer->job_sheets[0],
+    cupsFilePrintf(fp, "JobSheets %s %s\n", printer->job_sheets[0],
             printer->job_sheets[1]);
 
-    fprintf(fp, "QuotaPeriod %d\n", printer->quota_period);
-    fprintf(fp, "PageLimit %d\n", printer->page_limit);
-    fprintf(fp, "KLimit %d\n", printer->k_limit);
+    cupsFilePrintf(fp, "QuotaPeriod %d\n", printer->quota_period);
+    cupsFilePrintf(fp, "PageLimit %d\n", printer->page_limit);
+    cupsFilePrintf(fp, "KLimit %d\n", printer->k_limit);
 
     for (i = 0; i < printer->num_users; i ++)
-      fprintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow",
+      cupsFilePrintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow",
               printer->users[i]);
 
-    fputs("</Printer>\n", fp);
+    cupsFilePuts(fp, "</Printer>\n");
+
 #ifdef __sgi
     /*
      * Make IRIX desktop & printer status happy
      */
 
     write_irix_state(printer);
-
 #endif /* __sgi */
   }
 
-  fclose(fp);
+  cupsFileClose(fp);
 }
 
 
@@ -842,11 +951,13 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
   ppd_option_t *input_slot,            /* InputSlot options */
                *media_type,            /* MediaType options */
                *page_size,             /* PageSize options */
-               *output_bin;            /* OutputBin options */
+               *output_bin,            /* OutputBin options */
+               *media_quality;         /* EFMediaQualityMode options */
+  ppd_attr_t   *ppdattr;               /* PPD attribute */
   ipp_attribute_t *attr;               /* Attribute data */
   ipp_value_t  *val;                   /* Attribute value */
-  int          nups[3] =               /* number-up-supported values */
-               { 1, 2, 4 };
+  int          nups[] =                /* number-up-supported values */
+               { 1, 2, 4, 6, 9, 16 };
   ipp_orient_t orients[4] =            /* orientation-requested-supported values */
                {
                  IPP_PORTRAIT,
@@ -926,6 +1037,15 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
                  "koi8-r",
                  "koi8-u",
                };
+  const char   *compressions[] =
+               {
+#ifdef HAVE_LIBZ
+                 "none",
+                 "gzip"
+#else
+                 "none"
+#endif /* HAVE_LIBZ */
+               };
   int          num_finishings;
   ipp_finish_t finishings[5];
   const char   *multiple_document_handling[] =
@@ -934,13 +1054,97 @@ SetPrinterAttrs(printer_t *p)            /* I - Printer to setup */
                  "separate-documents-collated-copies"
                };
 #ifdef __sgi
-  FILE         *fp;            /* Interface script file */
+  cups_file_t  *fp;            /* Interface script file */
 #endif /* __sgi */
 
 
   DEBUG_printf(("SetPrinterAttrs: entering name = %s, type = %x\n", p->name,
                 p->type));
 
+ /*
+  * Make sure that we have the common attributes defined...
+  */
+
+  if (!CommonData)
+  {
+    CommonData = ippNew();
+
+    ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                "pdl-override-supported", NULL, "not-attempted");
+    ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
+                 NULL, versions);
+    ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported",
+                   sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, (int *)ops);
+    ippAddBoolean(CommonData, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 1);
+    ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                  "multiple-operation-time-out", 60);
+    ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "multiple-document-handling-supported",
+                  sizeof(multiple_document_handling) /
+                     sizeof(multiple_document_handling[0]), NULL,
+                 multiple_document_handling);
+    ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET, "charset-configured",
+                NULL, DefaultCharset);
+    ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET, "charset-supported",
+                  sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
+    ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
+                "natural-language-configured", NULL, DefaultLanguage);
+    ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
+                "generated-natural-language-supported", NULL, DefaultLanguage);
+    ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
+                "document-format-default", NULL, "application/octet-stream");
+    ippAddStrings(CommonData, IPP_TAG_PRINTER,
+                  (ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY),
+                  "document-format-supported", NumMimeTypes, NULL, MimeTypes);
+    ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                 "compression-supported",
+                 sizeof(compressions) / sizeof(compressions[0]),
+                 NULL, compressions);
+    ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                  "job-priority-supported", 100);
+    ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                  "job-priority-default", 50);
+    ippAddRange(CommonData, IPP_TAG_PRINTER, "copies-supported", 1, MaxCopies);
+    ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                  "copies-default", 1);
+    ippAddBoolean(CommonData, IPP_TAG_PRINTER, "page-ranges-supported", 1);
+    ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                   "number-up-supported", sizeof(nups) / sizeof(nups[0]), nups);
+    ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                  "number-up-default", 1);
+    ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+                   "orientation-requested-supported", 4, (int *)orients);
+    ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+                  "orientation-requested-default", IPP_PORTRAIT);
+
+    if (NumBanners > 0)
+    {
+     /*
+      * Setup the job-sheets-supported and job-sheets-default attributes...
+      */
+
+      if (Classification && !ClassifyOverride)
+       attr = ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                           "job-sheets-supported", NULL, Classification);
+      else
+       attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                            "job-sheets-supported", NumBanners + 1, NULL, NULL);
+
+      if (attr == NULL)
+       LogMessage(L_EMERG, "SetPrinterAttrs: Unable to allocate memory for "
+                            "job-sheets-supported attribute: %s!",
+                  strerror(errno));
+      else if (!Classification || ClassifyOverride)
+      {
+       attr->values[0].string.text = strdup("none");
+
+       for (i = 0; i < NumBanners; i ++)
+         attr->values[i + 1].string.text = strdup(Banners[i].name);
+      }
+    }
+  }
+
  /*
   * Clear out old filters and add a filter from application/vnd.cups-raw to
   * printer/name to handle "raw" printing by users.
@@ -988,57 +1192,11 @@ SetPrinterAttrs(printer_t *p)            /* I - Printer to setup */
   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL,
                p->name);
   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
-               NULL, p->location);
+               NULL, p->location ? p->location : "");
   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
-               NULL, p->info);
+               NULL, p->info ? p->info : "");
   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info",
                NULL, p->uri);
-  ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-               "pdl-override-supported", NULL, "not-attempted");
-  ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
-               NULL, versions);
-  ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported",
-                 sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, (int *)ops);
-  ippAddBoolean(p->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 1);
-  ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "multiple-operation-time-out", 60);
-  ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                "multiple-document-handling-supported",
-                sizeof(multiple_document_handling) /
-                   sizeof(multiple_document_handling[0]), NULL,
-               multiple_document_handling);
-  ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET, "charset-configured",
-               NULL, DefaultCharset);
-  ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET, "charset-supported",
-                sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
-  ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
-               "natural-language-configured", NULL, DefaultLanguage);
-  ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
-               "generated-natural-language-supported", NULL, DefaultLanguage);
-  ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
-               "document-format-default", NULL, "application/octet-stream");
-  ippAddStrings(p->attrs, IPP_TAG_PRINTER,
-                (ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY),
-                "document-format-supported", NumMimeTypes, NULL, MimeTypes);
-  ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-               "compression-supported", NULL, "none");
-  ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "job-priority-supported", 100);
-  ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "job-priority-default", 50);
-  ippAddRange(p->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 65535);
-  ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "copies-default", 1);
-  ippAddBoolean(p->attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
-  ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                 "number-up-supported", 3, nups);
-  ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "number-up-default", 1);
-  ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
-                 "orientation-requested-supported", 4, (int *)orients);
-  ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
-                "orientation-requested-default", IPP_PORTRAIT);
 
   if (p->num_users)
   {
@@ -1059,48 +1217,28 @@ SetPrinterAttrs(printer_t *p)           /* I - Printer to setup */
   ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                 "job-page-limit", p->page_limit);
 
-  if (NumBanners > 0)
+  if (NumBanners > 0 && !(p->type & CUPS_PRINTER_REMOTE))
   {
    /*
-    * Setup the job-sheets-supported and job-sheets-default attributes...
+    * Setup the job-sheets-default attribute...
     */
 
-    if (Classification[0] && !ClassifyOverride)
-      attr = ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                         "job-sheets-supported", NULL, Classification);
-    else
-      attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                          "job-sheets-supported", NumBanners + 1, NULL, NULL);
-
-    if (attr == NULL)
-      LogMessage(L_EMERG, "SetPrinterAttrs: Unable to allocate memory for "
-                          "job-sheets-supported attribute: %s!",
-                strerror(errno));
-    else if (!Classification[0] || ClassifyOverride)
-    {
-      attr->values[0].string.text = strdup("none");
-
-      for (i = 0; i < NumBanners; i ++)
-       attr->values[i + 1].string.text = strdup(Banners[i].name);
-    }
+    attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                        "job-sheets-default", 2, NULL, NULL);
 
-    if (!(p->type & CUPS_PRINTER_REMOTE))
+    if (attr != NULL)
     {
-      attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
-                          "job-sheets-default", 2, NULL, NULL);
-
-      if (attr != NULL)
-      {
-       attr->values[0].string.text = strdup(Classification[0] ?
-                                            Classification : p->job_sheets[0]);
-       attr->values[1].string.text = strdup(Classification[0] ?
-                                            Classification : p->job_sheets[1]);
-      }
+      attr->values[0].string.text = strdup(Classification ?
+                                          Classification : p->job_sheets[0]);
+      attr->values[1].string.text = strdup(Classification ?
+                                          Classification : p->job_sheets[1]);
     }
   }
 
   printer_type = p->type;
 
+  p->raw = 0;
+
   if (p->type & CUPS_PRINTER_REMOTE)
   {
    /*
@@ -1109,6 +1247,8 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
 
     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
                  "printer-make-and-model", NULL, p->make_model);
+
+    p->raw = 1;
   }
   else
   {
@@ -1167,7 +1307,9 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
       * URI so it doesn't have a username or password in it...
       */
 
-      if (strstr(p->device_uri, "://") != NULL)
+      if (!p->device_uri)
+        strcpy(uri, "file:/dev/null");
+      else if (strstr(p->device_uri, "://") != NULL)
       {
        /*
         * http://..., ipp://..., etc.
@@ -1186,7 +1328,7 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
         * file:..., serial:..., etc.
        */
 
-        strcpy(uri, p->device_uri);
+        strlcpy(uri, p->device_uri, sizeof(uri));
       }
 
       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
@@ -1215,21 +1357,25 @@ SetPrinterAttrs(printer_t *p)           /* I - Printer to setup */
          p->type |= CUPS_PRINTER_VARIABLE;
        if (!ppd->manual_copies)
          p->type |= CUPS_PRINTER_COPIES;
+        if ((ppdattr = ppdFindAttr(ppd, "cupsFax", NULL)) != NULL)
+         if (ppdattr->value && !strcasecmp(ppdattr->value, "true"))
+           p->type |= CUPS_PRINTER_FAX;
 
        ippAddBoolean(p->attrs, IPP_TAG_PRINTER, "color-supported",
                       ppd->color_device);
        if (ppd->throughput)
          ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                        "pages-per-minute", ppd->throughput);
-       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
-                     "printer-make-and-model", NULL, ppd->nickname);
 
         if (ppd->nickname)
-          strncpy(p->make_model, ppd->nickname, sizeof(p->make_model) - 1);
+          SetString(&p->make_model, ppd->nickname);
        else if (ppd->modelname)
-          strncpy(p->make_model, ppd->modelname, sizeof(p->make_model) - 1);
+          SetString(&p->make_model, ppd->modelname);
        else
-         strcpy(p->make_model, "Bad PPD File");
+         SetString(&p->make_model, "Bad PPD File");
+
+       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+                     "printer-make-and-model", NULL, p->make_model);
 
        /*
        * Add media options from the PPD file...
@@ -1246,37 +1392,56 @@ SetPrinterAttrs(printer_t *p)           /* I - Printer to setup */
        if ((page_size = ppdFindOption(ppd, "PageSize")) != NULL)
          num_media += page_size->num_choices;
 
-       attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                             "media-supported", num_media, NULL, NULL);
-        if (attr != NULL)
-       {
-         val = attr->values;
-
-         if (input_slot != NULL)
-           for (i = 0; i < input_slot->num_choices; i ++, val ++)
-             val->string.text = strdup(input_slot->choices[i].choice);
-
-         if (media_type != NULL)
-           for (i = 0; i < media_type->num_choices; i ++, val ++)
-             val->string.text = strdup(media_type->choices[i].choice);
+       if ((media_quality = ppdFindOption(ppd, "EFMediaQualityMode")) != NULL)
+         num_media += media_quality->num_choices;
 
-         if (page_size != NULL)
+        if (num_media == 0)
+       {
+         LogMessage(L_CRIT, "SetPrinterAttrs: The PPD file for printer %s "
+                            "contains no media options and is therefore "
+                            "invalid!", p->name);
+       }
+       else
+       {
+         attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                               "media-supported", num_media, NULL, NULL);
+          if (attr != NULL)
          {
-           for (i = 0; i < page_size->num_choices; i ++, val ++)
-             val->string.text = strdup(page_size->choices[i].choice);
-
-           ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                        NULL, page_size->defchoice);
+           val = attr->values;
+
+           if (input_slot != NULL)
+             for (i = 0; i < input_slot->num_choices; i ++, val ++)
+               val->string.text = strdup(input_slot->choices[i].choice);
+
+           if (media_type != NULL)
+             for (i = 0; i < media_type->num_choices; i ++, val ++)
+               val->string.text = strdup(media_type->choices[i].choice);
+
+           if (media_quality != NULL)
+             for (i = 0; i < media_quality->num_choices; i ++, val ++)
+               val->string.text = strdup(media_quality->choices[i].choice);
+
+           if (page_size != NULL)
+           {
+             for (i = 0; i < page_size->num_choices; i ++, val ++)
+               val->string.text = strdup(page_size->choices[i].choice);
+
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
+                          NULL, page_size->defchoice);
+            }
+           else if (input_slot != NULL)
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
+                          NULL, input_slot->defchoice);
+           else if (media_type != NULL)
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
+                          NULL, media_type->defchoice);
+           else if (media_quality != NULL)
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
+                          NULL, media_quality->defchoice);
+           else
+             ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
+                          NULL, "none");
           }
-         else if (input_slot != NULL)
-           ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                        NULL, input_slot->defchoice);
-         else if (media_type != NULL)
-           ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                        NULL, media_type->defchoice);
-         else
-           ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
-                        NULL, "none");
         }
 
        /*
@@ -1355,8 +1520,23 @@ SetPrinterAttrs(printer_t *p)            /* I - Printer to setup */
       }
       else if (access(filename, 0) == 0)
       {
+        int            pline;                  /* PPD line number */
+       ppd_status_t    pstatus;                /* PPD load status */
+
+
+        pstatus = ppdLastError(&pline);
+
        LogMessage(L_ERROR, "PPD file for %s cannot be loaded!", p->name);
 
+       if (pstatus <= PPD_ALLOC_ERROR)
+         LogMessage(L_ERROR, "%s", strerror(errno));
+        else
+         LogMessage(L_ERROR, "%s on line %d.", ppdErrorString(pstatus),
+                    pline);
+
+        LogMessage(L_INFO, "Hint: Run \"cupstestppd %s\" and fix any errors.",
+                  filename);
+
        AddPrinterFilter(p, "application/vnd.cups-postscript 0 -");
       }
       else
@@ -1380,7 +1560,8 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
                   ServerRoot, p->name);
          AddPrinterFilter(p, filename);
        }
-       else if (strncmp(p->device_uri, "ipp://", 6) == 0 &&
+       else if (p->device_uri &&
+                strncmp(p->device_uri, "ipp://", 6) == 0 &&
                 (strstr(p->device_uri, "/printers/") != NULL ||
                  strstr(p->device_uri, "/classes/") != NULL))
         {
@@ -1410,7 +1591,7 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
          * Print all files directly...
          */
 
-         AddPrinterFilter(p, "*/* 0 -");
+         p->raw = 1;
        }
        else
        {
@@ -1422,7 +1603,7 @@ SetPrinterAttrs(printer_t *p)             /* I - Printer to setup */
          ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
                        "printer-make-and-model", NULL, "Local Raw Printer");
 
-         AddPrinterFilter(p, "*/* 0 -");
+         p->raw = 1;
        }
       }
 
@@ -1445,111 +1626,114 @@ SetPrinterAttrs(printer_t *p)         /* I - Printer to setup */
 
 #ifdef __sgi
  /*
-  * Add dummy interface and GUI scripts to fool SGI's "challenged" printing
-  * tools.  First the interface script that tells the tools what kind of
-  * printer we have...
+  * Write the IRIX printer config and status files...
   */
 
-  snprintf(filename, sizeof(filename), "/var/spool/lp/interface/%s", p->name);
+  write_irix_config(p);
+  write_irix_state(p);
+#endif /* __sgi */
+}
 
-  if ((fp = fopen(filename, "w")) != NULL)
-  {
-    fputs("#!/bin/sh\n", fp);
 
-    if ((attr = ippFindAttribute(p->attrs, "printer-make-and-model",
-                                 IPP_TAG_TEXT)) != NULL)
-      fprintf(fp, "NAME=\"%s\"\n", attr->values[0].string.text);
-    else if (p->type & CUPS_PRINTER_CLASS)
-      fputs("NAME=\"Printer Class\"\n", fp);
-    else
-      fputs("NAME=\"Remote Destination\"\n", fp);
+/*
+ * 'SetPrinterReasons()' - Set/update the reasons strings.
+ */
 
-    if (p->type & CUPS_PRINTER_COLOR)
-      fputs("TYPE=ColorPostScript\n", fp);
-    else
-      fputs("TYPE=MonoPostScript\n", fp);
+void
+SetPrinterReasons(printer_t  *p,       /* I - Printer */
+                  const char *s)       /* I - Reasons strings */
+{
+  int          i;                      /* Looping var */
+  const char   *sptr;                  /* Pointer into reasons */
+  char         reason[255],            /* Reason string */
+               *rptr;                  /* Pointer into reason */
 
-    fprintf(fp, "HOSTNAME=%s\n", ServerName);
-    fprintf(fp, "HOSTPRINTER=%s\n", p->name);
 
-    fclose(fp);
+  if (s[0] == '-' || s[0] == '+')
+  {
+   /*
+    * Add/remove reasons...
+    */
 
-    chmod(filename, 0755);
-    chown(filename, User, Group);
+    sptr = s + 1;
   }
-
- /*
-  * Then the member file that tells which device file the queue is connected
-  * to...  Networked printers use "/dev/null" in this file, so that's what
-  * we use (the actual device URI can confuse some apps...)
-  */
-
-  snprintf(filename, sizeof(filename), "/var/spool/lp/member/%s", p->name);
-  if ((fp = fopen(filename, "w")) != NULL)
+  else
   {
-    fputs("/dev/null\n", fp);
+   /*
+    * Replace reasons...
+    */
 
-    fclose(fp);
+    sptr = s;
 
-    chmod(filename, 0644);
-    chown(filename, User, Group);
+    for (i = 0; i < p->num_reasons; i ++)
+      free(p->reasons[i]);
+
+    p->num_reasons = 0;
   }
 
  /*
-  * The gui_interface file is a script or program that launches a GUI
-  * option panel for the printer, using options specified on the
-  * command-line in the third argument.  The option panel must send
-  * any printing options to stdout on a single line when the user
-  * accepts them, or nothing if the user cancels the dialog.
-  *
-  * The default options panel program is /usr/bin/glpoptions, from
-  * the ESP Print Pro software.  You can select another using the
-  * PrintcapGUI option.
+  * Loop through all of the reasons...
   */
 
-  snprintf(filename, sizeof(filename), "/var/spool/lp/gui_interface/ELF/%s.gui", p->name);
-
-  if ((fp = fopen(filename, "w")) != NULL)
+  while (*sptr)
   {
-    fputs("#!/bin/sh\n", fp);
-    fprintf(fp, "%s -d %s -o \"$3\"\n", PrintcapGUI, p->name);
+   /*
+    * Skip leading whitespace and commas...
+    */
 
-    fclose(fp);
+    while (isspace(*sptr) || *sptr == ',')
+      sptr ++;
 
-    chmod(filename, 0755);
-    chown(filename, User, Group);
-  }
+    for (rptr = reason; *sptr && !isspace(*sptr) && *sptr != ','; sptr ++)
+      if (rptr < (reason + sizeof(reason) - 1))
+        *rptr++ = *sptr;
 
- /*
-  * The POD config file is needed by the printstatus command to show
-  * the printer location and device.
-  */
+    if (rptr == reason)
+      break;
 
-  snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.config", p->name);
-  if ((fp = fopen(filename, "w")) != NULL)
-  {
-    fprintf(fp, "Printer Class      | %s\n",
-            (p->type & CUPS_PRINTER_COLOR) ? "ColorPostScript" : "MonoPostScript");
-    fprintf(fp, "Printer Model      | %s\n", p->make_model);
-    fprintf(fp, "Location Code      | %s\n", p->location);
-    fprintf(fp, "Physical Location  | %s\n", p->info);
-    fprintf(fp, "Port Path          | %s\n", p->device_uri);
-    fprintf(fp, "Config Path        | /var/spool/lp/pod/%s.config\n", p->name);
-    fprintf(fp, "Active Status Path | /var/spool/lp/pod/%s.status\n", p->name);
-    fputs("Status Update Wait | 10 seconds\n", fp);
+    *rptr = '\0';
 
-    fclose(fp);
+    if (s[0] == '-')
+    {
+     /*
+      * Remove reason...
+      */
 
-    chmod(filename, 0664);
-    chown(filename, User, Group);
-  }
+      for (i = 0; i < p->num_reasons; i ++)
+        if (!strcasecmp(reason, p->reasons[i]))
+       {
+        /*
+         * Found a match, so remove it...
+         */
 
- /*
-  * Write the IRIX printer status files...
-  */
+         p->num_reasons --;
+         free(p->reasons[i]);
 
-  write_irix_state(p);
-#endif /* __sgi */
+         if (i < p->num_reasons)
+           memmove(p->reasons + i, p->reasons + i + 1,
+                   (p->num_reasons - i) * sizeof(char *));
+
+         i --;
+       }
+    }
+    else if (s[0] == '+' &&
+             p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
+    {
+     /*
+      * Add reason...
+      */
+
+      for (i = 0; i < p->num_reasons; i ++)
+        if (!strcasecmp(reason, p->reasons[i]))
+         break;
+
+      if (i >= p->num_reasons)
+      {
+        p->reasons[i] = strdup(reason);
+       p->num_reasons ++;
+      }
+    }
+  }
 }
 
 
@@ -1575,12 +1759,12 @@ SetPrinterState(printer_t    *p,        /* I - Printer to change */
   * Set the new state...
   */
 
-  old_state      = p->state;
-  p->state       = s;
-  p->state_time  = time(NULL);
+  old_state = p->state;
+  p->state  = s;
 
   if (old_state != s)
   {
+    p->state_time  = time(NULL);
     p->browse_time = 0;
 
 #ifdef __sgi
@@ -1588,6 +1772,8 @@ SetPrinterState(printer_t    *p,  /* I - Printer to change */
 #endif /* __sgi */
   }
 
+  AddPrinterHistory(p);
+
  /*
   * Save the printer configuration if a printer goes from idle or processing
   * to stopped (or visa-versa)...
@@ -1595,12 +1781,6 @@ SetPrinterState(printer_t    *p, /* I - Printer to change */
 
   if ((old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED))
     SaveAllPrinters();
-
- /*
-  * Check to see if any pending jobs can now be printed...
-  */
-
-  CheckJobs();
 }
 
 
@@ -1668,6 +1848,12 @@ StopPrinter(printer_t *p)        /* I - Printer to stop */
   job_t        *job;                   /* Active print job */
 
 
+ /*
+  * Set the printer state...
+  */
+
+  p->state = IPP_PRINTER_STOPPED;
+
  /*
   * See if we have a job printing on this printer...
   */
@@ -1694,8 +1880,6 @@ StopPrinter(printer_t *p) /* I - Printer to stop */
 
     SaveJob(job->id);
   }
-
-  p->state = IPP_PRINTER_STOPPED;
 }
 
 
@@ -1756,7 +1940,8 @@ ValidateDest(const char   *hostname,      /* I - Host name */
     return (NULL);
   else if (p != NULL)
   {
-    *dtype = p->type & CUPS_PRINTER_CLASS;
+    *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
+                        CUPS_PRINTER_REMOTE);
     return (p->name);
   }
 
@@ -1767,8 +1952,7 @@ ValidateDest(const char   *hostname,      /* I - Host name */
   if (strcasecmp(hostname, "localhost") == 0)
     hostname = ServerName;
 
-  strncpy(localname, hostname, sizeof(localname) - 1);
-  localname[sizeof(localname) - 1] = '\0';
+  strlcpy(localname, hostname, sizeof(localname));
 
   if (strcasecmp(hostname, ServerName) != 0)
   {
@@ -1808,7 +1992,8 @@ ValidateDest(const char   *hostname,      /* I - Host name */
     if (strcasecmp(p->hostname, localname) == 0 &&
         strcasecmp(p->name, resource) == 0)
     {
-      *dtype = p->type & CUPS_PRINTER_CLASS;
+      *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
+                          CUPS_PRINTER_REMOTE);
       return (p->name);
     }
 
@@ -1817,17 +2002,27 @@ ValidateDest(const char   *hostname,    /* I - Host name */
 
 
 /*
- * 'write_printcap()' - Write a pseudo-printcap file for older applications
- *                      that need it...
+ * 'WritePrintcap()' - Write a pseudo-printcap file for older applications
+ *                     that need it...
  */
 
-static void
-write_printcap(void)
+void
+WritePrintcap(void)
 {
-  FILE         *fp;            /* printcap file */
+  cups_file_t  *fp;            /* printcap file */
   printer_t    *p;             /* Current printer */
 
 
+#ifdef __sgi
+ /*
+  * Update the IRIX printer state for the default printer; if
+  * no printers remain, then the default printer file will be
+  * removed...
+  */
+
+  write_irix_state(DefaultPrinter);
+#endif /* __sgi */
+
  /*
   * See if we have a printcap file; if not, don't bother writing it.
   */
@@ -1839,9 +2034,19 @@ write_printcap(void)
   * Open the printcap file...
   */
 
-  if ((fp = fopen(Printcap, "w")) == NULL)
+  if ((fp = cupsFileOpen(Printcap, "w")) == NULL)
     return;
 
+ /*
+  * Put a comment header at the top so that users will know where the
+  * data has come from...
+  */
+
+  cupsFilePuts(fp, "# This file was automatically generated by cupsd(8) from the\n");
+  cupsFilePrintf(fp, "# %s/printers.conf file.  All changes to this file\n",
+          ServerRoot);
+  cupsFilePuts(fp, "# will be lost.\n");
+
  /*
   * Write a new printcap with the current list of printers.
   */
@@ -1859,8 +2064,14 @@ write_printcap(void)
        *    PrinterN:
        */
 
+        if (DefaultPrinter)
+         cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name,
+                 DefaultPrinter->info, ServerName, DefaultPrinter->name);
+
        for (p = Printers; p != NULL; p = p->next)
-         fprintf(fp, "%s:\n", p->name);
+         if (p != DefaultPrinter)
+           cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", p->name, p->info,
+                   ServerName, p->name);
         break;
 
     case PRINTCAP_SOLARIS:
@@ -1869,22 +2080,33 @@ write_printcap(void)
        *
        *    _all:all=Printer1,Printer2,Printer3,...,PrinterN
        *    _default:use=DefaultPrinter
-       *    Printer1:
+       *    Printer1:\
+       *            :bsdaddr=ServerName,Printer1:\
+       *            :description=Description:
        *    Printer2:
+       *            :bsdaddr=ServerName,Printer2:\
+       *            :description=Description:
        *    Printer3:
+       *            :bsdaddr=ServerName,Printer3:\
+       *            :description=Description:
        *    ...
        *    PrinterN:
+       *            :bsdaddr=ServerName,PrinterN:\
+       *            :description=Description:
        */
 
-        fputs("_all:all=", fp);
+        cupsFilePuts(fp, "_all:all=");
        for (p = Printers; p != NULL; p = p->next)
-         fprintf(fp, "%s%c", p->name, p->next ? ',' : '\n');
+         cupsFilePrintf(fp, "%s%c", p->name, p->next ? ',' : '\n');
 
         if (DefaultPrinter)
-         fprintf(fp, "_default:use=%s\n", DefaultPrinter->name);
+         cupsFilePrintf(fp, "_default:use=%s\n", DefaultPrinter->name);
 
        for (p = Printers; p != NULL; p = p->next)
-         fprintf(fp, "%s:\n", p->name);
+         cupsFilePrintf(fp, "%s:\\\n"
+                     "\t:bsdaddr=%s,%s:\\\n"
+                     "\t:description=%s:\n",
+                 p->name, ServerName, p->name, p->info ? p->info : "");
         break;
   }
 
@@ -1892,107 +2114,240 @@ write_printcap(void)
   * Close the file...
   */
 
-  fclose(fp);
+  cupsFileClose(fp);
 }
 
 
 #ifdef __sgi
 /*
- * 'write_irix_state()' - Update the status files used by IRIX printing
- *                        desktop tools.
+ * 'write_irix_config()' - Update the config files used by the IRIX
+ *                         desktop tools.
  */
 
 static void
-write_irix_state(printer_t *p) /* I - Printer to update */
+write_irix_config(printer_t *p)                /* I - Printer to update */
 {
-  char filename[1024];         /* Interface script filename */
-  FILE *fp;                    /* Interface script file */
-  int  tag;                    /* Status tag value */
+  char         filename[1024];         /* Interface script filename */
+  cups_file_t  *fp;                    /* Interface script file */
+  int          tag;                    /* Status tag value */
+
 
 
  /*
-  * The POD status file is needed for the printstatus window to
-  * provide the current status of the printer.
+  * Add dummy interface and GUI scripts to fool SGI's "challenged" printing
+  * tools.  First the interface script that tells the tools what kind of
+  * printer we have...
   */
 
-  snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.status", p->name);
+  snprintf(filename, sizeof(filename), "/var/spool/lp/interface/%s", p->name);
 
-  if ((fp = fopen(filename, "w")) != NULL)
+  if (p->type & CUPS_PRINTER_CLASS)
+    unlink(filename);
+  else if ((fp = cupsFileOpen(filename, "w")) != NULL)
   {
-    fprintf(fp, "Operational Status | %s\n",
-            (p->state == IPP_PRINTER_IDLE)       ? "Idle" :
-            (p->state == IPP_PRINTER_PROCESSING) ? "Busy" :
-                                                   "Faulted");
-    fprintf(fp, "Information        | 01 00 00 | %s\n", CUPS_SVERSION);
-    fprintf(fp, "Information        | 02 00 00 | Device URI: %s\n", p->device_uri);
-    fprintf(fp, "Information        | 03 00 00 | %s jobs\n",
-            p->accepting ? "Accepting" : "Not accepting");
-    fprintf(fp, "Information        | 04 00 00 | %s\n", p->state_message);
-
-    fclose(fp);
+    cupsFilePuts(fp, "#!/bin/sh\n");
 
-    chmod(filename, 0664);
+    if ((attr = ippFindAttribute(p->attrs, "printer-make-and-model",
+                                 IPP_TAG_TEXT)) != NULL)
+      cupsFilePrintf(fp, "NAME=\"%s\"\n", attr->values[0].string.text);
+    else if (p->type & CUPS_PRINTER_CLASS)
+      cupsFilePuts(fp, "NAME=\"Printer Class\"\n");
+    else
+      cupsFilePuts(fp, "NAME=\"Remote Destination\"\n");
+
+    if (p->type & CUPS_PRINTER_COLOR)
+      cupsFilePuts(fp, "TYPE=ColorPostScript\n");
+    else
+      cupsFilePuts(fp, "TYPE=MonoPostScript\n");
+
+    cupsFilePrintf(fp, "HOSTNAME=%s\n", ServerName);
+    cupsFilePrintf(fp, "HOSTPRINTER=%s\n", p->name);
+
+    cupsFileClose(fp);
+
+    chmod(filename, 0755);
     chown(filename, User, Group);
   }
 
  /*
-  * The activeicons file is needed to provide desktop icons for printers:
-  *
-  * [ quoted from /usr/lib/print/tagit ]
-  *
-  * --- Type of printer tags (base values)
-  *
-  * Dumb=66048                 # 0x10200
-  * DumbColor=66080            # 0x10220
-  * Raster=66112               # 0x10240
-  * ColorRaster=66144          # 0x10260
-  * Plotter=66176              # 0x10280
-  * PostScript=66208           # 0x102A0
-  * ColorPostScript=66240      # 0x102C0
-  * MonoPostScript=66272       # 0x102E0
-  *
-  * --- Printer state modifiers for local printers
-  *
-  * Idle=0                     # 0x0
-  * Busy=1                     # 0x1
-  * Faulted=2                  # 0x2
-  * Unknown=3                  # 0x3 (Faulted due to unknown reason)
-  *
-  * --- Printer state modifiers for network printers
-  *
-  * NetIdle=8                  # 0x8
-  * NetBusy=9                  # 0x9
-  * NetFaulted=10              # 0xA
-  * NetUnknown=11              # 0xB (Faulted due to unknown reason)
+  * Then the member file that tells which device file the queue is connected
+  * to...  Networked printers use "/dev/null" in this file, so that's what
+  * we use (the actual device URI can confuse some apps...)
   */
 
-  snprintf(filename, sizeof(filename), "/var/spool/lp/activeicons/%s", p->name);
+  snprintf(filename, sizeof(filename), "/var/spool/lp/member/%s", p->name);
 
-  if ((fp = fopen(filename, "w")) != NULL)
+  if (p->type & CUPS_PRINTER_CLASS)
+    unlink(filename);
+  else if ((fp = cupsFileOpen(filename, "w")) != NULL)
   {
-    if (p->type & CUPS_PRINTER_COLOR)
-      tag = 66240;
-    else
-      tag = 66272;
+    cupsFilePuts(fp, "/dev/null\n");
 
-    if (p->type & CUPS_PRINTER_REMOTE)
-      tag |= 8;
+    cupsFileClose(fp);
 
-    if (p->state == IPP_PRINTER_PROCESSING)
-      tag |= 1;
+    chmod(filename, 0644);
+    chown(filename, User, Group);
+  }
 
-    else if (p->state == IPP_PRINTER_STOPPED)
-      tag |= 2;
+ /*
+  * The gui_interface file is a script or program that launches a GUI
+  * option panel for the printer, using options specified on the
+  * command-line in the third argument.  The option panel must send
+  * any printing options to stdout on a single line when the user
+  * accepts them, or nothing if the user cancels the dialog.
+  *
+  * The default options panel program is /usr/bin/glpoptions, from
+  * the ESP Print Pro software.  You can select another using the
+  * PrintcapGUI option.
+  */
 
-    fputs("#!/bin/sh\n", fp);
-    fprintf(fp, "#Tag %d\n", tag);
+  snprintf(filename, sizeof(filename), "/var/spool/lp/gui_interface/ELF/%s.gui", p->name);
 
-    fclose(fp);
+  if (p->type & CUPS_PRINTER_CLASS)
+    unlink(filename);
+  else if ((fp = cupsFileOpen(filename, "w")) != NULL)
+  {
+    cupsFilePuts(fp, "#!/bin/sh\n");
+    cupsFilePrintf(fp, "%s -d %s -o \"$3\"\n", PrintcapGUI, p->name);
+
+    cupsFileClose(fp);
 
     chmod(filename, 0755);
     chown(filename, User, Group);
   }
 
+ /*
+  * The POD config file is needed by the printstatus command to show
+  * the printer location and device.
+  */
+
+  snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.config", p->name);
+
+  if (p->type & CUPS_PRINTER_CLASS)
+    unlink(filename);
+  else if ((fp = cupsFileOpen(filename, "w")) != NULL)
+  {
+    cupsFilePrintf(fp, "Printer Class      | %s\n",
+            (p->type & CUPS_PRINTER_COLOR) ? "ColorPostScript" : "MonoPostScript");
+    cupsFilePrintf(fp, "Printer Model      | %s\n", p->make_model ? p->make_model : "");
+    cupsFilePrintf(fp, "Location Code      | %s\n", p->location ? p->location : "");
+    cupsFilePrintf(fp, "Physical Location  | %s\n", p->info ? p->info : "");
+    cupsFilePrintf(fp, "Port Path          | %s\n", p->device_uri ? p->device_uri : "");
+    cupsFilePrintf(fp, "Config Path        | /var/spool/lp/pod/%s.config\n", p->name);
+    cupsFilePrintf(fp, "Active Status Path | /var/spool/lp/pod/%s.status\n", p->name);
+    cupsFilePuts(fp, "Status Update Wait | 10 seconds\n");
+
+    cupsFileClose(fp);
+
+    chmod(filename, 0664);
+    chown(filename, User, Group);
+  }
+
+
+/*
+ * 'write_irix_state()' - Update the status files used by IRIX printing
+ *                        desktop tools.
+ */
+
+static void
+write_irix_state(printer_t *p)         /* I - Printer to update */
+{
+  char         filename[1024];         /* Interface script filename */
+  cups_file_t  *fp;                    /* Interface script file */
+  int          tag;                    /* Status tag value */
+
+
+  if (p)
+  {
+   /*
+    * The POD status file is needed for the printstatus window to
+    * provide the current status of the printer.
+    */
+
+    snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.status", p->name);
+
+    if (p->type & CUPS_PRINTER_CLASS)
+      unlink(filename);
+    else if ((fp = cupsFileOpen(filename, "w")) != NULL)
+    {
+      cupsFilePrintf(fp, "Operational Status | %s\n",
+              (p->state == IPP_PRINTER_IDLE)       ? "Idle" :
+              (p->state == IPP_PRINTER_PROCESSING) ? "Busy" :
+                                                     "Faulted");
+      cupsFilePrintf(fp, "Information        | 01 00 00 | %s\n", CUPS_SVERSION);
+      cupsFilePrintf(fp, "Information        | 02 00 00 | Device URI: %s\n",
+              p->device_uri ? p->device_uri : "");
+      cupsFilePrintf(fp, "Information        | 03 00 00 | %s jobs\n",
+              p->accepting ? "Accepting" : "Not accepting");
+      cupsFilePrintf(fp, "Information        | 04 00 00 | %s\n", p->state_message);
+
+      cupsFileClose(fp);
+
+      chmod(filename, 0664);
+      chown(filename, User, Group);
+    }
+
+   /*
+    * The activeicons file is needed to provide desktop icons for printers:
+    *
+    * [ quoted from /usr/lib/print/tagit ]
+    *
+    * --- Type of printer tags (base values)
+    *
+    * Dumb=66048                       # 0x10200
+    * DumbColor=66080          # 0x10220
+    * Raster=66112             # 0x10240
+    * ColorRaster=66144                # 0x10260
+    * Plotter=66176            # 0x10280
+    * PostScript=66208         # 0x102A0
+    * ColorPostScript=66240    # 0x102C0
+    * MonoPostScript=66272     # 0x102E0
+    *
+    * --- Printer state modifiers for local printers
+    *
+    * Idle=0                   # 0x0
+    * Busy=1                   # 0x1
+    * Faulted=2                        # 0x2
+    * Unknown=3                        # 0x3 (Faulted due to unknown reason)
+    *
+    * --- Printer state modifiers for network printers
+    *
+    * NetIdle=8                        # 0x8
+    * NetBusy=9                        # 0x9
+    * NetFaulted=10            # 0xA
+    * NetUnknown=11            # 0xB (Faulted due to unknown reason)
+    */
+
+    snprintf(filename, sizeof(filename), "/var/spool/lp/activeicons/%s", p->name);
+
+    if (p->type & CUPS_PRINTER_CLASS)
+      unlink(filename);
+    else if ((fp = cupsFileOpen(filename, "w")) != NULL)
+    {
+      if (p->type & CUPS_PRINTER_COLOR)
+       tag = 66240;
+      else
+       tag = 66272;
+
+      if (p->type & CUPS_PRINTER_REMOTE)
+       tag |= 8;
+
+      if (p->state == IPP_PRINTER_PROCESSING)
+       tag |= 1;
+
+      else if (p->state == IPP_PRINTER_STOPPED)
+       tag |= 2;
+
+      cupsFilePuts(fp, "#!/bin/sh\n");
+      cupsFilePrintf(fp, "#Tag %d\n", tag);
+
+      cupsFileClose(fp);
+
+      chmod(filename, 0755);
+      chown(filename, User, Group);
+    }
+  }
+
  /*
   * The default file is needed by the printers window to show
   * the default printer.
@@ -2002,11 +2357,11 @@ write_irix_state(printer_t *p)  /* I - Printer to update */
 
   if (DefaultPrinter != NULL)
   {
-    if ((fp = fopen(filename, "w")) != NULL)
+    if ((fp = cupsFileOpen(filename, "w")) != NULL)
     {
-      fprintf(fp, "%s\n", DefaultPrinter->name);
+      cupsFilePrintf(fp, "%s\n", DefaultPrinter->name);
 
-      fclose(fp);
+      cupsFileClose(fp);
 
       chmod(filename, 0644);
       chown(filename, User, Group);
@@ -2019,5 +2374,5 @@ write_irix_state(printer_t *p)    /* I - Printer to update */
 
 
 /*
- * End of "$Id: printers.c,v 1.93.2.13 2002/01/29 21:18:16 mike Exp $".
+ * End of "$Id: printers.c,v 1.93.2.46 2003/04/03 03:33:41 mike Exp $".
  */