]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/dest.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / dest.c
index 042e7b54bf5cdeab6eae5bd2ac71aebcf5da3b57..240ebae8f8a44441c92d5599f452bdc2ed614489 100644 (file)
@@ -1,10 +1,10 @@
 /*
- * "$Id: dest.c 4918 2006-01-12 05:14:40Z mike $"
+ * "$Id: dest.c 6265 2007-02-11 19:42:42Z mike $"
  *
  *   User-defined destination (and option) support for the Common UNIX
  *   Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products.
+ *   Copyright 1997-2007 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Easy Software Products and are protected by Federal
  *
  * Contents:
  *
- *   cupsAddDest()     - Add a destination to the list of destinations.
- *   cupsFreeDests()   - Free the memory used by the list of destinations.
- *   cupsGetDest()     - Get the named destination from the list.
- *   cupsGetDests()    - Get the list of destinations from the default server.
- *   cupsGetDests2()   - Get the list of destinations from the specified server.
- *   cupsSetDests()    - Set the list of destinations for the default server.
- *   cupsSetDests2()   - Set the list of destinations for the specified server.
- *   cups_get_dests()  - Get destinations from a file.
- *   cups_get_sdests() - Get destinations from a server.
+ *   cupsAddDest()            - Add a destination to the list of destinations.
+ *   cupsFreeDests()          - Free the memory used by the list of
+ *                              destinations.
+ *   cupsGetDest()            - Get the named destination from the list.
+ *   cupsGetDests()           - Get the list of destinations from the default
+ *                              server.
+ *   cupsGetDests2()          - Get the list of destinations from the
+ *                              specified server.
+ *   cupsRemoveDest()         - Remove a destination from the destination list.
+ *   cupsDestSetDefaultDest() - Set the default destination.
+ *   cupsSetDests()           - Set the list of destinations for the default
+ *                              server.
+ *   cupsSetDests2()          - Set the list of destinations for the specified
+ *                              server.
+ *   cups_get_dests()         - Get destinations from a file.
+ *   cups_get_sdests()        - Get destinations from a server.
  */
 
 /*
 #include "globals.h"
 #include <stdlib.h>
 #include <ctype.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_NOTIFY_H
+#  include <notify.h>
+#endif /* HAVE_NOTIFY_H */
 
 
 /*
@@ -59,21 +71,31 @@ static int  cups_get_sdests(http_t *http, ipp_op_t op, int num_dests,
 /*
  * 'cupsAddDest()' - Add a destination to the list of destinations.
  *
- * Use the cupsSaveDests() function to save the updated list of destinations
- * to the user's lpoptions file.
+ * This function cannot be used to add a new class or printer queue,
+ * it only adds a new container of saved options for the named
+ * destination or instance.
+ *
+ * If the named destination already exists, the destination list is
+ * returned unchanged.  Adding a new instance of a destination creates
+ * a copy of that destination's options.
+ *
+ * Use the cupsSaveDests() function to save the updated list of
+ * destinations to the user's lpoptions file.
  */
 
 int                                    /* O  - New number of destinations */
-cupsAddDest(const char  *name,         /* I  - Name of destination */
-            const char *instance,      /* I  - Instance of destination or NULL for none/primary */
+cupsAddDest(const char  *name,         /* I  - Destination name */
+            const char *instance,      /* I  - Instance name or NULL for none/primary */
             int         num_dests,     /* I  - Number of destinations */
             cups_dest_t **dests)       /* IO - Destinations */
 {
   int          i;                      /* Looping var */
   cups_dest_t  *dest;                  /* Destination pointer */
+  cups_dest_t  *parent;                /* Parent destination */
+  cups_option_t        *option;                /* Current option */
 
 
-  if (name == NULL || dests == NULL)
+  if (!name || !dests)
     return (0);
 
   if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
@@ -93,27 +115,53 @@ cupsAddDest(const char  *name,             /* I  - Name of destination */
 
   *dests = dest;
 
+ /*
+  * Find where to insert the destination...
+  */
+
   for (i = num_dests; i > 0; i --, dest ++)
     if (strcasecmp(name, dest->name) < 0)
       break;
-    else if (strcasecmp(name, dest->name) == 0 &&
-             instance != NULL && dest->instance != NULL &&
+    else if (!instance && dest->instance)
+      break;
+    else if (!strcasecmp(name, dest->name) &&
+             instance  && dest->instance &&
              strcasecmp(instance, dest->instance) < 0)
       break;
 
   if (i > 0)
     memmove(dest + 1, dest, i * sizeof(cups_dest_t));
 
+ /*
+  * Initialize the destination...
+  */
+
   dest->name        = strdup(name);
   dest->is_default  = 0;
   dest->num_options = 0;
   dest->options     = (cups_option_t *)0;
 
-  if (instance == NULL)
+  if (!instance)
     dest->instance = NULL;
   else
+  {
+   /*
+    * Copy options from the primary instance...
+    */
+
     dest->instance = strdup(instance);
 
+    if ((parent = cupsGetDest(name, NULL, num_dests + 1, *dests)) != NULL)
+    {
+      for (i = parent->num_options, option = parent->options;
+           i > 0;
+          i --, option ++)
+       dest->num_options = cupsAddOption(option->name, option->value,
+                                         dest->num_options,
+                                         &(dest->options));
+    }
+  }
+
   return (num_dests + 1);
 }
 
@@ -155,18 +203,18 @@ cupsFreeDests(int         num_dests,      /* I - Number of destinations */
  */
 
 cups_dest_t *                          /* O - Destination pointer or NULL */
-cupsGetDest(const char  *name,         /* I - Name of destination */
-            const char *instance,      /* I - Instance of destination */
+cupsGetDest(const char  *name,         /* I - Destination name or NULL for the default destination */
+            const char *instance,      /* I - Instance name or NULL */
             int         num_dests,     /* I - Number of destinations */
             cups_dest_t *dests)                /* I - Destinations */
 {
   int  comp;                           /* Result of comparison */
 
 
-  if (num_dests == 0 || dests == NULL)
+  if (num_dests <= 0 || !dests)
     return (NULL);
 
-  if (name == NULL)
+  if (!name)
   {
    /*
     * NULL name for default printer.
@@ -193,9 +241,9 @@ cupsGetDest(const char  *name,              /* I - Name of destination */
        return (NULL);
       else if (comp == 0)
       {
-       if ((instance == NULL && dests->instance == NULL) ||
+       if ((!instance && !dests->instance) ||
             (instance != NULL && dests->instance != NULL &&
-            strcasecmp(instance, dests->instance) == 0))
+            !strcasecmp(instance, dests->instance)))
          return (dests);
       }
 
@@ -210,6 +258,14 @@ cupsGetDest(const char  *name,             /* I - Name of destination */
 
 /*
  * 'cupsGetDests()' - Get the list of destinations from the default server.
+ *
+ * Starting with CUPS 1.2, the returned list of destinations include the
+ * printer-info, printer-is-accepting-jobs, printer-is-shared,
+ * printer-make-and-model, printer-state, printer-state-change-time,
+ * printer-state-reasons, and printer-type attributes as options.
+ *
+ * Use the cupsFreeDests() function to free the destination list and
+ * the cupsGetDest() function to find a particular destination.
  */
 
 int                                    /* O - Number of destinations */
@@ -237,6 +293,14 @@ cupsGetDests(cups_dest_t **dests)  /* O - Destinations */
 /*
  * 'cupsGetDests2()' - Get the list of destinations from the specified server.
  *
+ * Starting with CUPS 1.2, the returned list of destinations include the
+ * printer-info, printer-is-accepting-jobs, printer-is-shared,
+ * printer-make-and-model, printer-state, printer-state-change-time,
+ * printer-state-reasons, and printer-type attributes as options.
+ *
+ * Use the cupsFreeDests() function to free the destination list and
+ * the cupsGetDest() function to find a particular destination.
+ *
  * @since CUPS 1.1.21@
  */
 
@@ -248,7 +312,7 @@ cupsGetDests2(http_t      *http,    /* I - HTTP connection */
   int          num_dests;              /* Number of destinations */
   cups_dest_t  *dest;                  /* Destination pointer */
   const char   *home;                  /* HOME environment variable */
-  char         filename[1024];         /* Local ~/.lpoptions file */
+  char         filename[1024];         /* Local ~/.cups/lpoptions file */
   const char   *defprinter;            /* Default printer */
   char         name[1024],             /* Copy of printer name */
                *instance;              /* Pointer to instance name */
@@ -331,7 +395,7 @@ cupsGetDests2(http_t      *http,    /* I - HTTP connection */
   }
 
  /*
-  * Load the /etc/cups/lpoptions and ~/.lpoptions files...
+  * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
   */
 
   snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
@@ -339,13 +403,16 @@ cupsGetDests2(http_t      *http,  /* I - HTTP connection */
 
   if ((home = getenv("HOME")) != NULL)
   {
-    snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
+    snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
+    if (access(filename, 0))
+      snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
+
     num_dests = cups_get_dests(filename, num_dests, dests);
   }
 
  /*
   * Validate the current default destination - this prevents old
-  * Default lines in /etc/cups/lpoptions and ~/.lpoptions from
+  * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
   * pointing to a non-existent printer or class...
   */
 
@@ -393,11 +460,97 @@ cupsGetDests2(http_t      *http,  /* I - HTTP connection */
 }
 
 
+/*
+ * 'cupsRemoveDest()' - Remove a destination from the destination list.
+ *
+ * Removing a destination/instance does not delete the class or printer
+ * queue, merely the lpoptions for that destination/instance.  Use the
+ * cupsSetDests() or cupsSetDests2() functions to save the new options
+ * for the user.
+ *
+ * @since CUPS 1.3@
+ */
+
+int                                    /* O  - New number of destinations */
+cupsRemoveDest(const char  *name,      /* I  - Destination name */
+               const char  *instance,  /* I  - Instance name or NULL */
+              int         num_dests,   /* I  - Number of destinations */
+              cups_dest_t **dests)     /* IO - Destinations */
+{
+  int          i;                      /* Index into destinations */
+  cups_dest_t  *dest;                  /* Pointer to destination */
+
+
+ /*
+  * Find the destination...
+  */
+
+  if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
+    return (num_dests);
+
+ /*
+  * Free memory...
+  */
+
+  cupsFreeOptions(dest->num_options, dest->options);
+
+ /*
+  * Remove the destination from the array...
+  */
+
+  num_dests --;
+
+  i = dest - *dests;
+
+  if (i < num_dests)
+    memmove(dest, dest + 1, (num_dests - i) * sizeof(cups_dest_t));
+
+  return (num_dests);
+}
+
+
+/*
+ * 'cupsDestSetDefaultDest()' - Set the default destination.
+ *
+ * @since CUPS 1.3@
+ */
+
+void
+cupsSetDefaultDest(
+    const char  *name,                 /* I - Destination name */
+    const char  *instance,             /* I - Instance name or NULL */
+    int         num_dests,             /* I - Number of destinations */
+    cups_dest_t *dests)                        /* I - Destinations */
+{
+  int          i;                      /* Looping var */
+  cups_dest_t  *dest;                  /* Current destination */
+
+
+ /*
+  * Range check input...
+  */
+
+  if (!name || num_dests <= 0 || !dests)
+    return;
+
+ /*
+  * Loop through the array and set the "is_default" flag for the matching
+  * destination...
+  */
+
+  for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+    dest->is_default = !strcasecmp(name, dest->name) &&
+                       ((!instance && !dest->instance) ||
+                       (instance && dest->instance &&
+                        !strcasecmp(instance, dest->instance)));
+}
+
+
 /*
  * 'cupsSetDests()' - Save the list of destinations for the default server.
  *
  * This function saves the destinations to /etc/cups/lpoptions when run
- * as root and ~/.lpoptions when run as a normal user.
+ * as root and ~/.cups/lpoptions when run as a normal user.
  */
 
 void
@@ -424,7 +577,7 @@ cupsSetDests(int         num_dests, /* I - Number of destinations */
  * 'cupsSetDests2()' - Save the list of destinations for the specified server.
  *
  * This function saves the destinations to /etc/cups/lpoptions when run
- * as root and ~/.lpoptions when run as a normal user.
+ * as root and ~/.cups/lpoptions when run as a normal user.
  *
  * @since CUPS 1.1.21@
  */
@@ -438,8 +591,11 @@ cupsSetDests2(http_t      *http,   /* I - HTTP connection */
   int          wrote;                  /* Wrote definition? */
   cups_dest_t  *dest;                  /* Current destination */
   cups_option_t        *option;                /* Current option */
+  _ipp_option_t        *match;                 /* Matching attribute for option */
   FILE         *fp;                    /* File pointer */
+#ifndef WIN32
   const char   *home;                  /* HOME environment variable */
+#endif /* WIN32 */
   char         filename[1024];         /* lpoptions file */
   int          num_temps;              /* Number of temporary destinations */
   cups_dest_t  *temps,                 /* Temporary destinations */
@@ -482,7 +638,24 @@ cupsSetDests2(http_t      *http,   /* I - HTTP connection */
     */
 
     if ((home = getenv("HOME")) != NULL)
+    {
+     /*
+      * Remove the old ~/.lpoptions file...
+      */
+
       snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
+      unlink(filename);
+
+     /*
+      * Create ~/.cups subdirectory...
+      */
+
+      snprintf(filename, sizeof(filename), "%s/.cups", home);
+      if (access(filename, 0))
+        mkdir(filename, 0700);
+
+      snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
+    }
   }
 #endif /* !WIN32 */
 
@@ -496,6 +669,16 @@ cupsSetDests2(http_t      *http,   /* I - HTTP connection */
     return (-1);
   }
 
+#ifndef WIN32
+ /*
+  * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
+  * file...
+  */
+
+  if (!getuid())
+    fchmod(fileno(fp), 0644);
+#endif /* !WIN32 */
+
  /*
   * Write each printer; each line looks like:
   *
@@ -522,17 +705,24 @@ cupsSetDests2(http_t      *http,  /* I - HTTP connection */
 
       for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
       {
+       /*
+        * See if this option is a printer attribute; if so, skip it...
+       */
+
+        if ((match = _ippFindOption(option->name)) != NULL &&
+           match->group_tag == IPP_TAG_PRINTER)
+         continue;
+
        /*
        * See if the server/global options match these; if so, don't
        * write 'em.
        */
 
-        if (temp && (val = cupsGetOption(option->name, temp->num_options,
-                                        temp->options)) != NULL)
-       {
-         if (strcasecmp(val, option->value) == 0)
-           continue;
-       }
+        if (temp &&
+           (val = cupsGetOption(option->name, temp->num_options,
+                                temp->options)) != NULL &&
+            !strcasecmp(val, option->value))
+         continue;
 
        /*
         * Options don't match, write to the file...
@@ -548,10 +738,35 @@ cupsSetDests2(http_t      *http,  /* I - HTTP connection */
         
         if (option->value[0])
        {
-         if (strchr(option->value, ' ') != NULL)
-           fprintf(fp, " %s=\"%s\"", option->name, option->value);
-          else
+         if (strchr(option->value, ' ') ||
+             strchr(option->value, '\\') ||
+             strchr(option->value, '\"') ||
+             strchr(option->value, '\''))
+         {
+          /*
+           * Quote the value...
+           */
+
+           fprintf(fp, " %s=\"", option->name);
+
+           for (val = option->value; *val; val ++)
+           {
+             if (strchr("\"\'\\", *val))
+               putc('\\', fp);
+
+              putc(*val, fp);
+           }
+
+           putc('\"', fp);
+          }
+         else
+         {
+          /*
+           * Store the literal value...
+           */
+
            fprintf(fp, " %s=%s", option->name, option->value);
+          }
        }
        else
          fprintf(fp, " %s", option->name);
@@ -562,16 +777,21 @@ cupsSetDests2(http_t      *http,  /* I - HTTP connection */
     }
 
  /*
-  * Free the temporary destinations...
+  * Free the temporary destinations and close the file...
   */
 
   cupsFreeDests(num_temps, temps);
 
+  fclose(fp);
+
+#ifdef HAVE_NOTIFY_POST
  /*
-  * Close the file and return...
+  * Send a notification so that MacOS X applications can know about the
+  * change, too.
   */
 
-  fclose(fp);
+  notify_post("com.apple.printerListChange");
+#endif /* HAVE_NOTIFY_POST */
 
   return (0);
 }
@@ -739,17 +959,39 @@ cups_get_sdests(http_t      *http,        /* I - HTTP connection */
                 int         num_dests, /* I - Number of destinations */
                 cups_dest_t **dests)   /* IO - Destinations */
 {
+  int          i;                      /* Looping var */
   cups_dest_t  *dest;                  /* Current destination */
   ipp_t                *request,               /* IPP Request */
                *response;              /* IPP Response */
   ipp_attribute_t *attr;               /* Current attribute */
-  cups_lang_t  *language;              /* Default language */
-  const char   *name;                  /* printer-name attribute */
-  char         job_sheets[1024];       /* job-sheets option */
+  int          accepting,              /* printer-is-accepting-jobs attribute */
+               shared,                 /* printer-is-shared attribute */
+               state,                  /* printer-state attribute */
+               change_time,            /* printer-state-change-time attribute */
+               type;                   /* printer-type attribute */
+  const char   *info,                  /* printer-info attribute */
+               *make_model,            /* printer-make-and-model attribute */
+               *name;                  /* printer-name attribute */
+  char         job_sheets[1024],       /* job-sheets-default attribute */
+               reasons[1024];          /* printer-state-reasons attribute */
+  int          num_options;            /* Number of options */
+  cups_option_t        *options;               /* Options */
+  char         optname[1024],          /* Option name */
+               value[2048],            /* Option value */
+               *ptr;                   /* Pointer into name/value */
   static const char * const pattrs[] = /* Attributes we're interested in */
                {
+                 "job-sheets-default",
+                 "printer-info",
+                 "printer-is-accepting-jobs",
+                 "printer-is-shared",
+                 "printer-make-and-model",
                  "printer-name",
-                 "job-sheets-default"
+                 "printer-state",
+                 "printer-state-change-time",
+                 "printer-state-reasons",
+                 "printer-type",
+                 "printer-defaults"
                };
 
 
@@ -759,27 +1001,18 @@ cups_get_sdests(http_t      *http,       /* I - HTTP connection */
   *
   *    attributes-charset
   *    attributes-natural-language
+  *    requesting-user-name
   */
 
-  request = ippNew();
-
-  request->request.op.operation_id = op;
-  request->request.op.request_id   = 1;
-
-  language = cupsLangDefault();
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL, language->language);
-
-  cupsLangFree(language);
+  request = ippNewRequest(op);
 
   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
                NULL, pattrs);
 
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+               "requesting-user-name", NULL, cupsUser());
+
  /*
   * Do the request and get back a response...
   */
@@ -799,20 +1032,26 @@ cups_get_sdests(http_t      *http,       /* I - HTTP connection */
         break;
 
      /*
-      * Pull the needed attributes from this job...
+      * Pull the needed attributes from this printer...
       */
 
-      name = NULL;
+      accepting   = 0;
+      change_time = 0;
+      info        = NULL;
+      make_model  = NULL;
+      name        = NULL;
+      num_options = 0;
+      options     = NULL;
+      shared      = 1;
+      state       = IPP_PRINTER_IDLE;
+      type        = CUPS_PRINTER_LOCAL;
 
       strcpy(job_sheets, "");
+      strcpy(reasons, "");
 
       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
       {
-        if (strcmp(attr->name, "printer-name") == 0 &&
-           attr->value_tag == IPP_TAG_NAME)
-         name = attr->values[0].string.text;
-
-        if (strcmp(attr->name, "job-sheets-default") == 0 &&
+        if (!strcmp(attr->name, "job-sheets-default") &&
            (attr->value_tag == IPP_TAG_KEYWORD ||
             attr->value_tag == IPP_TAG_NAME))
         {
@@ -820,8 +1059,120 @@ cups_get_sdests(http_t      *http,       /* I - HTTP connection */
            snprintf(job_sheets, sizeof(job_sheets), "%s,%s",
                     attr->values[0].string.text, attr->values[1].string.text);
          else
-           strcpy(job_sheets, attr->values[0].string.text);
+           strlcpy(job_sheets, attr->values[0].string.text,
+                   sizeof(job_sheets));
         }
+        else if (!strcmp(attr->name, "printer-info") &&
+                attr->value_tag == IPP_TAG_TEXT)
+         info = attr->values[0].string.text;
+       else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
+                attr->value_tag == IPP_TAG_BOOLEAN)
+          accepting = attr->values[0].boolean;
+       else if (!strcmp(attr->name, "printer-is-shared") &&
+                attr->value_tag == IPP_TAG_BOOLEAN)
+          shared = attr->values[0].boolean;
+        else if (!strcmp(attr->name, "printer-make-and-model") &&
+                attr->value_tag == IPP_TAG_TEXT)
+         make_model = attr->values[0].string.text;
+        else if (!strcmp(attr->name, "printer-name") &&
+                attr->value_tag == IPP_TAG_NAME)
+         name = attr->values[0].string.text;
+       else if (!strcmp(attr->name, "printer-state") &&
+                attr->value_tag == IPP_TAG_ENUM)
+          state = attr->values[0].integer;
+       else if (!strcmp(attr->name, "printer-state-change-time") &&
+                attr->value_tag == IPP_TAG_INTEGER)
+          change_time = attr->values[0].integer;
+        else if (!strcmp(attr->name, "printer-state-reasons") &&
+                attr->value_tag == IPP_TAG_KEYWORD)
+       {
+         strlcpy(reasons, attr->values[0].string.text, sizeof(reasons));
+         for (i = 1, ptr = reasons + strlen(reasons);
+              i < attr->num_values;
+              i ++)
+         {
+           snprintf(ptr, sizeof(reasons) - (ptr - reasons), ",%s",
+                    attr->values[i].string.text);
+           ptr += strlen(ptr);
+         }
+       }
+       else if (!strcmp(attr->name, "printer-type") &&
+                attr->value_tag == IPP_TAG_ENUM)
+          type = attr->values[0].integer;
+        else if (strncmp(attr->name, "notify-", 7) &&
+                (attr->value_tag == IPP_TAG_BOOLEAN ||
+                 attr->value_tag == IPP_TAG_ENUM ||
+                 attr->value_tag == IPP_TAG_INTEGER ||
+                 attr->value_tag == IPP_TAG_KEYWORD ||
+                 attr->value_tag == IPP_TAG_NAME ||
+                 attr->value_tag == IPP_TAG_RANGE) &&
+                strstr(attr->name, "-default"))
+       {
+         char  *valptr;                /* Pointer into attribute value */
+
+
+        /*
+         * Add a default option...
+         */
+
+          strlcpy(optname, attr->name, sizeof(optname));
+         if ((ptr = strstr(optname, "-default")) != NULL)
+           *ptr = '\0';
+
+          value[0] = '\0';
+         for (i = 0, ptr = value; i < attr->num_values; i ++)
+         {
+           if (ptr >= (value + sizeof(value) - 1))
+             break;
+
+            if (i)
+             *ptr++ = ',';
+
+            switch (attr->value_tag)
+           {
+             case IPP_TAG_INTEGER :
+             case IPP_TAG_ENUM :
+                 snprintf(ptr, sizeof(value) - (ptr - value), "%d",
+                          attr->values[i].integer);
+                 break;
+
+             case IPP_TAG_BOOLEAN :
+                 if (attr->values[i].boolean)
+                   strlcpy(ptr, "true", sizeof(value) - (ptr - value));
+                 else
+                   strlcpy(ptr, "false", sizeof(value) - (ptr - value));
+                 break;
+
+             case IPP_TAG_RANGE :
+                 if (attr->values[i].range.lower ==
+                         attr->values[i].range.upper)
+                   snprintf(ptr, sizeof(value) - (ptr - value), "%d",
+                            attr->values[i].range.lower);
+                 else
+                   snprintf(ptr, sizeof(value) - (ptr - value), "%d-%d",
+                            attr->values[i].range.lower,
+                            attr->values[i].range.upper);
+                 break;
+
+             default :
+                 for (valptr = attr->values[i].string.text;
+                      *valptr && ptr < (value + sizeof(value) - 2);)
+                 {
+                   if (strchr(" \t\n\\\'\"", *valptr))
+                     *ptr++ = '\\';
+
+                   *ptr++ = *valptr++;
+                 }
+
+                 *ptr = '\0';
+                 break;
+           }
+
+           ptr += strlen(ptr);
+          }
+
+         num_options = cupsAddOption(optname, value, num_options, &options);
+       }
 
         attr = attr->next;
       }
@@ -832,6 +1183,8 @@ cups_get_sdests(http_t      *http, /* I - HTTP connection */
 
       if (!name)
       {
+        cupsFreeOptions(num_options, options);
+
         if (attr == NULL)
          break;
        else
@@ -841,10 +1194,64 @@ cups_get_sdests(http_t      *http,       /* I - HTTP connection */
       num_dests = cupsAddDest(name, NULL, num_dests, dests);
 
       if ((dest = cupsGetDest(name, NULL, num_dests, *dests)) != NULL)
+      {
+        dest->num_options = num_options;
+       dest->options     = options;
+
+        num_options = 0;
+       options     = NULL;
+
         if (job_sheets[0])
-          dest->num_options = cupsAddOption("job-sheets", job_sheets, 0,
+          dest->num_options = cupsAddOption("job-sheets", job_sheets,
+                                           dest->num_options,
                                            &(dest->options));
 
+        if (info)
+          dest->num_options = cupsAddOption("printer-info", info,
+                                           dest->num_options,
+                                           &(dest->options));
+
+        sprintf(value, "%d", accepting);
+       dest->num_options = cupsAddOption("printer-is-accepting-jobs", value,
+                                         dest->num_options,
+                                         &(dest->options));
+
+        sprintf(value, "%d", shared);
+       dest->num_options = cupsAddOption("printer-is-shared", value,
+                                         dest->num_options,
+                                         &(dest->options));
+
+        if (make_model)
+          dest->num_options = cupsAddOption("printer-make-and-model",
+                                           make_model, dest->num_options,
+                                           &(dest->options));
+
+        sprintf(value, "%d", state);
+       dest->num_options = cupsAddOption("printer-state", value,
+                                         dest->num_options,
+                                         &(dest->options));
+
+        if (change_time)
+       {
+         sprintf(value, "%d", change_time);
+         dest->num_options = cupsAddOption("printer-state-change-time", value,
+                                           dest->num_options,
+                                           &(dest->options));
+        }
+
+        if (reasons[0])
+          dest->num_options = cupsAddOption("printer-state-reasons", reasons,
+                                           dest->num_options,
+                                           &(dest->options));
+
+        sprintf(value, "%d", type);
+       dest->num_options = cupsAddOption("printer-type", value,
+                                         dest->num_options,
+                                         &(dest->options));
+      }
+
+      cupsFreeOptions(num_options, options);
+
       if (attr == NULL)
        break;
     }
@@ -861,5 +1268,5 @@ cups_get_sdests(http_t      *http, /* I - HTTP connection */
 
 
 /*
- * End of "$Id: dest.c 4918 2006-01-12 05:14:40Z mike $".
+ * End of "$Id: dest.c 6265 2007-02-11 19:42:42Z mike $".
  */