2 * "$Id: dest.c 7460 2008-04-16 02:19:54Z mike $"
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
7 * Copyright 2007-2008 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() - Get the location history array.
35 * appleCopyNetwork() - Get the network ID for the current location.
36 * appleGetDefault() - Get the default printer for this location.
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_get_default() - Get the default destination from an lpoptions file.
41 * cups_get_dests() - Get destinations from a file.
42 * cups_get_sdests() - Get destinations from a server.
46 * Include necessary headers...
57 #endif /* HAVE_NOTIFY_H */
60 # include <sys/cdefs.h>
61 # include <CoreFoundation/CoreFoundation.h>
62 # include <SystemConfiguration/SystemConfiguration.h>
63 # define kLocationHistoryArrayKey CFSTR("kLocationHistoryArrayKeyTMP")
64 # define kLocationNetworkKey CFSTR("kLocationNetworkKey")
65 # define kLocationPrinterIDKey CFSTR("kLocationPrinterIDKey")
66 # define kPMPrintingPreferences CFSTR("com.apple.print.PrintingPrefs")
67 # define kUseLastPrinterAsCurrentPrinterKey CFSTR("UseLastPrinterAsCurrentPrinter")
68 #endif /* __APPLE__ */
76 static CFArrayRef
appleCopyLocations(void);
77 static CFStringRef
appleCopyNetwork(void);
78 static char *appleGetDefault(char *name
, int namesize
);
79 static CFStringRef
appleGetPrinter(CFArrayRef locations
, CFStringRef network
,
81 static void appleSetDefault(const char *name
);
82 static int appleUseLastPrinter(void);
83 #endif /* __APPLE__ */
84 static char *cups_get_default(const char *filename
, char *namebuf
,
85 size_t namesize
, const char **instance
);
86 static int cups_get_dests(const char *filename
, const char *match_name
,
87 const char *match_inst
, int num_dests
,
89 static int cups_get_sdests(http_t
*http
, ipp_op_t op
, const char *name
,
90 int num_dests
, cups_dest_t
**dests
);
94 * 'cupsAddDest()' - Add a destination to the list of destinations.
96 * This function cannot be used to add a new class or printer queue,
97 * it only adds a new container of saved options for the named
98 * destination or instance.
100 * If the named destination already exists, the destination list is
101 * returned unchanged. Adding a new instance of a destination creates
102 * a copy of that destination's options.
104 * Use the @link cupsSaveDests@ function to save the updated list of
105 * destinations to the user's lpoptions file.
108 int /* O - New number of destinations */
109 cupsAddDest(const char *name
, /* I - Destination name */
110 const char *instance
, /* I - Instance name or @code NULL@ for none/primary */
111 int num_dests
, /* I - Number of destinations */
112 cups_dest_t
**dests
) /* IO - Destinations */
114 int i
; /* Looping var */
115 cups_dest_t
*dest
; /* Destination pointer */
116 cups_dest_t
*parent
; /* Parent destination */
117 cups_option_t
*option
; /* Current option */
123 if (cupsGetDest(name
, instance
, num_dests
, *dests
))
127 * Add new destination...
131 dest
= malloc(sizeof(cups_dest_t
));
133 dest
= realloc(*dests
, sizeof(cups_dest_t
) * (num_dests
+ 1));
141 * Find where to insert the destination...
144 for (i
= num_dests
; i
> 0; i
--, dest
++)
145 if (strcasecmp(name
, dest
->name
) < 0)
147 else if (!instance
&& dest
->instance
)
149 else if (!strcasecmp(name
, dest
->name
) &&
150 instance
&& dest
->instance
&&
151 strcasecmp(instance
, dest
->instance
) < 0)
155 memmove(dest
+ 1, dest
, i
* sizeof(cups_dest_t
));
158 * Initialize the destination...
161 dest
->name
= _cupsStrAlloc(name
);
162 dest
->is_default
= 0;
163 dest
->num_options
= 0;
164 dest
->options
= (cups_option_t
*)0;
167 dest
->instance
= NULL
;
171 * Copy options from the primary instance...
174 dest
->instance
= _cupsStrAlloc(instance
);
176 if ((parent
= cupsGetDest(name
, NULL
, num_dests
+ 1, *dests
)) != NULL
)
178 for (i
= parent
->num_options
, option
= parent
->options
;
181 dest
->num_options
= cupsAddOption(option
->name
, option
->value
,
187 return (num_dests
+ 1);
192 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
196 cupsFreeDests(int num_dests
, /* I - Number of destinations */
197 cups_dest_t
*dests
) /* I - Destinations */
199 int i
; /* Looping var */
200 cups_dest_t
*dest
; /* Current destination */
203 if (num_dests
== 0 || dests
== NULL
)
206 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
208 _cupsStrFree(dest
->name
);
209 _cupsStrFree(dest
->instance
);
211 cupsFreeOptions(dest
->num_options
, dest
->options
);
219 * 'cupsGetDest()' - Get the named destination from the list.
221 * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
222 * list of supported destinations for the current user.
225 cups_dest_t
* /* O - Destination pointer or @code NULL@ */
226 cupsGetDest(const char *name
, /* I - Destination name or @code NULL@ for the default destination */
227 const char *instance
, /* I - Instance name or @code NULL@ */
228 int num_dests
, /* I - Number of destinations */
229 cups_dest_t
*dests
) /* I - Destinations */
231 int comp
; /* Result of comparison */
234 if (num_dests
<= 0 || !dests
)
240 * NULL name for default printer.
243 while (num_dests
> 0)
245 if (dests
->is_default
)
255 * Lookup name and optionally the instance...
258 while (num_dests
> 0)
260 if ((comp
= strcasecmp(name
, dests
->name
)) < 0)
264 if ((!instance
&& !dests
->instance
) ||
265 (instance
!= NULL
&& dests
->instance
!= NULL
&&
266 !strcasecmp(instance
, dests
->instance
)))
280 * 'cupsGetDests()' - Get the list of destinations from the default server.
282 * Starting with CUPS 1.2, the returned list of destinations include the
283 * printer-info, printer-is-accepting-jobs, printer-is-shared,
284 * printer-make-and-model, printer-state, printer-state-change-time,
285 * printer-state-reasons, and printer-type attributes as options.
287 * Use the @link cupsFreeDests@ function to free the destination list and
288 * the @link cupsGetDest@ function to find a particular destination.
291 int /* O - Number of destinations */
292 cupsGetDests(cups_dest_t
**dests
) /* O - Destinations */
294 return (cupsGetDests2(CUPS_HTTP_DEFAULT
, dests
));
299 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
301 * Starting with CUPS 1.2, the returned list of destinations include the
302 * printer-info, printer-is-accepting-jobs, printer-is-shared,
303 * printer-make-and-model, printer-state, printer-state-change-time,
304 * printer-state-reasons, and printer-type attributes as options.
306 * Use the @link cupsFreeDests@ function to free the destination list and
307 * the @link cupsGetDest@ function to find a particular destination.
309 * @since CUPS 1.1.21@
312 int /* O - Number of destinations */
313 cupsGetDests2(http_t
*http
, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
314 cups_dest_t
**dests
) /* O - Destinations */
316 int i
; /* Looping var */
317 int num_dests
; /* Number of destinations */
318 cups_dest_t
*dest
; /* Destination pointer */
319 const char *home
; /* HOME environment variable */
320 char filename
[1024]; /* Local ~/.cups/lpoptions file */
321 const char *defprinter
; /* Default printer */
322 char name
[1024], /* Copy of printer name */
323 *instance
; /* Pointer to instance name */
324 int num_reals
; /* Number of real queues */
325 cups_dest_t
*reals
; /* Real queues */
326 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
330 * Range check the input...
335 _cupsSetError(IPP_INTERNAL_ERROR
, _("Bad NULL dests pointer"), 1);
340 * Initialize destination array...
344 *dests
= (cups_dest_t
*)0;
347 * Grab the printers and classes...
350 num_dests
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, NULL
, num_dests
, dests
);
351 num_dests
= cups_get_sdests(http
, CUPS_GET_CLASSES
, NULL
, num_dests
, dests
);
354 * Make a copy of the "real" queues for a later sanity check...
359 num_reals
= num_dests
;
360 reals
= calloc(num_reals
, sizeof(cups_dest_t
));
363 memcpy(reals
, *dests
, num_reals
* sizeof(cups_dest_t
));
374 * Grab the default destination...
378 if ((defprinter
= appleGetDefault(name
, sizeof(name
))) == NULL
)
379 #endif /* __APPLE__ */
380 defprinter
= cupsGetDefault2(http
);
385 * Grab printer and instance name...
389 if (name
!= defprinter
)
390 #endif /* __APPLE__ */
391 strlcpy(name
, defprinter
, sizeof(name
));
393 if ((instance
= strchr(name
, '/')) != NULL
)
397 * Lookup the printer and instance and make it the default...
400 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
401 dest
->is_default
= 1;
406 * This initialization of "instance" is unnecessary, but avoids a
407 * compiler warning...
414 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
417 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
418 num_dests
= cups_get_dests(filename
, NULL
, NULL
, num_dests
, dests
);
420 if ((home
= getenv("HOME")) != NULL
)
422 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
423 if (access(filename
, 0))
424 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
426 num_dests
= cups_get_dests(filename
, NULL
, NULL
, num_dests
, dests
);
430 * Validate the current default destination - this prevents old
431 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
432 * pointing to a non-existent printer or class...
438 * See if we have a default printer...
441 if ((dest
= cupsGetDest(NULL
, NULL
, num_dests
, *dests
)) != NULL
)
444 * Have a default; see if it is real...
447 dest
= cupsGetDest(dest
->name
, NULL
, num_reals
, reals
);
451 * If dest is NULL, then no default (that exists) is set, so we
452 * need to set a default if one exists...
455 if (dest
== NULL
&& defprinter
!= NULL
)
457 for (i
= 0; i
< num_dests
; i
++)
458 (*dests
)[i
].is_default
= 0;
460 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
461 dest
->is_default
= 1;
472 * Return the number of destinations...
476 _cupsSetError(IPP_OK
, NULL
, 0);
483 * 'cupsGetNamedDest()' - Get options for the named destination.
485 * This function is optimized for retrieving a single destination and should
486 * be used instead of @link cupsGetDests@ and @link cupsGetDest@ when you either
487 * know the name of the destination or want to print to the default destination.
488 * If @code NULL@ is returned, the destination does not exist or there is no
489 * default destination.
491 * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
492 * server will be used.
494 * If "name" is @code NULL@, the default printer for the current user will be
497 * The returned destination must be freed using @link cupsFreeDests@ with a
498 * "num_dests" value of 1.
503 cups_dest_t
* /* O - Destination or @code NULL@ */
504 cupsGetNamedDest(http_t
*http
, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
505 const char *name
, /* I - Destination name or @code NULL@ for the default destination */
506 const char *instance
) /* I - Instance name or @code NULL@ */
508 cups_dest_t
*dest
; /* Destination */
509 char filename
[1024], /* Path to lpoptions */
510 defname
[256]; /* Default printer name */
511 const char *home
= getenv("HOME"); /* Home directory */
512 ipp_op_t op
= IPP_GET_PRINTER_ATTRIBUTES
;
513 /* IPP operation to get server ops */
514 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
518 * If "name" is NULL, find the default destination...
523 if ((name
= getenv("LPDEST")) == NULL
)
524 if ((name
= getenv("PRINTER")) != NULL
&& !strcmp(name
, "lp"))
530 * No default in the environment, try the user's lpoptions files...
533 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
535 if ((name
= cups_get_default(filename
, defname
, sizeof(defname
),
538 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
539 name
= cups_get_default(filename
, defname
, sizeof(defname
),
547 * Still not there? Try the system lpoptions file...
550 snprintf(filename
, sizeof(filename
), "%s/lpoptions",
551 cg
->cups_serverroot
);
552 name
= cups_get_default(filename
, defname
, sizeof(defname
), &instance
);
558 * No locally-set default destination, ask the server...
561 op
= CUPS_GET_DEFAULT
;
566 * Get the printer's attributes...
569 if (!cups_get_sdests(http
, op
, name
, 0, &dest
))
573 dest
->instance
= _cupsStrAlloc(instance
);
576 * Then add local options...
579 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
580 cups_get_dests(filename
, name
, instance
, 1, &dest
);
584 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
586 if (access(filename
, 0))
587 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
589 cups_get_dests(filename
, name
, instance
, 1, &dest
);
593 * Return the result...
601 * 'cupsRemoveDest()' - Remove a destination from the destination list.
603 * Removing a destination/instance does not delete the class or printer
604 * queue, merely the lpoptions for that destination/instance. Use the
605 * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
606 * options for the user.
611 int /* O - New number of destinations */
612 cupsRemoveDest(const char *name
, /* I - Destination name */
613 const char *instance
, /* I - Instance name or @code NULL@ */
614 int num_dests
, /* I - Number of destinations */
615 cups_dest_t
**dests
) /* IO - Destinations */
617 int i
; /* Index into destinations */
618 cups_dest_t
*dest
; /* Pointer to destination */
622 * Find the destination...
625 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) == NULL
)
632 _cupsStrFree(dest
->name
);
633 _cupsStrFree(dest
->instance
);
634 cupsFreeOptions(dest
->num_options
, dest
->options
);
637 * Remove the destination from the array...
645 memmove(dest
, dest
+ 1, (num_dests
- i
) * sizeof(cups_dest_t
));
652 * 'cupsSetDefaultDest()' - Set the default destination.
659 const char *name
, /* I - Destination name */
660 const char *instance
, /* I - Instance name or @code NULL@ */
661 int num_dests
, /* I - Number of destinations */
662 cups_dest_t
*dests
) /* I - Destinations */
664 int i
; /* Looping var */
665 cups_dest_t
*dest
; /* Current destination */
669 * Range check input...
672 if (!name
|| num_dests
<= 0 || !dests
)
676 * Loop through the array and set the "is_default" flag for the matching
680 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
681 dest
->is_default
= !strcasecmp(name
, dest
->name
) &&
682 ((!instance
&& !dest
->instance
) ||
683 (instance
&& dest
->instance
&&
684 !strcasecmp(instance
, dest
->instance
)));
689 * 'cupsSetDests()' - Save the list of destinations for the default server.
691 * This function saves the destinations to /etc/cups/lpoptions when run
692 * as root and ~/.cups/lpoptions when run as a normal user.
696 cupsSetDests(int num_dests
, /* I - Number of destinations */
697 cups_dest_t
*dests
) /* I - Destinations */
699 cupsSetDests2(CUPS_HTTP_DEFAULT
, num_dests
, dests
);
704 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
706 * This function saves the destinations to /etc/cups/lpoptions when run
707 * as root and ~/.cups/lpoptions when run as a normal user.
709 * @since CUPS 1.1.21@
712 int /* O - 0 on success, -1 on error */
713 cupsSetDests2(http_t
*http
, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
714 int num_dests
, /* I - Number of destinations */
715 cups_dest_t
*dests
) /* I - Destinations */
717 int i
, j
; /* Looping vars */
718 int wrote
; /* Wrote definition? */
719 cups_dest_t
*dest
; /* Current destination */
720 cups_option_t
*option
; /* Current option */
721 _ipp_option_t
*match
; /* Matching attribute for option */
722 FILE *fp
; /* File pointer */
724 const char *home
; /* HOME environment variable */
726 char filename
[1024]; /* lpoptions file */
727 int num_temps
; /* Number of temporary destinations */
728 cups_dest_t
*temps
, /* Temporary destinations */
729 *temp
; /* Current temporary dest */
730 const char *val
; /* Value of temporary option */
731 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
735 * Range check the input...
738 if (!num_dests
|| !dests
)
742 * Get the server destinations...
745 num_temps
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, NULL
, 0, &temps
);
746 num_temps
= cups_get_sdests(http
, CUPS_GET_CLASSES
, NULL
, num_temps
, &temps
);
749 * Figure out which file to write to...
752 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
758 * Merge in server defaults...
761 num_temps
= cups_get_dests(filename
, NULL
, NULL
, num_temps
, &temps
);
764 * Point to user defaults...
767 if ((home
= getenv("HOME")) != NULL
)
770 * Remove the old ~/.lpoptions file...
773 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
777 * Create ~/.cups subdirectory...
780 snprintf(filename
, sizeof(filename
), "%s/.cups", home
);
781 if (access(filename
, 0))
782 mkdir(filename
, 0700);
784 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
790 * Try to open the file...
793 if ((fp
= fopen(filename
, "w")) == NULL
)
795 cupsFreeDests(num_temps
, temps
);
801 * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
806 fchmod(fileno(fp
), 0644);
810 * Write each printer; each line looks like:
812 * Dest name[/instance] options
813 * Default name[/instance] options
816 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
817 if (dest
->instance
!= NULL
|| dest
->num_options
!= 0 || dest
->is_default
)
819 if (dest
->is_default
)
821 fprintf(fp
, "Default %s", dest
->name
);
823 fprintf(fp
, "/%s", dest
->instance
);
830 if ((temp
= cupsGetDest(dest
->name
, dest
->instance
, num_temps
, temps
)) == NULL
)
831 temp
= cupsGetDest(dest
->name
, NULL
, num_temps
, temps
);
833 for (j
= dest
->num_options
, option
= dest
->options
; j
> 0; j
--, option
++)
836 * See if this option is a printer attribute; if so, skip it...
839 if ((match
= _ippFindOption(option
->name
)) != NULL
&&
840 match
->group_tag
== IPP_TAG_PRINTER
)
844 * See if the server/global options match these; if so, don't
849 (val
= cupsGetOption(option
->name
, temp
->num_options
,
850 temp
->options
)) != NULL
&&
851 !strcasecmp(val
, option
->value
))
855 * Options don't match, write to the file...
860 fprintf(fp
, "Dest %s", dest
->name
);
862 fprintf(fp
, "/%s", dest
->instance
);
866 if (option
->value
[0])
868 if (strchr(option
->value
, ' ') ||
869 strchr(option
->value
, '\\') ||
870 strchr(option
->value
, '\"') ||
871 strchr(option
->value
, '\''))
877 fprintf(fp
, " %s=\"", option
->name
);
879 for (val
= option
->value
; *val
; val
++)
881 if (strchr("\"\'\\", *val
))
892 * Store the literal value...
895 fprintf(fp
, " %s=%s", option
->name
, option
->value
);
899 fprintf(fp
, " %s", option
->name
);
907 * Free the temporary destinations and close the file...
910 cupsFreeDests(num_temps
, temps
);
916 * Set the default printer for this location - this allows command-line
917 * and GUI applications to share the same default destination...
920 if ((dest
= cupsGetDest(NULL
, NULL
, num_dests
, dests
)) != NULL
)
921 appleSetDefault(dest
->name
);
922 #endif /* __APPLE__ */
924 #ifdef HAVE_NOTIFY_POST
926 * Send a notification so that MacOS X applications can know about the
930 notify_post("com.apple.printerListChange");
931 #endif /* HAVE_NOTIFY_POST */
939 * 'appleCopyLocations()' - Copy the location history array.
942 static CFArrayRef
/* O - Location array or NULL */
943 appleCopyLocations(void)
945 CFArrayRef locations
; /* Location array */
949 * Look up the location array in the preferences...
952 if ((locations
= CFPreferencesCopyAppValue(kLocationHistoryArrayKey
,
953 kPMPrintingPreferences
)) == NULL
)
956 if (CFGetTypeID(locations
) != CFArrayGetTypeID())
958 CFRelease(locations
);
967 * 'appleCopyNetwork()' - Get the network ID for the current location.
970 static CFStringRef
/* O - Network ID */
971 appleCopyNetwork(void)
973 SCDynamicStoreRef dynamicStore
; /* System configuration data */
974 CFStringRef key
; /* Current network configuration key */
975 CFDictionaryRef ip_dict
; /* Network configuration data */
976 CFStringRef network
= NULL
; /* Current network ID */
979 if ((dynamicStore
= SCDynamicStoreCreate(NULL
, CFSTR("Printing"), NULL
,
982 if ((key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(
983 NULL
, kSCDynamicStoreDomainState
, kSCEntNetIPv4
)) != NULL
)
985 if ((ip_dict
= SCDynamicStoreCopyValue(dynamicStore
, key
)) != NULL
)
987 if ((network
= CFDictionaryGetValue(ip_dict
,
988 kSCPropNetIPv4Router
)) != NULL
)
997 CFRelease(dynamicStore
);
1005 * 'appleGetDefault()' - Get the default printer for this location.
1008 static char * /* O - Name or NULL if no default */
1009 appleGetDefault(char *name
, /* I - Name buffer */
1010 int namesize
) /* I - Size of name buffer */
1012 CFStringRef network
; /* Network location */
1013 CFArrayRef locations
; /* Location array */
1014 CFStringRef locprinter
; /* Current printer */
1018 * Use location-based defaults if "use last printer" is selected in the
1019 * system preferences...
1022 if (!appleUseLastPrinter())
1024 DEBUG_puts("appleGetDefault: Not using last printer as default...");
1029 * Get the current location...
1032 if ((network
= appleCopyNetwork()) == NULL
)
1034 DEBUG_puts("appleGetDefault: Unable to get current network...");
1039 CFStringGetCString(network
, name
, namesize
, kCFStringEncodingUTF8
);
1040 DEBUG_printf(("appleGetDefault: network=\"%s\"\n", name
));
1044 * Lookup the network in the preferences...
1047 if ((locations
= appleCopyLocations()) == NULL
)
1050 * Missing or bad location array, so no location-based default...
1053 DEBUG_puts("appleGetDefault: Missing or bad location history array...");
1060 DEBUG_printf(("appleGetDefault: Got location, %d entries...\n",
1061 (int)CFArrayGetCount(locations
)));
1063 if ((locprinter
= appleGetPrinter(locations
, network
, NULL
)) != NULL
)
1064 CFStringGetCString(locprinter
, name
, namesize
, kCFStringEncodingUTF8
);
1069 CFRelease(locations
);
1071 DEBUG_printf(("appleGetDefault: Returning \"%s\"...\n", name
));
1073 return (*name
? name
: NULL
);
1078 * 'appleGetPrinter()' - Get a printer from the history array.
1081 static CFStringRef
/* O - Printer name or NULL */
1082 appleGetPrinter(CFArrayRef locations
, /* I - Location array */
1083 CFStringRef network
, /* I - Network name */
1084 CFIndex
*locindex
) /* O - Index in array */
1086 CFIndex i
, /* Looping var */
1087 count
; /* Number of locations */
1088 CFDictionaryRef location
; /* Current location */
1089 CFStringRef locnetwork
, /* Current network */
1090 locprinter
; /* Current printer */
1093 for (i
= 0, count
= CFArrayGetCount(locations
); i
< count
; i
++)
1094 if ((location
= CFArrayGetValueAtIndex(locations
, i
)) != NULL
&&
1095 CFGetTypeID(location
) == CFDictionaryGetTypeID())
1097 if ((locnetwork
= CFDictionaryGetValue(location
,
1098 kLocationNetworkKey
)) != NULL
&&
1099 CFGetTypeID(locnetwork
) == CFStringGetTypeID() &&
1100 CFStringCompare(network
, locnetwork
, 0) == kCFCompareEqualTo
&&
1101 (locprinter
= CFDictionaryGetValue(location
,
1102 kLocationPrinterIDKey
)) != NULL
&&
1103 CFGetTypeID(locprinter
) == CFStringGetTypeID())
1108 return (locprinter
);
1117 * 'appleSetDefault()' - Set the default printer for this location.
1121 appleSetDefault(const char *name
) /* I - Default printer/class name */
1123 CFStringRef network
; /* Current network */
1124 CFArrayRef locations
; /* Old locations array */
1125 CFIndex locindex
; /* Index in locations array */
1126 CFStringRef locprinter
; /* Current printer */
1127 CFMutableArrayRef newlocations
; /* New locations array */
1128 CFMutableDictionaryRef newlocation
; /* New location */
1129 CFStringRef newprinter
; /* New printer */
1133 * Get the current location...
1136 if ((network
= appleCopyNetwork()) == NULL
)
1138 DEBUG_puts("appleSetDefault: Unable to get current network...");
1142 if ((newprinter
= CFStringCreateWithCString(kCFAllocatorDefault
, name
,
1143 kCFStringEncodingUTF8
)) == NULL
)
1150 * Lookup the network in the preferences...
1153 if ((locations
= appleCopyLocations()) != NULL
)
1154 locprinter
= appleGetPrinter(locations
, network
, &locindex
);
1162 CFStringCompare(locprinter
, newprinter
, 0) != kCFCompareEqualTo
)
1165 * Need to change the locations array...
1170 newlocations
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0,
1174 CFArrayRemoveValueAtIndex(newlocations
, locindex
);
1177 newlocations
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, NULL
);
1179 newlocation
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1180 &kCFTypeDictionaryKeyCallBacks
,
1181 &kCFTypeDictionaryValueCallBacks
);
1183 if (newlocation
&& newlocations
)
1186 * Put the new location at the front of the array...
1189 CFDictionaryAddValue(newlocation
, kLocationNetworkKey
, network
);
1190 CFDictionaryAddValue(newlocation
, kLocationPrinterIDKey
, newprinter
);
1191 CFArrayInsertValueAtIndex(newlocations
, 0, newlocation
);
1194 * Limit the number of locations to 10...
1197 while (CFArrayGetCount(newlocations
) > 10)
1198 CFArrayRemoveValueAtIndex(newlocations
, 10);
1201 * Push the changes out...
1204 CFPreferencesSetAppValue(kLocationHistoryArrayKey
, newlocations
,
1205 kPMPrintingPreferences
);
1206 CFPreferencesAppSynchronize(kPMPrintingPreferences
);
1210 CFRelease(newlocations
);
1213 CFRelease(newlocation
);
1217 CFRelease(locations
);
1220 CFRelease(newprinter
);
1225 * 'appleUseLastPrinter()' - Get the default printer preference value.
1228 static int /* O - 1 to use last printer, 0 otherwise */
1229 appleUseLastPrinter(void)
1231 CFPropertyListRef uselast
; /* Use last printer preference value */
1234 if ((uselast
= CFPreferencesCopyAppValue(kUseLastPrinterAsCurrentPrinterKey
,
1235 kPMPrintingPreferences
)) != NULL
)
1239 if (uselast
== kCFBooleanFalse
)
1245 #endif /* __APPLE__ */
1249 * 'cups_get_default()' - Get the default destination from an lpoptions file.
1252 static char * /* O - Default destination or NULL */
1253 cups_get_default(const char *filename
, /* I - File to read */
1254 char *namebuf
, /* I - Name buffer */
1255 size_t namesize
, /* I - Size of name buffer */
1256 const char **instance
) /* I - Instance */
1258 cups_file_t
*fp
; /* lpoptions file */
1259 char line
[8192], /* Line from file */
1260 *value
, /* Value for line */
1261 *nameptr
; /* Pointer into name */
1262 int linenum
; /* Current line */
1267 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
1271 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
1273 if (!strcasecmp(line
, "default") && value
)
1275 strlcpy(namebuf
, value
, namesize
);
1277 if ((nameptr
= strchr(namebuf
, ' ')) != NULL
)
1279 if ((nameptr
= strchr(namebuf
, '\t')) != NULL
)
1282 if ((nameptr
= strchr(namebuf
, '/')) != NULL
)
1285 *instance
= nameptr
;
1293 return (*namebuf
? namebuf
: NULL
);
1298 * 'cups_get_dests()' - Get destinations from a file.
1301 static int /* O - Number of destinations */
1302 cups_get_dests(const char *filename
, /* I - File to read from */
1303 const char *match_name
, /* I - Destination name we want */
1304 const char *match_inst
, /* I - Instance name we want */
1305 int num_dests
, /* I - Number of destinations */
1306 cups_dest_t
**dests
) /* IO - Destinations */
1308 int i
; /* Looping var */
1309 cups_dest_t
*dest
; /* Current destination */
1310 cups_file_t
*fp
; /* File pointer */
1311 char line
[8192], /* Line from file */
1312 *lineptr
, /* Pointer into line */
1313 *name
, /* Name of destination/option */
1314 *instance
; /* Instance of destination */
1315 int linenum
; /* Current line number */
1316 const char *printer
; /* PRINTER or LPDEST */
1319 DEBUG_printf(("cups_get_dests(filename=\"%s\", match_name=\"%s\", "
1320 "match_inst=\"%s\", num_dests=%d, dests=%p)\n", filename
,
1321 match_name
? match_name
: "(null)",
1322 match_inst
? match_inst
: "(null)", num_dests
, dests
));
1325 * Try to open the file...
1328 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
1332 * Check environment variables...
1335 if ((printer
= getenv("LPDEST")) == NULL
)
1336 if ((printer
= getenv("PRINTER")) != NULL
)
1337 if (strcmp(printer
, "lp") == 0)
1340 DEBUG_printf(("cups_get_dests: printer=\"%s\"\n",
1341 printer
? printer
: "(null)"));
1344 * Read each printer; each line looks like:
1346 * Dest name[/instance] options
1347 * Default name[/instance] options
1352 while (cupsFileGetConf(fp
, line
, sizeof(line
), &lineptr
, &linenum
))
1355 * See what type of line it is...
1358 DEBUG_printf(("cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"\n",
1359 linenum
, line
, lineptr
? lineptr
: "(null)"));
1361 if ((strcasecmp(line
, "dest") && strcasecmp(line
, "default")) || !lineptr
)
1363 DEBUG_puts("cups_get_dests: Not a dest or default line...");
1370 * Search for an instance...
1373 while (!isspace(*lineptr
& 255) && *lineptr
&& *lineptr
!= '/')
1376 if (*lineptr
== '/')
1379 * Found an instance...
1386 * Search for an instance...
1389 while (!isspace(*lineptr
& 255) && *lineptr
)
1398 DEBUG_printf(("cups_get_dests: name=\"%s\", instance=\"%s\"\n", name
,
1402 * See if the primary instance of the destination exists; if not,
1403 * ignore this entry and move on...
1408 if (strcasecmp(name
, match_name
) ||
1409 (!instance
&& match_inst
) ||
1410 (instance
&& !match_inst
) ||
1411 (instance
&& strcasecmp(instance
, match_inst
)))
1416 else if (cupsGetDest(name
, NULL
, num_dests
, *dests
) == NULL
)
1418 DEBUG_puts("cups_get_dests: Not found!");
1424 * Add the destination...
1427 num_dests
= cupsAddDest(name
, instance
, num_dests
, dests
);
1429 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) == NULL
)
1435 DEBUG_puts("cups_get_dests: Out of memory!");
1441 * Add options until we hit the end of the line...
1444 dest
->num_options
= cupsParseOptions(lineptr
, dest
->num_options
,
1448 * If we found what we were looking for, stop now...
1455 * Set this as default if needed...
1458 if (!printer
&& !strcasecmp(line
, "default"))
1460 DEBUG_puts("cups_get_dests: Setting as default...");
1462 for (i
= 0; i
< num_dests
; i
++)
1463 (*dests
)[i
].is_default
= 0;
1465 dest
->is_default
= 1;
1470 * Close the file and return...
1480 * 'cups_get_sdests()' - Get destinations from a server.
1483 static int /* O - Number of destinations */
1484 cups_get_sdests(http_t
*http
, /* I - Connection to server or CUPS_HTTP_DEFAULT */
1485 ipp_op_t op
, /* I - IPP operation */
1486 const char *name
, /* I - Name of destination */
1487 int num_dests
, /* I - Number of destinations */
1488 cups_dest_t
**dests
) /* IO - Destinations */
1490 int i
; /* Looping var */
1491 cups_dest_t
*dest
; /* Current destination */
1492 ipp_t
*request
, /* IPP Request */
1493 *response
; /* IPP Response */
1494 ipp_attribute_t
*attr
; /* Current attribute */
1495 int accepting
, /* printer-is-accepting-jobs attribute */
1496 shared
, /* printer-is-shared attribute */
1497 state
, /* printer-state attribute */
1498 change_time
, /* printer-state-change-time attribute */
1499 type
; /* printer-type attribute */
1500 const char *info
, /* printer-info attribute */
1501 *location
, /* printer-location attribute */
1502 *make_model
, /* printer-make-and-model attribute */
1503 *printer_name
; /* printer-name attribute */
1504 char uri
[1024], /* printer-uri value */
1505 job_sheets
[1024], /* job-sheets-default attribute */
1506 auth_info_req
[1024], /* auth-info-required attribute */
1507 reasons
[1024]; /* printer-state-reasons attribute */
1508 int num_options
; /* Number of options */
1509 cups_option_t
*options
; /* Options */
1510 char optname
[1024], /* Option name */
1511 value
[2048], /* Option value */
1512 *ptr
; /* Pointer into name/value */
1513 static const char * const pattrs
[] = /* Attributes we're interested in */
1515 "auth-info-required",
1516 "job-sheets-default",
1518 "printer-is-accepting-jobs",
1519 "printer-is-shared",
1521 "printer-make-and-model",
1524 "printer-state-change-time",
1525 "printer-state-reasons",
1532 * Build a CUPS_GET_PRINTERS or CUPS_GET_CLASSES request, which require
1533 * the following attributes:
1535 * attributes-charset
1536 * attributes-natural-language
1537 * requesting-user-name
1540 request
= ippNewRequest(op
);
1542 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1543 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
1546 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1547 "requesting-user-name", NULL
, cupsUser());
1551 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1552 "localhost", ippPort(), "/printers/%s", name
);
1553 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
,
1558 * Do the request and get back a response...
1561 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1563 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1566 * Skip leading attributes until we hit a printer...
1569 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
1576 * Pull the needed attributes from this printer...
1584 printer_name
= NULL
;
1588 state
= IPP_PRINTER_IDLE
;
1589 type
= CUPS_PRINTER_LOCAL
;
1591 auth_info_req
[0] = '\0';
1592 job_sheets
[0] = '\0';
1595 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_PRINTER
)
1597 if (!strcmp(attr
->name
, "auth-info-required") &&
1598 attr
->value_tag
== IPP_TAG_KEYWORD
)
1600 strlcpy(auth_info_req
, attr
->values
[0].string
.text
,
1601 sizeof(auth_info_req
));
1603 for (i
= 1, ptr
= auth_info_req
+ strlen(auth_info_req
);
1604 i
< attr
->num_values
;
1607 snprintf(ptr
, sizeof(auth_info_req
) - (ptr
- auth_info_req
), ",%s",
1608 attr
->values
[i
].string
.text
);
1612 else if (!strcmp(attr
->name
, "job-sheets-default") &&
1613 (attr
->value_tag
== IPP_TAG_KEYWORD
||
1614 attr
->value_tag
== IPP_TAG_NAME
))
1616 if (attr
->num_values
== 2)
1617 snprintf(job_sheets
, sizeof(job_sheets
), "%s,%s",
1618 attr
->values
[0].string
.text
, attr
->values
[1].string
.text
);
1620 strlcpy(job_sheets
, attr
->values
[0].string
.text
,
1621 sizeof(job_sheets
));
1623 else if (!strcmp(attr
->name
, "printer-info") &&
1624 attr
->value_tag
== IPP_TAG_TEXT
)
1625 info
= attr
->values
[0].string
.text
;
1626 else if (!strcmp(attr
->name
, "printer-is-accepting-jobs") &&
1627 attr
->value_tag
== IPP_TAG_BOOLEAN
)
1628 accepting
= attr
->values
[0].boolean
;
1629 else if (!strcmp(attr
->name
, "printer-is-shared") &&
1630 attr
->value_tag
== IPP_TAG_BOOLEAN
)
1631 shared
= attr
->values
[0].boolean
;
1632 else if (!strcmp(attr
->name
, "printer-location") &&
1633 attr
->value_tag
== IPP_TAG_TEXT
)
1634 location
= attr
->values
[0].string
.text
;
1635 else if (!strcmp(attr
->name
, "printer-make-and-model") &&
1636 attr
->value_tag
== IPP_TAG_TEXT
)
1637 make_model
= attr
->values
[0].string
.text
;
1638 else if (!strcmp(attr
->name
, "printer-name") &&
1639 attr
->value_tag
== IPP_TAG_NAME
)
1640 printer_name
= attr
->values
[0].string
.text
;
1641 else if (!strcmp(attr
->name
, "printer-state") &&
1642 attr
->value_tag
== IPP_TAG_ENUM
)
1643 state
= attr
->values
[0].integer
;
1644 else if (!strcmp(attr
->name
, "printer-state-change-time") &&
1645 attr
->value_tag
== IPP_TAG_INTEGER
)
1646 change_time
= attr
->values
[0].integer
;
1647 else if (!strcmp(attr
->name
, "printer-state-reasons") &&
1648 attr
->value_tag
== IPP_TAG_KEYWORD
)
1650 strlcpy(reasons
, attr
->values
[0].string
.text
, sizeof(reasons
));
1651 for (i
= 1, ptr
= reasons
+ strlen(reasons
);
1652 i
< attr
->num_values
;
1655 snprintf(ptr
, sizeof(reasons
) - (ptr
- reasons
), ",%s",
1656 attr
->values
[i
].string
.text
);
1660 else if (!strcmp(attr
->name
, "printer-type") &&
1661 attr
->value_tag
== IPP_TAG_ENUM
)
1662 type
= attr
->values
[0].integer
;
1663 else if (strncmp(attr
->name
, "notify-", 7) &&
1664 (attr
->value_tag
== IPP_TAG_BOOLEAN
||
1665 attr
->value_tag
== IPP_TAG_ENUM
||
1666 attr
->value_tag
== IPP_TAG_INTEGER
||
1667 attr
->value_tag
== IPP_TAG_KEYWORD
||
1668 attr
->value_tag
== IPP_TAG_NAME
||
1669 attr
->value_tag
== IPP_TAG_RANGE
) &&
1670 strstr(attr
->name
, "-default"))
1672 char *valptr
; /* Pointer into attribute value */
1676 * Add a default option...
1679 strlcpy(optname
, attr
->name
, sizeof(optname
));
1680 if ((ptr
= strstr(optname
, "-default")) != NULL
)
1684 for (i
= 0, ptr
= value
; i
< attr
->num_values
; i
++)
1686 if (ptr
>= (value
+ sizeof(value
) - 1))
1692 switch (attr
->value_tag
)
1694 case IPP_TAG_INTEGER
:
1696 snprintf(ptr
, sizeof(value
) - (ptr
- value
), "%d",
1697 attr
->values
[i
].integer
);
1700 case IPP_TAG_BOOLEAN
:
1701 if (attr
->values
[i
].boolean
)
1702 strlcpy(ptr
, "true", sizeof(value
) - (ptr
- value
));
1704 strlcpy(ptr
, "false", sizeof(value
) - (ptr
- value
));
1707 case IPP_TAG_RANGE
:
1708 if (attr
->values
[i
].range
.lower
==
1709 attr
->values
[i
].range
.upper
)
1710 snprintf(ptr
, sizeof(value
) - (ptr
- value
), "%d",
1711 attr
->values
[i
].range
.lower
);
1713 snprintf(ptr
, sizeof(value
) - (ptr
- value
), "%d-%d",
1714 attr
->values
[i
].range
.lower
,
1715 attr
->values
[i
].range
.upper
);
1719 for (valptr
= attr
->values
[i
].string
.text
;
1720 *valptr
&& ptr
< (value
+ sizeof(value
) - 2);)
1722 if (strchr(" \t\n\\\'\"", *valptr
))
1735 num_options
= cupsAddOption(optname
, value
, num_options
, &options
);
1742 * See if we have everything needed...
1747 cupsFreeOptions(num_options
, options
);
1755 num_dests
= cupsAddDest(printer_name
, NULL
, num_dests
, dests
);
1757 if ((dest
= cupsGetDest(printer_name
, NULL
, num_dests
, *dests
)) != NULL
)
1759 dest
->num_options
= num_options
;
1760 dest
->options
= options
;
1765 if (auth_info_req
[0])
1766 dest
->num_options
= cupsAddOption("auth-info-required", auth_info_req
,
1771 dest
->num_options
= cupsAddOption("job-sheets", job_sheets
,
1776 dest
->num_options
= cupsAddOption("printer-info", info
,
1780 sprintf(value
, "%d", accepting
);
1781 dest
->num_options
= cupsAddOption("printer-is-accepting-jobs", value
,
1785 sprintf(value
, "%d", shared
);
1786 dest
->num_options
= cupsAddOption("printer-is-shared", value
,
1791 dest
->num_options
= cupsAddOption("printer-location",
1792 location
, dest
->num_options
,
1796 dest
->num_options
= cupsAddOption("printer-make-and-model",
1797 make_model
, dest
->num_options
,
1800 sprintf(value
, "%d", state
);
1801 dest
->num_options
= cupsAddOption("printer-state", value
,
1807 sprintf(value
, "%d", change_time
);
1808 dest
->num_options
= cupsAddOption("printer-state-change-time", value
,
1814 dest
->num_options
= cupsAddOption("printer-state-reasons", reasons
,
1818 sprintf(value
, "%d", type
);
1819 dest
->num_options
= cupsAddOption("printer-type", value
,
1824 cupsFreeOptions(num_options
, options
);
1830 ippDelete(response
);
1834 * Return the count...
1842 * End of "$Id: dest.c 7460 2008-04-16 02:19:54Z mike $".