1/* 2 * "$Id: lpoptions.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * Printer option program for CUPS. 5 * 6 * Copyright 2007-2011 by Apple Inc. 7 * Copyright 1997-2006 by Easy Software Products. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * Contents: 16 * 17 * main() - Main entry. 18 * list_group() - List printer-specific options from the PPD group. 19 * list_options() - List printer-specific options from the PPD file. 20 * usage() - Show program usage and exit. 21 */ 22 23/* 24 * Include necessary headers... 25 */ 26 27#include <cups/cups-private.h> 28 29 30/* 31 * Local functions... 32 */ 33 34static void list_group(ppd_file_t *ppd, ppd_group_t *group); 35static void list_options(cups_dest_t *dest); 36static void usage(void) __attribute__((noreturn)); 37 38 39/* 40 * 'main()' - Main entry. 41 */ 42 43int /* O - Exit status */ 44main(int argc, /* I - Number of command-line arguments */ 45 char *argv[]) /* I - Command-line arguments */ 46{ 47 int i, j; /* Looping vars */ 48 int changes; /* Did we make changes? */ 49 int num_options; /* Number of options */ 50 cups_option_t *options; /* Options */ 51 int num_dests; /* Number of destinations */ 52 cups_dest_t *dests; /* Destinations */ 53 cups_dest_t *dest; /* Current destination */ 54 char *printer, /* Printer name */ 55 *instance, /* Instance name */ 56 *option; /* Current option */ 57 58 59 _cupsSetLocale(argv); 60 61 /* 62 * Loop through the command-line arguments... 63 */ 64 65 dest = NULL; 66 num_dests = 0; 67 dests = NULL; 68 num_options = 0; 69 options = NULL; 70 changes = 0; 71 72 for (i = 1; i < argc; i ++) 73 if (argv[i][0] == '-') 74 { 75 switch (argv[i][1]) 76 { 77 case 'd' : /* -d printer */ 78 if (argv[i][2]) 79 printer = argv[i] + 2; 80 else 81 { 82 i ++; 83 if (i >= argc) 84 usage(); 85 86 printer = argv[i]; 87 } 88 89 if ((instance = strrchr(printer, '/')) != NULL) 90 *instance++ = '\0'; 91 92 if (num_dests == 0) 93 num_dests = cupsGetDests(&dests); 94 95 if (num_dests == 0 || !dests || 96 (dest = cupsGetDest(printer, instance, num_dests, 97 dests)) == NULL) 98 { 99 _cupsLangPuts(stderr, _("lpoptions: Unknown printer or class.")); 100 return (1); 101 } 102 103 /* 104 * Set the default destination... 105 */ 106 107 for (j = 0; j < num_dests; j ++) 108 dests[j].is_default = 0; 109 110 dest->is_default = 1; 111 112 cupsSetDests(num_dests, dests); 113 114 for (j = 0; j < dest->num_options; j ++) 115 if (cupsGetOption(dest->options[j].name, num_options, 116 options) == NULL) 117 num_options = cupsAddOption(dest->options[j].name, 118 dest->options[j].value, 119 num_options, &options); 120 break; 121 122 case 'h' : /* -h server */ 123 if (argv[i][2]) 124 cupsSetServer(argv[i] + 2); 125 else 126 { 127 i ++; 128 if (i >= argc) 129 usage(); 130 131 cupsSetServer(argv[i]); 132 } 133 break; 134 135 case 'E' : /* Encrypt connection */ 136 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED); 137 break; 138 139 case 'l' : /* -l (list options) */ 140 if (dest == NULL) 141 { 142 if (num_dests == 0) 143 num_dests = cupsGetDests(&dests); 144 145 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) 146 dest = dests; 147 } 148 149 if (dest == NULL) 150 _cupsLangPuts(stderr, _("lpoptions: No printers.")); 151 else 152 list_options(dest); 153 154 changes = -1; 155 break; 156 157 case 'o' : /* -o option[=value] */ 158 if (dest == NULL) 159 { 160 if (num_dests == 0) 161 num_dests = cupsGetDests(&dests); 162 163 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) 164 dest = dests; 165 166 if (dest == NULL) 167 { 168 _cupsLangPuts(stderr, _("lpoptions: No printers.")); 169 return (1); 170 } 171 172 for (j = 0; j < dest->num_options; j ++) 173 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) 174 num_options = cupsAddOption(dest->options[j].name, 175 dest->options[j].value, 176 num_options, &options); 177 } 178 179 if (argv[i][2]) 180 num_options = cupsParseOptions(argv[i] + 2, num_options, &options); 181 else 182 { 183 i ++; 184 if (i >= argc) 185 usage(); 186 187 num_options = cupsParseOptions(argv[i], num_options, &options); 188 } 189 190 changes = 1; 191 break; 192 193 case 'p' : /* -p printer */ 194 if (argv[i][2]) 195 printer = argv[i] + 2; 196 else 197 { 198 i ++; 199 if (i >= argc) 200 usage(); 201 202 printer = argv[i]; 203 } 204 205 if ((instance = strrchr(printer, '/')) != NULL) 206 *instance++ = '\0'; 207 208 if (num_dests == 0) 209 num_dests = cupsGetDests(&dests); 210 211 if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL) 212 { 213 num_dests = cupsAddDest(printer, instance, num_dests, &dests); 214 dest = cupsGetDest(printer, instance, num_dests, dests); 215 216 if (dest == NULL) 217 { 218 _cupsLangPrintf(stderr, 219 _("lpoptions: Unable to add printer or " 220 "instance: %s"), 221 strerror(errno)); 222 return (1); 223 } 224 } 225 226 for (j = 0; j < dest->num_options; j ++) 227 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) 228 num_options = cupsAddOption(dest->options[j].name, 229 dest->options[j].value, 230 num_options, &options); 231 break; 232 233 case 'r' : /* -r option (remove) */ 234 if (dest == NULL) 235 { 236 if (num_dests == 0) 237 num_dests = cupsGetDests(&dests); 238 239 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) 240 dest = dests; 241 242 if (dest == NULL) 243 { 244 _cupsLangPuts(stderr, _("lpoptions: No printers.")); 245 return (1); 246 } 247 248 for (j = 0; j < dest->num_options; j ++) 249 if (cupsGetOption(dest->options[j].name, num_options, 250 options) == NULL) 251 num_options = cupsAddOption(dest->options[j].name, 252 dest->options[j].value, 253 num_options, &options); 254 } 255 256 if (argv[i][2]) 257 option = argv[i] + 2; 258 else 259 { 260 i ++; 261 if (i >= argc) 262 usage(); 263 264 option = argv[i]; 265 } 266 267 for (j = 0; j < num_options; j ++) 268 if (!_cups_strcasecmp(options[j].name, option)) 269 { 270 /* 271 * Remove this option... 272 */ 273 274 num_options --; 275 276 if (j < num_options) 277 memcpy(options + j, options + j + 1, 278 sizeof(cups_option_t) * (num_options - j)); 279 break; 280 } 281 282 changes = 1; 283 break; 284 285 case 'x' : /* -x printer */ 286 if (argv[i][2]) 287 printer = argv[i] + 2; 288 else 289 { 290 i ++; 291 if (i >= argc) 292 usage(); 293 294 printer = argv[i]; 295 } 296 297 if ((instance = strrchr(printer, '/')) != NULL) 298 *instance++ = '\0'; 299 300 if (num_dests == 0) 301 num_dests = cupsGetDests(&dests); 302 303 if ((dest = cupsGetDest(printer, instance, num_dests, 304 dests)) != NULL) 305 { 306 cupsFreeOptions(dest->num_options, dest->options); 307 308 /* 309 * If we are "deleting" the default printer, then just set the 310 * number of options to 0; if it is also the system default 311 * then cupsSetDests() will remove it for us... 312 */ 313 314 if (dest->is_default) 315 { 316 dest->num_options = 0; 317 dest->options = NULL; 318 } 319 else 320 { 321 num_dests --; 322 323 j = dest - dests; 324 if (j < num_dests) 325 memcpy(dest, dest + 1, (num_dests - j) * sizeof(cups_dest_t)); 326 } 327 } 328 329 cupsSetDests(num_dests, dests); 330 dest = NULL; 331 changes = -1; 332 break; 333 334 default : 335 usage(); 336 } 337 } 338 else 339 usage(); 340 341 if (num_dests == 0) 342 num_dests = cupsGetDests(&dests); 343 344 if (dest == NULL) 345 { 346 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL) 347 { 348 for (j = 0; j < dest->num_options; j ++) 349 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) 350 num_options = cupsAddOption(dest->options[j].name, 351 dest->options[j].value, 352 num_options, &options); 353 } 354 } 355 356 if (dest == NULL) 357 return (0); 358 359 if (changes > 0) 360 { 361 /* 362 * Set printer options... 363 */ 364 365 cupsFreeOptions(dest->num_options, dest->options); 366 367 dest->num_options = num_options; 368 dest->options = options; 369 370 cupsSetDests(num_dests, dests); 371 } 372 else if (changes == 0) 373 { 374 char buffer[10240], /* String for options */ 375 *ptr; /* Pointer into string */ 376 377 num_options = dest->num_options; 378 options = dest->options; 379 380 for (i = 0, ptr = buffer; 381 ptr < (buffer + sizeof(buffer) - 1) && i < num_options; 382 i ++) 383 { 384 if (i) 385 *ptr++ = ' '; 386 387 if (!options[i].value[0]) 388 strlcpy(ptr, options[i].name, sizeof(buffer) - (ptr - buffer)); 389 else if (strchr(options[i].value, ' ') != NULL || 390 strchr(options[i].value, '\t') != NULL) 391 snprintf(ptr, sizeof(buffer) - (ptr - buffer), "%s=\'%s\'", 392 options[i].name, options[i].value); 393 else 394 snprintf(ptr, sizeof(buffer) - (ptr - buffer), "%s=%s", 395 options[i].name, options[i].value); 396 397 ptr += strlen(ptr); 398 } 399 400 _cupsLangPuts(stdout, buffer); 401 } 402 403 return (0); 404} 405 406/* 407 * 'list_group()' - List printer-specific options from the PPD group. 408 */ 409 410static void 411list_group(ppd_file_t *ppd, /* I - PPD file */ 412 ppd_group_t *group) /* I - Group to show */ 413{ 414 int i, j; /* Looping vars */ 415 ppd_option_t *option; /* Current option */ 416 ppd_choice_t *choice; /* Current choice */ 417 ppd_group_t *subgroup; /* Current subgroup */ 418 char buffer[10240], /* Option string buffer */ 419 *ptr; /* Pointer into option string */ 420 421 422 for (i = group->num_options, option = group->options; i > 0; i --, option ++) 423 { 424 if (!_cups_strcasecmp(option->keyword, "PageRegion")) 425 continue; 426 427 snprintf(buffer, sizeof(buffer), "%s/%s:", option->keyword, option->text); 428 429 for (j = option->num_choices, choice = option->choices, 430 ptr = buffer + strlen(buffer); 431 j > 0 && ptr < (buffer + sizeof(buffer) - 1); 432 j --, choice ++) 433 { 434 if (!_cups_strcasecmp(choice->choice, "Custom")) 435 { 436 ppd_coption_t *coption; /* Custom option */ 437 ppd_cparam_t *cparam; /* Custom parameter */ 438 static const char * const types[] = 439 { /* Parameter types */ 440 "CURVE", 441 "INTEGER", 442 "INVCURVE", 443 "PASSCODE", 444 "PASSWORD", 445 "POINTS", 446 "REAL", 447 "STRING" 448 }; 449 450 451 if ((coption = ppdFindCustomOption(ppd, option->keyword)) == NULL || 452 cupsArrayCount(coption->params) == 0) 453 snprintf(ptr, sizeof(buffer) - (ptr - buffer), " %sCustom", 454 choice->marked ? "*" : ""); 455 else if (!_cups_strcasecmp(option->keyword, "PageSize") || 456 !_cups_strcasecmp(option->keyword, "PageRegion")) 457 snprintf(ptr, sizeof(buffer) - (ptr - buffer), 458 " %sCustom.WIDTHxHEIGHT", choice->marked ? "*" : ""); 459 else 460 { 461 cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); 462 463 if (cupsArrayCount(coption->params) == 1) 464 snprintf(ptr, sizeof(buffer) - (ptr - buffer), " %sCustom.%s", 465 choice->marked ? "*" : "", types[cparam->type]); 466 else 467 { 468 const char *prefix; /* Prefix string */ 469 470 471 if (choice->marked) 472 prefix = " *{"; 473 else 474 prefix = " {"; 475 476 while (cparam) 477 { 478 snprintf(ptr, sizeof(buffer) - (ptr - buffer), "%s%s=%s", prefix, 479 cparam->name, types[cparam->type]); 480 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params); 481 prefix = " "; 482 ptr += strlen(ptr); 483 } 484 485 if (ptr < (buffer + sizeof(buffer) - 1)) 486 strlcpy(ptr, "}", sizeof(buffer) - (ptr - buffer)); 487 } 488 } 489 } 490 else if (choice->marked) 491 snprintf(ptr, sizeof(buffer) - (ptr - buffer), " *%s", choice->choice); 492 else 493 snprintf(ptr, sizeof(buffer) - (ptr - buffer), " %s", choice->choice); 494 495 ptr += strlen(ptr); 496 } 497 498 _cupsLangPuts(stdout, buffer); 499 } 500 501 for (i = group->num_subgroups, subgroup = group->subgroups; i > 0; i --, subgroup ++) 502 list_group(ppd, subgroup); 503} 504 505 506/* 507 * 'list_options()' - List printer-specific options from the PPD file. 508 */ 509 510static void 511list_options(cups_dest_t *dest) /* I - Destination to list */ 512{ 513 int i; /* Looping var */ 514 const char *filename; /* PPD filename */ 515 ppd_file_t *ppd; /* PPD data */ 516 ppd_group_t *group; /* Current group */ 517 518 519 if ((filename = cupsGetPPD(dest->name)) == NULL) 520 { 521 _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"), 522 dest->name, cupsLastErrorString()); 523 return; 524 } 525 526 if ((ppd = ppdOpenFile(filename)) == NULL) 527 { 528 unlink(filename); 529 _cupsLangPrintf(stderr, _("lpoptions: Unable to open PPD file for %s."), 530 dest->name); 531 return; 532 } 533 534 ppdMarkDefaults(ppd); 535 cupsMarkOptions(ppd, dest->num_options, dest->options); 536 537 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) 538 list_group(ppd, group); 539 540 ppdClose(ppd); 541 unlink(filename); 542} 543 544 545/* 546 * 'usage()' - Show program usage and exit. 547 */ 548 549static void 550usage(void) 551{ 552 _cupsLangPuts(stdout, 553 _("Usage: lpoptions [-h server] [-E] -d printer\n" 554 " lpoptions [-h server] [-E] [-p printer] -l\n" 555 " lpoptions [-h server] [-E] -p printer -o " 556 "option[=value] ...\n" 557 " lpoptions [-h server] [-E] -x printer")); 558 559 exit(1); 560} 561 562 563/* 564 * End of "$Id: lpoptions.c 11093 2013-07-03 20:48:42Z msweet $". 565 */ 566