2 * "$Id: dest.c 7946 2008-09-16 23:27:54Z mike $"
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
7 * Copyright 2007-2009 by Apple Inc.
8 * Copyright 1997-2007 by Easy Software Products.
10 * These coded instructions, statements, and computer programs are the
11 * property of Apple Inc. and are protected by Federal copyright
12 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
13 * which should have been included with this file. If this file is
14 * file is missing or damaged, see the license at "http://www.cups.org/".
16 * This file is subject to the Apple OS-Developed Software exception.
20 * cupsAddDest() - Add a destination to the list of destinations.
21 * cupsFreeDests() - Free the memory used by the list of destinations.
22 * cupsGetDest() - Get the named destination from the list.
23 * cupsGetDests() - Get the list of destinations from the default
25 * cupsGetDests2() - Get the list of destinations from the specified
27 * cupsGetNamedDest() - Get options for the named destination.
28 * cupsRemoveDest() - Remove a destination from the destination list.
29 * cupsSetDefaultDest() - Set the default destination.
30 * cupsSetDests() - Save the list of destinations for the default
32 * cupsSetDests2() - Save the list of destinations for the specified
34 * appleCopyLocations() - Copy the location history array.
35 * appleCopyNetwork() - Get the network ID for the current location.
36 * appleGetPaperSize() - Get the default paper size.
37 * appleGetPrinter() - Get a printer from the history array.
38 * appleSetDefault() - Set the default printer for this location.
39 * appleUseLastPrinter() - Get the default printer preference value.
40 * cups_add_dest() - Add a destination to the array.
41 * cups_compare_dests() - Compare two destinations.
42 * cups_find_dest() - Find a destination using a binary search.
43 * cups_get_default() - Get the default destination from an lpoptions file.
44 * cups_get_dests() - Get destinations from a file.
45 * cups_get_sdests() - Get destinations from a server.
46 * cups_make_string() - Make a comma-separated string of values from an IPP
51 * Include necessary headers...
63 #endif /* HAVE_NOTIFY_H */
66 # include <sys/cdefs.h>
67 # include <CoreFoundation/CoreFoundation.h>
68 # include <SystemConfiguration/SystemConfiguration.h>
69 # define kDefaultPaperIDKey CFSTR("DefaultPaperID")
70 # define kLocationHistoryArrayKey CFSTR("kLocationHistoryArrayKeyTMP")
71 # define kLocationNetworkKey CFSTR("kLocationNetworkKey")
72 # define kLocationPrinterIDKey CFSTR("kLocationPrinterIDKey")
73 # define kPMPrintingPreferences CFSTR("com.apple.print.PrintingPrefs")
74 # define kUseLastPrinterAsCurrentPrinterKey CFSTR("UseLastPrinterAsCurrentPrinter")
75 #endif /* __APPLE__ */
83 static CFArrayRef
appleCopyLocations(void);
84 static CFStringRef
appleCopyNetwork(void);
85 static char *appleGetPaperSize(char *name
, int namesize
);
86 static CFStringRef
appleGetPrinter(CFArrayRef locations
, CFStringRef network
,
88 static void appleSetDefault(const char *name
);
89 static int appleUseLastPrinter(void);
90 #endif /* __APPLE__ */
91 static cups_dest_t
*cups_add_dest(const char *name
, const char *instance
,
92 int *num_dests
, cups_dest_t
**dests
);
93 static int cups_compare_dests(cups_dest_t
*a
, cups_dest_t
*b
);
94 static int cups_find_dest(const char *name
, const char *instance
,
95 int num_dests
, cups_dest_t
*dests
, int prev
,
97 static char *cups_get_default(const char *filename
, char *namebuf
,
98 size_t namesize
, const char **instance
);
99 static int cups_get_dests(const char *filename
, const char *match_name
,
100 const char *match_inst
, int user_default_set
,
101 int num_dests
, cups_dest_t
**dests
);
102 static int cups_get_sdests(http_t
*http
, ipp_op_t op
, const char *name
,
103 int num_dests
, cups_dest_t
**dests
);
104 static char *cups_make_string(ipp_attribute_t
*attr
, char *buffer
,
109 * 'cupsAddDest()' - Add a destination to the list of destinations.
111 * This function cannot be used to add a new class or printer queue,
112 * it only adds a new container of saved options for the named
113 * destination or instance.
115 * If the named destination already exists, the destination list is
116 * returned unchanged. Adding a new instance of a destination creates
117 * a copy of that destination's options.
119 * Use the @link cupsSaveDests@ function to save the updated list of
120 * destinations to the user's lpoptions file.
123 int /* O - New number of destinations */
124 cupsAddDest(const char *name
, /* I - Destination name */
125 const char *instance
, /* I - Instance name or @code NULL@ for none/primary */
126 int num_dests
, /* I - Number of destinations */
127 cups_dest_t
**dests
) /* IO - Destinations */
129 int i
; /* Looping var */
130 cups_dest_t
*dest
; /* Destination pointer */
131 cups_dest_t
*parent
= NULL
; /* Parent destination */
132 cups_option_t
*doption
, /* Current destination option */
133 *poption
; /* Current parent option */
139 if (!cupsGetDest(name
, instance
, num_dests
, *dests
))
141 if (instance
&& !cupsGetDest(name
, NULL
, num_dests
, *dests
))
144 dest
= cups_add_dest(name
, instance
, &num_dests
, dests
);
147 * Find the base dest again now the array has been realloc'd.
150 parent
= cupsGetDest(name
, NULL
, num_dests
, *dests
);
152 if (instance
&& parent
&& parent
->num_options
> 0)
155 * Copy options from parent...
158 dest
->options
= calloc(sizeof(cups_option_t
), parent
->num_options
);
162 dest
->num_options
= parent
->num_options
;
164 for (i
= dest
->num_options
, doption
= dest
->options
,
165 poption
= parent
->options
;
167 i
--, doption
++, poption
++)
169 doption
->name
= _cupsStrRetain(poption
->name
);
170 doption
->value
= _cupsStrRetain(poption
->value
);
181 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
185 cupsFreeDests(int num_dests
, /* I - Number of destinations */
186 cups_dest_t
*dests
) /* I - Destinations */
188 int i
; /* Looping var */
189 cups_dest_t
*dest
; /* Current destination */
192 if (num_dests
== 0 || dests
== NULL
)
195 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
197 _cupsStrFree(dest
->name
);
198 _cupsStrFree(dest
->instance
);
200 cupsFreeOptions(dest
->num_options
, dest
->options
);
208 * 'cupsGetDest()' - Get the named destination from the list.
210 * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
211 * list of supported destinations for the current user.
214 cups_dest_t
* /* O - Destination pointer or @code NULL@ */
215 cupsGetDest(const char *name
, /* I - Destination name or @code NULL@ for the default destination */
216 const char *instance
, /* I - Instance name or @code NULL@ */
217 int num_dests
, /* I - Number of destinations */
218 cups_dest_t
*dests
) /* I - Destinations */
220 int diff
, /* Result of comparison */
221 match
; /* Matching index */
224 if (num_dests
<= 0 || !dests
)
230 * NULL name for default printer.
233 while (num_dests
> 0)
235 if (dests
->is_default
)
245 * Lookup name and optionally the instance...
248 match
= cups_find_dest(name
, instance
, num_dests
, dests
, -1, &diff
);
251 return (dests
+ match
);
259 * 'cupsGetDests()' - Get the list of destinations from the default server.
261 * Starting with CUPS 1.2, the returned list of destinations include the
262 * printer-info, printer-is-accepting-jobs, printer-is-shared,
263 * printer-make-and-model, printer-state, printer-state-change-time,
264 * printer-state-reasons, and printer-type attributes as options. CUPS 1.4
265 * adds the marker-change-time, marker-colors, marker-high-levels,
266 * marker-levels, marker-low-levels, marker-message, marker-names,
267 * marker-types, and printer-commands attributes as well.
269 * Use the @link cupsFreeDests@ function to free the destination list and
270 * the @link cupsGetDest@ function to find a particular destination.
273 int /* O - Number of destinations */
274 cupsGetDests(cups_dest_t
**dests
) /* O - Destinations */
276 return (cupsGetDests2(CUPS_HTTP_DEFAULT
, dests
));
281 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
283 * Starting with CUPS 1.2, the returned list of destinations include the
284 * printer-info, printer-is-accepting-jobs, printer-is-shared,
285 * printer-make-and-model, printer-state, printer-state-change-time,
286 * printer-state-reasons, and printer-type attributes as options. CUPS 1.4
287 * adds the marker-change-time, marker-colors, marker-high-levels,
288 * marker-levels, marker-low-levels, marker-message, marker-names,
289 * marker-types, and printer-commands attributes as well.
291 * Use the @link cupsFreeDests@ function to free the destination list and
292 * the @link cupsGetDest@ function to find a particular destination.
294 * @since CUPS 1.1.21/Mac OS X 10.4@
297 int /* O - Number of destinations */
298 cupsGetDests2(http_t
*http
, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
299 cups_dest_t
**dests
) /* O - Destinations */
301 int i
; /* Looping var */
302 int num_dests
; /* Number of destinations */
303 cups_dest_t
*dest
; /* Destination pointer */
304 const char *home
; /* HOME environment variable */
305 char filename
[1024]; /* Local ~/.cups/lpoptions file */
306 const char *defprinter
; /* Default printer */
307 char name
[1024], /* Copy of printer name */
308 *instance
, /* Pointer to instance name */
309 *user_default
; /* User default printer */
310 int num_reals
; /* Number of real queues */
311 cups_dest_t
*reals
; /* Real queues */
312 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
316 * Range check the input...
321 _cupsSetError(IPP_INTERNAL_ERROR
, _("Bad NULL dests pointer"), 1);
326 * Initialize destination array...
330 *dests
= (cups_dest_t
*)0;
333 * Grab the printers and classes...
336 num_dests
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, NULL
, num_dests
, dests
);
338 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE
)
340 cupsFreeDests(num_dests
, *dests
);
341 *dests
= (cups_dest_t
*)0;
346 * Make a copy of the "real" queues for a later sanity check...
351 num_reals
= num_dests
;
352 reals
= calloc(num_reals
, sizeof(cups_dest_t
));
355 memcpy(reals
, *dests
, num_reals
* sizeof(cups_dest_t
));
366 * Grab the default destination...
369 if ((user_default
= _cupsUserDefault(name
, sizeof(name
))) != NULL
)
371 else if ((defprinter
= cupsGetDefault2(http
)) != NULL
)
373 strlcpy(name
, defprinter
, sizeof(name
));
380 * Separate printer and instance name...
383 if ((instance
= strchr(name
, '/')) != NULL
)
387 * Lookup the printer and instance and make it the default...
390 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
391 dest
->is_default
= 1;
397 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
400 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
401 num_dests
= cups_get_dests(filename
, NULL
, NULL
, user_default
!= NULL
,
404 if ((home
= getenv("HOME")) != NULL
)
406 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
407 if (access(filename
, 0))
408 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
410 num_dests
= cups_get_dests(filename
, NULL
, NULL
, user_default
!= NULL
,
415 * Validate the current default destination - this prevents old
416 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
417 * pointing to a non-existent printer or class...
423 * See if we have a default printer...
426 if ((dest
= cupsGetDest(NULL
, NULL
, num_dests
, *dests
)) != NULL
)
429 * Have a default; see if it is real...
432 dest
= cupsGetDest(dest
->name
, NULL
, num_reals
, reals
);
436 * If dest is NULL, then no default (that exists) is set, so we
437 * need to set a default if one exists...
440 if (dest
== NULL
&& defprinter
!= NULL
)
442 for (i
= 0; i
< num_dests
; i
++)
443 (*dests
)[i
].is_default
= 0;
445 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
446 dest
->is_default
= 1;
457 * Return the number of destinations...
461 _cupsSetError(IPP_OK
, NULL
, 0);
468 * 'cupsGetNamedDest()' - Get options for the named destination.
470 * This function is optimized for retrieving a single destination and should
471 * be used instead of @link cupsGetDests@ and @link cupsGetDest@ when you either
472 * know the name of the destination or want to print to the default destination.
473 * If @code NULL@ is returned, the destination does not exist or there is no
474 * default destination.
476 * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
477 * server will be used.
479 * If "name" is @code NULL@, the default printer for the current user will be
482 * The returned destination must be freed using @link cupsFreeDests@ with a
483 * "num_dests" value of 1.
485 * @since CUPS 1.4/Mac OS X 10.6@
488 cups_dest_t
* /* O - Destination or @code NULL@ */
489 cupsGetNamedDest(http_t
*http
, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
490 const char *name
, /* I - Destination name or @code NULL@ for the default destination */
491 const char *instance
) /* I - Instance name or @code NULL@ */
493 cups_dest_t
*dest
; /* Destination */
494 char filename
[1024], /* Path to lpoptions */
495 defname
[256]; /* Default printer name */
496 const char *home
= getenv("HOME"); /* Home directory */
497 int set_as_default
= 0; /* Set returned destination as default */
498 ipp_op_t op
= IPP_GET_PRINTER_ATTRIBUTES
;
499 /* IPP operation to get server ops */
500 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
504 * If "name" is NULL, find the default destination...
510 name
= _cupsUserDefault(defname
, sizeof(defname
));
515 * No default in the environment, try the user's lpoptions files...
518 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
520 if ((name
= cups_get_default(filename
, defname
, sizeof(defname
),
523 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
524 name
= cups_get_default(filename
, defname
, sizeof(defname
),
532 * Still not there? Try the system lpoptions file...
535 snprintf(filename
, sizeof(filename
), "%s/lpoptions",
536 cg
->cups_serverroot
);
537 name
= cups_get_default(filename
, defname
, sizeof(defname
), &instance
);
543 * No locally-set default destination, ask the server...
546 op
= CUPS_GET_DEFAULT
;
551 * Get the printer's attributes...
554 if (!cups_get_sdests(http
, op
, name
, 0, &dest
))
556 if (op
== CUPS_GET_DEFAULT
|| name
)
560 * The default printer from environment variables or from a
561 * configuration file does not exist. Find out the real default.
564 if (!cups_get_sdests(http
, CUPS_GET_DEFAULT
, NULL
, 0, &dest
))
569 dest
->instance
= _cupsStrAlloc(instance
);
572 dest
->is_default
= 1;
575 * Then add local options...
578 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
579 cups_get_dests(filename
, name
, instance
, 1, 1, &dest
);
583 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
585 if (access(filename
, 0))
586 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
588 cups_get_dests(filename
, name
, instance
, 1, 1, &dest
);
592 * Return the result...
600 * 'cupsRemoveDest()' - Remove a destination from the destination list.
602 * Removing a destination/instance does not delete the class or printer
603 * queue, merely the lpoptions for that destination/instance. Use the
604 * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
605 * options for the user.
607 * @since CUPS 1.3/Mac OS X 10.5@
610 int /* O - New number of destinations */
611 cupsRemoveDest(const char *name
, /* I - Destination name */
612 const char *instance
, /* I - Instance name or @code NULL@ */
613 int num_dests
, /* I - Number of destinations */
614 cups_dest_t
**dests
) /* IO - Destinations */
616 int i
; /* Index into destinations */
617 cups_dest_t
*dest
; /* Pointer to destination */
621 * Find the destination...
624 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) == NULL
)
631 _cupsStrFree(dest
->name
);
632 _cupsStrFree(dest
->instance
);
633 cupsFreeOptions(dest
->num_options
, dest
->options
);
636 * Remove the destination from the array...
644 memmove(dest
, dest
+ 1, (num_dests
- i
) * sizeof(cups_dest_t
));
651 * 'cupsSetDefaultDest()' - Set the default destination.
653 * @since CUPS 1.3/Mac OS X 10.5@
658 const char *name
, /* I - Destination name */
659 const char *instance
, /* I - Instance name or @code NULL@ */
660 int num_dests
, /* I - Number of destinations */
661 cups_dest_t
*dests
) /* I - Destinations */
663 int i
; /* Looping var */
664 cups_dest_t
*dest
; /* Current destination */
668 * Range check input...
671 if (!name
|| num_dests
<= 0 || !dests
)
675 * Loop through the array and set the "is_default" flag for the matching
679 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
680 dest
->is_default
= !strcasecmp(name
, dest
->name
) &&
681 ((!instance
&& !dest
->instance
) ||
682 (instance
&& dest
->instance
&&
683 !strcasecmp(instance
, dest
->instance
)));
688 * 'cupsSetDests()' - Save the list of destinations for the default server.
690 * This function saves the destinations to /etc/cups/lpoptions when run
691 * as root and ~/.cups/lpoptions when run as a normal user.
695 cupsSetDests(int num_dests
, /* I - Number of destinations */
696 cups_dest_t
*dests
) /* I - Destinations */
698 cupsSetDests2(CUPS_HTTP_DEFAULT
, num_dests
, dests
);
703 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
705 * This function saves the destinations to /etc/cups/lpoptions when run
706 * as root and ~/.cups/lpoptions when run as a normal user.
708 * @since CUPS 1.1.21/Mac OS X 10.4@
711 int /* O - 0 on success, -1 on error */
712 cupsSetDests2(http_t
*http
, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
713 int num_dests
, /* I - Number of destinations */
714 cups_dest_t
*dests
) /* I - Destinations */
716 int i
, j
; /* Looping vars */
717 int wrote
; /* Wrote definition? */
718 cups_dest_t
*dest
; /* Current destination */
719 cups_option_t
*option
; /* Current option */
720 _ipp_option_t
*match
; /* Matching attribute for option */
721 FILE *fp
; /* File pointer */
723 const char *home
; /* HOME environment variable */
725 char filename
[1024]; /* lpoptions file */
726 int num_temps
; /* Number of temporary destinations */
727 cups_dest_t
*temps
, /* Temporary destinations */
728 *temp
; /* Current temporary dest */
729 const char *val
; /* Value of temporary option */
730 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
734 * Range check the input...
737 if (!num_dests
|| !dests
)
741 * Get the server destinations...
744 num_temps
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, NULL
, 0, &temps
);
746 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE
)
748 cupsFreeDests(num_temps
, temps
);
753 * Figure out which file to write to...
756 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
762 * Merge in server defaults...
765 num_temps
= cups_get_dests(filename
, NULL
, NULL
, 0, num_temps
, &temps
);
768 * Point to user defaults...
771 if ((home
= getenv("HOME")) != NULL
)
774 * Remove the old ~/.lpoptions file...
777 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
781 * Create ~/.cups subdirectory...
784 snprintf(filename
, sizeof(filename
), "%s/.cups", home
);
785 if (access(filename
, 0))
786 mkdir(filename
, 0700);
788 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
794 * Try to open the file...
797 if ((fp
= fopen(filename
, "w")) == NULL
)
799 cupsFreeDests(num_temps
, temps
);
805 * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
810 fchmod(fileno(fp
), 0644);
814 * Write each printer; each line looks like:
816 * Dest name[/instance] options
817 * Default name[/instance] options
820 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
821 if (dest
->instance
!= NULL
|| dest
->num_options
!= 0 || dest
->is_default
)
823 if (dest
->is_default
)
825 fprintf(fp
, "Default %s", dest
->name
);
827 fprintf(fp
, "/%s", dest
->instance
);
834 if ((temp
= cupsGetDest(dest
->name
, dest
->instance
, num_temps
, temps
)) == NULL
)
835 temp
= cupsGetDest(dest
->name
, NULL
, num_temps
, temps
);
837 for (j
= dest
->num_options
, option
= dest
->options
; j
> 0; j
--, option
++)
840 * See if this option is a printer attribute; if so, skip it...
843 if ((match
= _ippFindOption(option
->name
)) != NULL
&&
844 match
->group_tag
== IPP_TAG_PRINTER
)
848 * See if the server/global options match these; if so, don't
853 (val
= cupsGetOption(option
->name
, temp
->num_options
,
854 temp
->options
)) != NULL
&&
855 !strcasecmp(val
, option
->value
))
859 * Options don't match, write to the file...
864 fprintf(fp
, "Dest %s", dest
->name
);
866 fprintf(fp
, "/%s", dest
->instance
);
870 if (option
->value
[0])
872 if (strchr(option
->value
, ' ') ||
873 strchr(option
->value
, '\\') ||
874 strchr(option
->value
, '\"') ||
875 strchr(option
->value
, '\''))
881 fprintf(fp
, " %s=\"", option
->name
);
883 for (val
= option
->value
; *val
; val
++)
885 if (strchr("\"\'\\", *val
))
896 * Store the literal value...
899 fprintf(fp
, " %s=%s", option
->name
, option
->value
);
903 fprintf(fp
, " %s", option
->name
);
911 * Free the temporary destinations and close the file...
914 cupsFreeDests(num_temps
, temps
);
920 * Set the default printer for this location - this allows command-line
921 * and GUI applications to share the same default destination...
924 if ((dest
= cupsGetDest(NULL
, NULL
, num_dests
, dests
)) != NULL
)
925 appleSetDefault(dest
->name
);
926 #endif /* __APPLE__ */
928 #ifdef HAVE_NOTIFY_POST
930 * Send a notification so that MacOS X applications can know about the
934 notify_post("com.apple.printerListChange");
935 #endif /* HAVE_NOTIFY_POST */
942 * '_cupsUserDefault()' - Get the user default printer from environment
943 * variables and location information.
946 char * /* O - Default printer or NULL */
947 _cupsUserDefault(char *name
, /* I - Name buffer */
948 size_t namesize
) /* I - Size of name buffer */
950 const char *env
; /* LPDEST or PRINTER env variable */
952 CFStringRef network
; /* Network location */
953 CFArrayRef locations
; /* Location array */
954 CFStringRef locprinter
; /* Current printer */
955 #endif /* __APPLE__ */
958 if ((env
= getenv("LPDEST")) == NULL
)
959 if ((env
= getenv("PRINTER")) != NULL
&& !strcmp(env
, "lp"))
964 strlcpy(name
, env
, namesize
);
970 * Use location-based defaults if "use last printer" is selected in the
971 * system preferences...
974 if (!appleUseLastPrinter())
976 DEBUG_puts("1_cupsUserDefault: Not using last printer as default...");
982 * Get the current location...
985 if ((network
= appleCopyNetwork()) == NULL
)
987 DEBUG_puts("1_cupsUserDefault: Unable to get current network...");
993 CFStringGetCString(network
, name
, namesize
, kCFStringEncodingUTF8
);
994 DEBUG_printf(("2_cupsUserDefault: network=\"%s\"", name
));
998 * Lookup the network in the preferences...
1001 if ((locations
= appleCopyLocations()) == NULL
)
1004 * Missing or bad location array, so no location-based default...
1007 DEBUG_puts("1_cupsUserDefault: Missing or bad location history array...");
1015 DEBUG_printf(("2_cupsUserDefault: Got location, %d entries...",
1016 (int)CFArrayGetCount(locations
)));
1018 if ((locprinter
= appleGetPrinter(locations
, network
, NULL
)) != NULL
)
1019 CFStringGetCString(locprinter
, name
, namesize
, kCFStringEncodingUTF8
);
1024 CFRelease(locations
);
1026 DEBUG_printf(("1_cupsUserDefault: Returning \"%s\"...", name
));
1028 return (*name
? name
: NULL
);
1032 * No location-based defaults on this platform...
1037 #endif /* __APPLE__ */
1043 * 'appleCopyLocations()' - Copy the location history array.
1046 static CFArrayRef
/* O - Location array or NULL */
1047 appleCopyLocations(void)
1049 CFArrayRef locations
; /* Location array */
1053 * Look up the location array in the preferences...
1056 if ((locations
= CFPreferencesCopyAppValue(kLocationHistoryArrayKey
,
1057 kPMPrintingPreferences
)) == NULL
)
1060 if (CFGetTypeID(locations
) != CFArrayGetTypeID())
1062 CFRelease(locations
);
1071 * 'appleCopyNetwork()' - Get the network ID for the current location.
1074 static CFStringRef
/* O - Network ID */
1075 appleCopyNetwork(void)
1077 SCDynamicStoreRef dynamicStore
; /* System configuration data */
1078 CFStringRef key
; /* Current network configuration key */
1079 CFDictionaryRef ip_dict
; /* Network configuration data */
1080 CFStringRef network
= NULL
; /* Current network ID */
1083 if ((dynamicStore
= SCDynamicStoreCreate(NULL
, CFSTR("Printing"), NULL
,
1086 if ((key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(
1087 NULL
, kSCDynamicStoreDomainState
, kSCEntNetIPv4
)) != NULL
)
1089 if ((ip_dict
= SCDynamicStoreCopyValue(dynamicStore
, key
)) != NULL
)
1091 if ((network
= CFDictionaryGetValue(ip_dict
,
1092 kSCPropNetIPv4Router
)) != NULL
)
1101 CFRelease(dynamicStore
);
1109 * 'appleGetPaperSize()' - Get the default paper size.
1112 static char * /* O - Default paper size */
1113 appleGetPaperSize(char *name
, /* I - Paper size name buffer */
1114 int namesize
) /* I - Size of buffer */
1116 CFStringRef defaultPaperID
; /* Default paper ID */
1117 _cups_pwg_media_t
*pwgmedia
; /* PWG media size */
1120 defaultPaperID
= CFPreferencesCopyAppValue(kDefaultPaperIDKey
,
1121 kPMPrintingPreferences
);
1122 if (!defaultPaperID
||
1123 CFGetTypeID(defaultPaperID
) != CFStringGetTypeID() ||
1124 !CFStringGetCString(defaultPaperID
, name
, namesize
,
1125 kCFStringEncodingUTF8
))
1127 else if ((pwgmedia
= _cupsPWGMediaByLegacy(name
)) != NULL
)
1128 strlcpy(name
, pwgmedia
->pwg
, namesize
);
1131 CFRelease(defaultPaperID
);
1138 * 'appleGetPrinter()' - Get a printer from the history array.
1141 static CFStringRef
/* O - Printer name or NULL */
1142 appleGetPrinter(CFArrayRef locations
, /* I - Location array */
1143 CFStringRef network
, /* I - Network name */
1144 CFIndex
*locindex
) /* O - Index in array */
1146 CFIndex i
, /* Looping var */
1147 count
; /* Number of locations */
1148 CFDictionaryRef location
; /* Current location */
1149 CFStringRef locnetwork
, /* Current network */
1150 locprinter
; /* Current printer */
1153 for (i
= 0, count
= CFArrayGetCount(locations
); i
< count
; i
++)
1154 if ((location
= CFArrayGetValueAtIndex(locations
, i
)) != NULL
&&
1155 CFGetTypeID(location
) == CFDictionaryGetTypeID())
1157 if ((locnetwork
= CFDictionaryGetValue(location
,
1158 kLocationNetworkKey
)) != NULL
&&
1159 CFGetTypeID(locnetwork
) == CFStringGetTypeID() &&
1160 CFStringCompare(network
, locnetwork
, 0) == kCFCompareEqualTo
&&
1161 (locprinter
= CFDictionaryGetValue(location
,
1162 kLocationPrinterIDKey
)) != NULL
&&
1163 CFGetTypeID(locprinter
) == CFStringGetTypeID())
1168 return (locprinter
);
1177 * 'appleSetDefault()' - Set the default printer for this location.
1181 appleSetDefault(const char *name
) /* I - Default printer/class name */
1183 CFStringRef network
; /* Current network */
1184 CFArrayRef locations
; /* Old locations array */
1185 CFIndex locindex
; /* Index in locations array */
1186 CFStringRef locprinter
; /* Current printer */
1187 CFMutableArrayRef newlocations
; /* New locations array */
1188 CFMutableDictionaryRef newlocation
; /* New location */
1189 CFStringRef newprinter
; /* New printer */
1193 * Get the current location...
1196 if ((network
= appleCopyNetwork()) == NULL
)
1198 DEBUG_puts("1appleSetDefault: Unable to get current network...");
1202 if ((newprinter
= CFStringCreateWithCString(kCFAllocatorDefault
, name
,
1203 kCFStringEncodingUTF8
)) == NULL
)
1210 * Lookup the network in the preferences...
1213 if ((locations
= appleCopyLocations()) != NULL
)
1214 locprinter
= appleGetPrinter(locations
, network
, &locindex
);
1222 CFStringCompare(locprinter
, newprinter
, 0) != kCFCompareEqualTo
)
1225 * Need to change the locations array...
1230 newlocations
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0,
1234 CFArrayRemoveValueAtIndex(newlocations
, locindex
);
1237 newlocations
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, NULL
);
1239 newlocation
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1240 &kCFTypeDictionaryKeyCallBacks
,
1241 &kCFTypeDictionaryValueCallBacks
);
1243 if (newlocation
&& newlocations
)
1246 * Put the new location at the front of the array...
1249 CFDictionaryAddValue(newlocation
, kLocationNetworkKey
, network
);
1250 CFDictionaryAddValue(newlocation
, kLocationPrinterIDKey
, newprinter
);
1251 CFArrayInsertValueAtIndex(newlocations
, 0, newlocation
);
1254 * Limit the number of locations to 10...
1257 while (CFArrayGetCount(newlocations
) > 10)
1258 CFArrayRemoveValueAtIndex(newlocations
, 10);
1261 * Push the changes out...
1264 CFPreferencesSetAppValue(kLocationHistoryArrayKey
, newlocations
,
1265 kPMPrintingPreferences
);
1266 CFPreferencesAppSynchronize(kPMPrintingPreferences
);
1267 notify_post("com.apple.printerPrefsChange");
1271 CFRelease(newlocations
);
1274 CFRelease(newlocation
);
1278 CFRelease(locations
);
1281 CFRelease(newprinter
);
1286 * 'appleUseLastPrinter()' - Get the default printer preference value.
1289 static int /* O - 1 to use last printer, 0 otherwise */
1290 appleUseLastPrinter(void)
1292 CFPropertyListRef uselast
; /* Use last printer preference value */
1295 if (getenv("CUPS_DISABLE_APPLE_DEFAULT"))
1298 if ((uselast
= CFPreferencesCopyAppValue(kUseLastPrinterAsCurrentPrinterKey
,
1299 kPMPrintingPreferences
)) != NULL
)
1303 if (uselast
== kCFBooleanFalse
)
1309 #endif /* __APPLE__ */
1313 * 'cups_add_dest()' - Add a destination to the array.
1315 * Unlike cupsAddDest(), this function does not check for duplicates.
1318 static cups_dest_t
* /* O - New destination */
1319 cups_add_dest(const char *name
, /* I - Name of destination */
1320 const char *instance
, /* I - Instance or NULL */
1321 int *num_dests
, /* IO - Number of destinations */
1322 cups_dest_t
**dests
) /* IO - Destinations */
1324 int insert
, /* Insertion point */
1325 diff
; /* Result of comparison */
1326 cups_dest_t
*dest
; /* Destination pointer */
1330 * Add new destination...
1333 if (*num_dests
== 0)
1334 dest
= malloc(sizeof(cups_dest_t
));
1336 dest
= realloc(*dests
, sizeof(cups_dest_t
) * (*num_dests
+ 1));
1344 * Find where to insert the destination...
1347 if (*num_dests
== 0)
1351 insert
= cups_find_dest(name
, instance
, *num_dests
, *dests
, *num_dests
- 1,
1359 * Move the array elements as needed...
1362 if (insert
< *num_dests
)
1363 memmove(*dests
+ insert
+ 1, *dests
+ insert
,
1364 (*num_dests
- insert
) * sizeof(cups_dest_t
));
1369 * Initialize the destination...
1372 dest
= *dests
+ insert
;
1373 dest
->name
= _cupsStrAlloc(name
);
1374 dest
->instance
= _cupsStrAlloc(instance
);
1375 dest
->is_default
= 0;
1376 dest
->num_options
= 0;
1377 dest
->options
= (cups_option_t
*)0;
1384 * 'cups_compare_dests()' - Compare two destinations.
1387 static int /* O - Result of comparison */
1388 cups_compare_dests(cups_dest_t
*a
, /* I - First destination */
1389 cups_dest_t
*b
) /* I - Second destination */
1391 int diff
; /* Difference */
1394 if ((diff
= strcasecmp(a
->name
, b
->name
)) != 0)
1396 else if (a
->instance
&& b
->instance
)
1397 return (strcasecmp(a
->instance
, b
->instance
));
1399 return ((a
->instance
&& !b
->instance
) - (!a
->instance
&& b
->instance
));
1404 * 'cups_find_dest()' - Find a destination using a binary search.
1407 static int /* O - Index of match */
1408 cups_find_dest(const char *name
, /* I - Destination name */
1409 const char *instance
, /* I - Instance or NULL */
1410 int num_dests
, /* I - Number of destinations */
1411 cups_dest_t
*dests
, /* I - Destinations */
1412 int prev
, /* I - Previous index */
1413 int *rdiff
) /* O - Difference of match */
1415 int left
, /* Low mark for binary search */
1416 right
, /* High mark for binary search */
1417 current
, /* Current index */
1418 diff
; /* Result of comparison */
1419 cups_dest_t key
; /* Search key */
1422 key
.name
= (char *)name
;
1423 key
.instance
= (char *)instance
;
1428 * Start search on either side of previous...
1431 if ((diff
= cups_compare_dests(&key
, dests
+ prev
)) == 0 ||
1432 (diff
< 0 && prev
== 0) ||
1433 (diff
> 0 && prev
== (num_dests
- 1)))
1441 * Start with previous on right side...
1450 * Start wih previous on left side...
1454 right
= num_dests
- 1;
1460 * Start search in the middle...
1464 right
= num_dests
- 1;
1469 current
= (left
+ right
) / 2;
1470 diff
= cups_compare_dests(&key
, dests
+ current
);
1479 while ((right
- left
) > 1);
1484 * Check the last 1 or 2 elements...
1487 if ((diff
= cups_compare_dests(&key
, dests
+ left
)) <= 0)
1491 diff
= cups_compare_dests(&key
, dests
+ right
);
1497 * Return the closest destination and the difference...
1507 * 'cups_get_default()' - Get the default destination from an lpoptions file.
1510 static char * /* O - Default destination or NULL */
1511 cups_get_default(const char *filename
, /* I - File to read */
1512 char *namebuf
, /* I - Name buffer */
1513 size_t namesize
, /* I - Size of name buffer */
1514 const char **instance
) /* I - Instance */
1516 cups_file_t
*fp
; /* lpoptions file */
1517 char line
[8192], /* Line from file */
1518 *value
, /* Value for line */
1519 *nameptr
; /* Pointer into name */
1520 int linenum
; /* Current line */
1525 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
1529 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
1531 if (!strcasecmp(line
, "default") && value
)
1533 strlcpy(namebuf
, value
, namesize
);
1535 if ((nameptr
= strchr(namebuf
, ' ')) != NULL
)
1537 if ((nameptr
= strchr(namebuf
, '\t')) != NULL
)
1540 if ((nameptr
= strchr(namebuf
, '/')) != NULL
)
1543 *instance
= nameptr
;
1551 return (*namebuf
? namebuf
: NULL
);
1556 * 'cups_get_dests()' - Get destinations from a file.
1559 static int /* O - Number of destinations */
1561 const char *filename
, /* I - File to read from */
1562 const char *match_name
, /* I - Destination name we want */
1563 const char *match_inst
, /* I - Instance name we want */
1564 int user_default_set
, /* I - User default printer set? */
1565 int num_dests
, /* I - Number of destinations */
1566 cups_dest_t
**dests
) /* IO - Destinations */
1568 int i
; /* Looping var */
1569 cups_dest_t
*dest
; /* Current destination */
1570 cups_file_t
*fp
; /* File pointer */
1571 char line
[8192], /* Line from file */
1572 *lineptr
, /* Pointer into line */
1573 *name
, /* Name of destination/option */
1574 *instance
; /* Instance of destination */
1575 int linenum
; /* Current line number */
1578 DEBUG_printf(("7cups_get_dests(filename=\"%s\", match_name=\"%s\", "
1579 "match_inst=\"%s\", user_default_set=%d, num_dests=%d, "
1580 "dests=%p)", filename
, match_name
, match_inst
,
1581 user_default_set
, num_dests
, dests
));
1584 * Try to open the file...
1587 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
1591 * Read each printer; each line looks like:
1593 * Dest name[/instance] options
1594 * Default name[/instance] options
1599 while (cupsFileGetConf(fp
, line
, sizeof(line
), &lineptr
, &linenum
))
1602 * See what type of line it is...
1605 DEBUG_printf(("9cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"",
1606 linenum
, line
, lineptr
));
1608 if ((strcasecmp(line
, "dest") && strcasecmp(line
, "default")) || !lineptr
)
1610 DEBUG_puts("9cups_get_dests: Not a dest or default line...");
1617 * Search for an instance...
1620 while (!isspace(*lineptr
& 255) && *lineptr
&& *lineptr
!= '/')
1623 if (*lineptr
== '/')
1626 * Found an instance...
1633 * Search for an instance...
1636 while (!isspace(*lineptr
& 255) && *lineptr
)
1645 DEBUG_printf(("9cups_get_dests: name=\"%s\", instance=\"%s\"", name
,
1649 * See if the primary instance of the destination exists; if not,
1650 * ignore this entry and move on...
1655 if (strcasecmp(name
, match_name
) ||
1656 (!instance
&& match_inst
) ||
1657 (instance
&& !match_inst
) ||
1658 (instance
&& strcasecmp(instance
, match_inst
)))
1663 else if (cupsGetDest(name
, NULL
, num_dests
, *dests
) == NULL
)
1665 DEBUG_puts("9cups_get_dests: Not found!");
1671 * Add the destination...
1674 num_dests
= cupsAddDest(name
, instance
, num_dests
, dests
);
1676 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) == NULL
)
1682 DEBUG_puts("9cups_get_dests: Out of memory!");
1688 * Add options until we hit the end of the line...
1691 dest
->num_options
= cupsParseOptions(lineptr
, dest
->num_options
,
1695 * If we found what we were looking for, stop now...
1702 * Set this as default if needed...
1705 if (!user_default_set
&& !strcasecmp(line
, "default"))
1707 DEBUG_puts("9cups_get_dests: Setting as default...");
1709 for (i
= 0; i
< num_dests
; i
++)
1710 (*dests
)[i
].is_default
= 0;
1712 dest
->is_default
= 1;
1717 * Close the file and return...
1727 * 'cups_get_sdests()' - Get destinations from a server.
1730 static int /* O - Number of destinations */
1731 cups_get_sdests(http_t
*http
, /* I - Connection to server or CUPS_HTTP_DEFAULT */
1732 ipp_op_t op
, /* I - IPP operation */
1733 const char *name
, /* I - Name of destination */
1734 int num_dests
, /* I - Number of destinations */
1735 cups_dest_t
**dests
) /* IO - Destinations */
1737 cups_dest_t
*dest
; /* Current destination */
1738 ipp_t
*request
, /* IPP Request */
1739 *response
; /* IPP Response */
1740 ipp_attribute_t
*attr
; /* Current attribute */
1741 const char *printer_name
; /* printer-name attribute */
1742 char uri
[1024]; /* printer-uri value */
1743 int num_options
; /* Number of options */
1744 cups_option_t
*options
; /* Options */
1746 char media_default
[41]; /* Default paper size */
1747 #endif /* __APPLE__ */
1748 char optname
[1024], /* Option name */
1749 value
[2048], /* Option value */
1750 *ptr
; /* Pointer into name/value */
1751 static const char * const pattrs
[] = /* Attributes we're interested in */
1753 "auth-info-required",
1755 "job-sheets-default",
1756 "marker-change-time",
1758 "marker-high-levels",
1760 "marker-low-levels",
1766 #endif /* __APPLE__ */
1770 "printer-is-accepting-jobs",
1771 "printer-is-shared",
1773 "printer-make-and-model",
1776 "printer-state-change-time",
1777 "printer-state-reasons",
1779 "printer-uri-supported"
1785 * Get the default paper size...
1788 appleGetPaperSize(media_default
, sizeof(media_default
));
1789 #endif /* __APPLE__ */
1792 * Build a CUPS_GET_PRINTERS or IPP_GET_PRINTER_ATTRIBUTES request, which
1793 * require the following attributes:
1795 * attributes-charset
1796 * attributes-natural-language
1797 * requesting-user-name
1798 * printer-uri [for IPP_GET_PRINTER_ATTRIBUTES]
1801 request
= ippNewRequest(op
);
1803 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1804 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
1807 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1808 "requesting-user-name", NULL
, cupsUser());
1810 if (name
&& op
!= CUPS_GET_DEFAULT
)
1812 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1813 "localhost", ippPort(), "/printers/%s", name
);
1814 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
1819 * Do the request and get back a response...
1822 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1824 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1827 * Skip leading attributes until we hit a printer...
1830 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
1837 * Pull the needed attributes from this printer...
1840 printer_name
= NULL
;
1844 for (; attr
&& attr
->group_tag
== IPP_TAG_PRINTER
; attr
= attr
->next
)
1846 if (attr
->value_tag
!= IPP_TAG_INTEGER
&&
1847 attr
->value_tag
!= IPP_TAG_ENUM
&&
1848 attr
->value_tag
!= IPP_TAG_BOOLEAN
&&
1849 attr
->value_tag
!= IPP_TAG_TEXT
&&
1850 attr
->value_tag
!= IPP_TAG_TEXTLANG
&&
1851 attr
->value_tag
!= IPP_TAG_NAME
&&
1852 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
1853 attr
->value_tag
!= IPP_TAG_KEYWORD
&&
1854 attr
->value_tag
!= IPP_TAG_RANGE
&&
1855 attr
->value_tag
!= IPP_TAG_URI
)
1858 if (!strcmp(attr
->name
, "auth-info-required") ||
1859 !strcmp(attr
->name
, "device-uri") ||
1860 !strcmp(attr
->name
, "marker-change-time") ||
1861 !strcmp(attr
->name
, "marker-colors") ||
1862 !strcmp(attr
->name
, "marker-high-levels") ||
1863 !strcmp(attr
->name
, "marker-levels") ||
1864 !strcmp(attr
->name
, "marker-low-levels") ||
1865 !strcmp(attr
->name
, "marker-message") ||
1866 !strcmp(attr
->name
, "marker-names") ||
1867 !strcmp(attr
->name
, "marker-types") ||
1868 !strcmp(attr
->name
, "printer-commands") ||
1869 !strcmp(attr
->name
, "printer-info") ||
1870 !strcmp(attr
->name
, "printer-is-shared") ||
1871 !strcmp(attr
->name
, "printer-make-and-model") ||
1872 !strcmp(attr
->name
, "printer-state") ||
1873 !strcmp(attr
->name
, "printer-state-change-time") ||
1874 !strcmp(attr
->name
, "printer-type") ||
1875 !strcmp(attr
->name
, "printer-is-accepting-jobs") ||
1876 !strcmp(attr
->name
, "printer-location") ||
1877 !strcmp(attr
->name
, "printer-state-reasons") ||
1878 !strcmp(attr
->name
, "printer-uri-supported"))
1881 * Add a printer description attribute...
1884 num_options
= cupsAddOption(attr
->name
,
1885 cups_make_string(attr
, value
,
1887 num_options
, &options
);
1890 else if (!strcmp(attr
->name
, "media-supported"))
1893 * See if we can set a default media size...
1896 int i
; /* Looping var */
1898 for (i
= 0; i
< attr
->num_values
; i
++)
1899 if (!strcasecmp(media_default
, attr
->values
[i
].string
.text
))
1901 num_options
= cupsAddOption("media", media_default
, num_options
,
1906 #endif /* __APPLE__ */
1907 else if (!strcmp(attr
->name
, "printer-name") &&
1908 attr
->value_tag
== IPP_TAG_NAME
)
1909 printer_name
= attr
->values
[0].string
.text
;
1910 else if (strncmp(attr
->name
, "notify-", 7) &&
1911 (attr
->value_tag
== IPP_TAG_BOOLEAN
||
1912 attr
->value_tag
== IPP_TAG_ENUM
||
1913 attr
->value_tag
== IPP_TAG_INTEGER
||
1914 attr
->value_tag
== IPP_TAG_KEYWORD
||
1915 attr
->value_tag
== IPP_TAG_NAME
||
1916 attr
->value_tag
== IPP_TAG_RANGE
) &&
1917 (ptr
= strstr(attr
->name
, "-default")) != NULL
)
1920 * Add a default option...
1923 strlcpy(optname
, attr
->name
, sizeof(optname
));
1924 optname
[ptr
- attr
->name
] = '\0';
1926 if (strcasecmp(optname
, "media") ||
1927 !cupsGetOption("media", num_options
, options
))
1928 num_options
= cupsAddOption(optname
,
1929 cups_make_string(attr
, value
,
1931 num_options
, &options
);
1936 * See if we have everything needed...
1941 cupsFreeOptions(num_options
, options
);
1949 if ((dest
= cups_add_dest(printer_name
, NULL
, &num_dests
, dests
)) != NULL
)
1951 dest
->num_options
= num_options
;
1952 dest
->options
= options
;
1955 cupsFreeOptions(num_options
, options
);
1961 ippDelete(response
);
1965 * Return the count...
1973 * 'cups_make_string()' - Make a comma-separated string of values from an IPP
1977 static char * /* O - New string */
1979 ipp_attribute_t
*attr
, /* I - Attribute to convert */
1980 char *buffer
, /* I - Buffer */
1981 size_t bufsize
) /* I - Size of buffer */
1983 int i
; /* Looping var */
1984 char *ptr
, /* Pointer into buffer */
1985 *end
, /* Pointer to end of buffer */
1986 *valptr
; /* Pointer into string attribute */
1990 * Return quickly if we have a single string value...
1993 if (attr
->num_values
== 1 &&
1994 attr
->value_tag
!= IPP_TAG_INTEGER
&&
1995 attr
->value_tag
!= IPP_TAG_ENUM
&&
1996 attr
->value_tag
!= IPP_TAG_BOOLEAN
&&
1997 attr
->value_tag
!= IPP_TAG_RANGE
)
1998 return (attr
->values
[0].string
.text
);
2001 * Copy the values to the string, separating with commas and escaping strings
2005 end
= buffer
+ bufsize
- 1;
2007 for (i
= 0, ptr
= buffer
; i
< attr
->num_values
&& ptr
< end
; i
++)
2012 switch (attr
->value_tag
)
2014 case IPP_TAG_INTEGER
:
2016 snprintf(ptr
, end
- ptr
+ 1, "%d", attr
->values
[i
].integer
);
2019 case IPP_TAG_BOOLEAN
:
2020 if (attr
->values
[i
].boolean
)
2021 strlcpy(ptr
, "true", end
- ptr
+ 1);
2023 strlcpy(ptr
, "false", end
- ptr
+ 1);
2026 case IPP_TAG_RANGE
:
2027 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
2028 snprintf(ptr
, end
- ptr
+ 1, "%d", attr
->values
[i
].range
.lower
);
2030 snprintf(ptr
, end
- ptr
+ 1, "%d-%d", attr
->values
[i
].range
.lower
,
2031 attr
->values
[i
].range
.upper
);
2035 for (valptr
= attr
->values
[i
].string
.text
;
2036 *valptr
&& ptr
< end
;)
2038 if (strchr(" \t\n\\\'\"", *valptr
))
2040 if (ptr
>= (end
- 1))
2063 * End of "$Id: dest.c 7946 2008-09-16 23:27:54Z mike $".