| /* |
| * Printer option program for CUPS. |
| * |
| * Copyright © 2007-2018 by Apple Inc. |
| * Copyright © 1997-2006 by Easy Software Products. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more |
| * information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include <cups/cups-private.h> |
| #include <cups/ppd-private.h> |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static void list_group(ppd_file_t *ppd, ppd_group_t *group); |
| static void list_options(cups_dest_t *dest); |
| static void usage(void) _CUPS_NORETURN; |
| |
| |
| /* |
| * 'main()' - Main entry. |
| */ |
| |
| int /* O - Exit status */ |
| main(int argc, /* I - Number of command-line arguments */ |
| char *argv[]) /* I - Command-line arguments */ |
| { |
| int i, j; /* Looping vars */ |
| int changes; /* Did we make changes? */ |
| int num_options; /* Number of options */ |
| cups_option_t *options; /* Options */ |
| int num_dests; /* Number of destinations */ |
| cups_dest_t *dests; /* Destinations */ |
| cups_dest_t *dest; /* Current destination */ |
| char *opt, /* Option pointer */ |
| *printer, /* Printer name */ |
| *instance, /* Instance name */ |
| *option; /* Current option */ |
| |
| |
| _cupsSetLocale(argv); |
| |
| /* |
| * Loop through the command-line arguments... |
| */ |
| |
| dest = NULL; |
| num_dests = 0; |
| dests = NULL; |
| num_options = 0; |
| options = NULL; |
| changes = 0; |
| |
| for (i = 1; i < argc; i ++) |
| { |
| if (!strcmp(argv[i], "--help")) |
| usage(); |
| else if (argv[i][0] == '-') |
| { |
| for (opt = argv[i] + 1; *opt; opt ++) |
| { |
| switch (*opt) |
| { |
| case 'd' : /* -d printer */ |
| if (opt[1] != '\0') |
| { |
| printer = opt + 1; |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| printer = argv[i]; |
| } |
| |
| if ((instance = strrchr(printer, '/')) != NULL) |
| *instance++ = '\0'; |
| |
| if (num_dests == 0) |
| num_dests = cupsGetDests(&dests); |
| |
| if (num_dests == 0 || !dests || (dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL) |
| { |
| _cupsLangPuts(stderr, _("lpoptions: Unknown printer or class.")); |
| return (1); |
| } |
| |
| /* |
| * Set the default destination... |
| */ |
| |
| for (j = 0; j < num_dests; j ++) |
| dests[j].is_default = 0; |
| |
| dest->is_default = 1; |
| |
| cupsSetDests(num_dests, dests); |
| |
| for (j = 0; j < dest->num_options; j ++) |
| if (cupsGetOption(dest->options[j].name, num_options, |
| options) == NULL) |
| num_options = cupsAddOption(dest->options[j].name, |
| dest->options[j].value, |
| num_options, &options); |
| break; |
| |
| case 'h' : /* -h server */ |
| if (opt[1] != '\0') |
| { |
| cupsSetServer(opt + 1); |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| cupsSetServer(argv[i]); |
| } |
| break; |
| |
| case 'E' : /* Encrypt connection */ |
| cupsSetEncryption(HTTP_ENCRYPT_REQUIRED); |
| break; |
| |
| case 'l' : /* -l (list options) */ |
| if (dest == NULL) |
| { |
| if (num_dests == 0) |
| num_dests = cupsGetDests(&dests); |
| |
| if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) |
| dest = dests; |
| } |
| |
| if (dest == NULL) |
| _cupsLangPuts(stderr, _("lpoptions: No printers.")); |
| else |
| list_options(dest); |
| |
| changes = -1; |
| break; |
| |
| case 'o' : /* -o option[=value] */ |
| if (dest == NULL) |
| { |
| if (num_dests == 0) |
| num_dests = cupsGetDests(&dests); |
| |
| if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) |
| dest = dests; |
| |
| if (dest == NULL) |
| { |
| _cupsLangPuts(stderr, _("lpoptions: No printers.")); |
| return (1); |
| } |
| |
| for (j = 0; j < dest->num_options; j ++) |
| if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) |
| num_options = cupsAddOption(dest->options[j].name, |
| dest->options[j].value, |
| num_options, &options); |
| } |
| |
| if (opt[1] != '\0') |
| { |
| num_options = cupsParseOptions(opt + 1, num_options, &options); |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| num_options = cupsParseOptions(argv[i], num_options, &options); |
| } |
| |
| changes = 1; |
| break; |
| |
| case 'p' : /* -p printer */ |
| if (opt[1] != '\0') |
| { |
| printer = opt + 1; |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| printer = argv[i]; |
| } |
| |
| if ((instance = strrchr(printer, '/')) != NULL) |
| *instance++ = '\0'; |
| |
| if (num_dests == 0) |
| num_dests = cupsGetDests(&dests); |
| |
| if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL) |
| { |
| num_dests = cupsAddDest(printer, instance, num_dests, &dests); |
| dest = cupsGetDest(printer, instance, num_dests, dests); |
| |
| if (dest == NULL) |
| { |
| _cupsLangPrintf(stderr, _("lpoptions: Unable to add printer or instance: %s"), strerror(errno)); |
| return (1); |
| } |
| } |
| |
| for (j = 0; j < dest->num_options; j ++) |
| if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) |
| num_options = cupsAddOption(dest->options[j].name, |
| dest->options[j].value, |
| num_options, &options); |
| break; |
| |
| case 'r' : /* -r option (remove) */ |
| if (dest == NULL) |
| { |
| if (num_dests == 0) |
| num_dests = cupsGetDests(&dests); |
| |
| if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) |
| dest = dests; |
| |
| if (dest == NULL) |
| { |
| _cupsLangPuts(stderr, _("lpoptions: No printers.")); |
| return (1); |
| } |
| |
| for (j = 0; j < dest->num_options; j ++) |
| if (cupsGetOption(dest->options[j].name, num_options, |
| options) == NULL) |
| num_options = cupsAddOption(dest->options[j].name, |
| dest->options[j].value, |
| num_options, &options); |
| } |
| |
| if (opt[1] != '\0') |
| { |
| option = opt + 1; |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| option = argv[i]; |
| } |
| |
| num_options = cupsRemoveOption(option, num_options, &options); |
| |
| changes = 1; |
| break; |
| |
| case 'x' : /* -x printer */ |
| if (opt[1] != '\0') |
| { |
| printer = opt + 1; |
| opt += strlen(opt) - 1; |
| } |
| else |
| { |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| printer = argv[i]; |
| } |
| |
| if ((instance = strrchr(printer, '/')) != NULL) |
| *instance++ = '\0'; |
| |
| if (num_dests == 0) |
| num_dests = cupsGetDests(&dests); |
| |
| num_dests = cupsRemoveDest(printer, instance, num_dests, &dests); |
| |
| cupsSetDests(num_dests, dests); |
| dest = NULL; |
| changes = -1; |
| break; |
| |
| default : |
| usage(); |
| } |
| } |
| } |
| else |
| { |
| usage(); |
| } |
| } |
| |
| if (num_dests == 0) |
| num_dests = cupsGetDests(&dests); |
| |
| if (dest == NULL) |
| { |
| if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL) |
| { |
| for (j = 0; j < dest->num_options; j ++) |
| if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) |
| num_options = cupsAddOption(dest->options[j].name, |
| dest->options[j].value, |
| num_options, &options); |
| } |
| } |
| |
| if (dest == NULL) |
| return (0); |
| |
| if (changes > 0) |
| { |
| /* |
| * Set printer options... |
| */ |
| |
| cupsFreeOptions(dest->num_options, dest->options); |
| |
| dest->num_options = num_options; |
| dest->options = options; |
| |
| cupsSetDests(num_dests, dests); |
| } |
| else if (changes == 0) |
| { |
| char buffer[10240], /* String for options */ |
| *ptr; /* Pointer into string */ |
| |
| num_options = dest->num_options; |
| options = dest->options; |
| |
| for (i = 0, ptr = buffer; |
| ptr < (buffer + sizeof(buffer) - 1) && i < num_options; |
| i ++) |
| { |
| if (i) |
| *ptr++ = ' '; |
| |
| if (!options[i].value[0]) |
| strlcpy(ptr, options[i].name, sizeof(buffer) - (size_t)(ptr - buffer)); |
| else if (strchr(options[i].value, ' ') != NULL || |
| strchr(options[i].value, '\t') != NULL) |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=\'%s\'", options[i].name, options[i].value); |
| else |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=%s", options[i].name, options[i].value); |
| |
| ptr += strlen(ptr); |
| } |
| |
| _cupsLangPuts(stdout, buffer); |
| } |
| |
| return (0); |
| } |
| |
| /* |
| * 'list_group()' - List printer-specific options from the PPD group. |
| */ |
| |
| static void |
| list_group(ppd_file_t *ppd, /* I - PPD file */ |
| ppd_group_t *group) /* I - Group to show */ |
| { |
| int i, j; /* Looping vars */ |
| ppd_option_t *option; /* Current option */ |
| ppd_choice_t *choice; /* Current choice */ |
| ppd_group_t *subgroup; /* Current subgroup */ |
| char buffer[10240], /* Option string buffer */ |
| *ptr; /* Pointer into option string */ |
| |
| |
| for (i = group->num_options, option = group->options; i > 0; i --, option ++) |
| { |
| if (!_cups_strcasecmp(option->keyword, "PageRegion")) |
| continue; |
| |
| snprintf(buffer, sizeof(buffer), "%s/%s:", option->keyword, option->text); |
| |
| for (j = option->num_choices, choice = option->choices, |
| ptr = buffer + strlen(buffer); |
| j > 0 && ptr < (buffer + sizeof(buffer) - 1); |
| j --, choice ++) |
| { |
| if (!_cups_strcasecmp(choice->choice, "Custom")) |
| { |
| ppd_coption_t *coption; /* Custom option */ |
| ppd_cparam_t *cparam; /* Custom parameter */ |
| static const char * const types[] = |
| { /* Parameter types */ |
| "CURVE", |
| "INTEGER", |
| "INVCURVE", |
| "PASSCODE", |
| "PASSWORD", |
| "POINTS", |
| "REAL", |
| "STRING" |
| }; |
| |
| |
| if ((coption = ppdFindCustomOption(ppd, option->keyword)) == NULL || |
| cupsArrayCount(coption->params) == 0) |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom", choice->marked ? "*" : ""); |
| else if (!_cups_strcasecmp(option->keyword, "PageSize") || |
| !_cups_strcasecmp(option->keyword, "PageRegion")) |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.WIDTHxHEIGHT", choice->marked ? "*" : ""); |
| else |
| { |
| cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); |
| |
| if (cupsArrayCount(coption->params) == 1) |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.%s", choice->marked ? "*" : "", types[cparam->type]); |
| else |
| { |
| const char *prefix; /* Prefix string */ |
| |
| |
| if (choice->marked) |
| prefix = " *{"; |
| else |
| prefix = " {"; |
| |
| while (cparam) |
| { |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s%s=%s", prefix, cparam->name, types[cparam->type]); |
| cparam = (ppd_cparam_t *)cupsArrayNext(coption->params); |
| prefix = " "; |
| ptr += strlen(ptr); |
| } |
| |
| if (ptr < (buffer + sizeof(buffer) - 1)) |
| strlcpy(ptr, "}", sizeof(buffer) - (size_t)(ptr - buffer)); |
| } |
| } |
| } |
| else if (choice->marked) |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " *%s", choice->choice); |
| else |
| snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %s", choice->choice); |
| |
| ptr += strlen(ptr); |
| } |
| |
| _cupsLangPuts(stdout, buffer); |
| } |
| |
| for (i = group->num_subgroups, subgroup = group->subgroups; i > 0; i --, subgroup ++) |
| list_group(ppd, subgroup); |
| } |
| |
| |
| /* |
| * 'list_options()' - List printer-specific options from the PPD file. |
| */ |
| |
| static void |
| list_options(cups_dest_t *dest) /* I - Destination to list */ |
| { |
| http_t *http; /* Connection to destination */ |
| char resource[1024]; /* Resource path */ |
| int i; /* Looping var */ |
| const char *filename; /* PPD filename */ |
| ppd_file_t *ppd; /* PPD data */ |
| ppd_group_t *group; /* Current group */ |
| |
| |
| if ((http = cupsConnectDest(dest, CUPS_DEST_FLAGS_NONE, 30000, NULL, resource, sizeof(resource), NULL, NULL)) == NULL) |
| { |
| _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"), |
| dest->name, cupsLastErrorString()); |
| return; |
| } |
| |
| if ((filename = cupsGetPPD2(http, dest->name)) == NULL) |
| { |
| httpClose(http); |
| |
| _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"), |
| dest->name, cupsLastErrorString()); |
| return; |
| } |
| |
| httpClose(http); |
| |
| if ((ppd = ppdOpenFile(filename)) == NULL) |
| { |
| unlink(filename); |
| _cupsLangPrintf(stderr, _("lpoptions: Unable to open PPD file for %s."), |
| dest->name); |
| return; |
| } |
| |
| ppdMarkDefaults(ppd); |
| cupsMarkOptions(ppd, dest->num_options, dest->options); |
| |
| for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) |
| list_group(ppd, group); |
| |
| ppdClose(ppd); |
| unlink(filename); |
| } |
| |
| |
| /* |
| * 'usage()' - Show program usage and exit. |
| */ |
| |
| static void |
| usage(void) |
| { |
| _cupsLangPuts(stdout, _("Usage: lpoptions [options] -d destination\n" |
| " lpoptions [options] [-p destination] [-l]\n" |
| " lpoptions [options] [-p destination] -o option[=value]\n" |
| " lpoptions [options] -x destination")); |
| _cupsLangPuts(stdout, _("Options:")); |
| _cupsLangPuts(stdout, _("-d destination Set default destination")); |
| _cupsLangPuts(stdout, _("-E Encrypt the connection to the server")); |
| _cupsLangPuts(stdout, _("-h server[:port] Connect to the named server and port")); |
| _cupsLangPuts(stdout, _("-l Show supported options and values")); |
| _cupsLangPuts(stdout, _("-o name[=value] Set default option and value")); |
| _cupsLangPuts(stdout, _("-p destination Specify a destination")); |
| _cupsLangPuts(stdout, _("-U username Specify the username to use for authentication")); |
| _cupsLangPuts(stdout, _("-x destination Remove default options for destination")); |
| |
| exit(1); |
| } |