/* * 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 #include /* * 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); }