1/*---------------------------------------------------------------------------* 2 | PDFlib - A library for generating PDF on the fly | 3 +---------------------------------------------------------------------------+ 4 | Copyright (c) 1997-2004 Thomas Merz and PDFlib GmbH. All rights reserved. | 5 +---------------------------------------------------------------------------+ 6 | | 7 | This software is subject to the PDFlib license. It is NOT in the | 8 | public domain. Extended versions and commercial licenses are | 9 | available, please check http://www.pdflib.com. | 10 | | 11 *---------------------------------------------------------------------------*/ 12 13/* $Id: pc_optparse.c 14574 2005-10-29 16:27:43Z bonefish $ 14 * 15 * Parser options routines 16 * 17 */ 18 19#include "pc_util.h" 20#include "pc_optparse.h" 21 22#define PDC_OPT_LISTSEPS "\f\n\r\t\v =" 23 24/* result of an option */ 25struct pdc_resopt_s 26{ 27 int numdef; /* number of definitions */ 28 const pdc_defopt *defopt; /* pointer to option definition */ 29 int num; /* number of parsed values */ 30 void *val; /* list of parsed values */ 31}; 32 33/* sizes of option types. must be parallel to pdc_opttype */ 34static const size_t pdc_typesizes[] = 35{ 36 sizeof (pdc_bool), 37 sizeof (char *), 38 sizeof (int), 39 sizeof (int), 40 sizeof (float), 41 sizeof (double), 42 sizeof (int), 43 sizeof (int), 44 sizeof (int), 45 sizeof (int), 46 sizeof (int), 47 sizeof (int), 48 sizeof (int), 49 sizeof (int), 50 sizeof (int), 51}; 52 53static const pdc_keyconn pdc_handletypes[] = 54{ 55 {"color", pdc_colorhandle}, 56 {"document", pdc_documenthandle}, 57 {"font", pdc_fonthandle}, 58 {"gstate", pdc_gstatehandle}, 59 {"iccprofile", pdc_iccprofilehandle}, 60 {"image", pdc_imagehandle}, 61 {"page", pdc_pagehandle}, 62 {"pattern", pdc_patternhandle}, 63 {"shading", pdc_shadinghandle}, 64}; 65 66int 67pdc_get_keycode(const char *keyword, const pdc_keyconn *keyconn) 68{ 69 int i; 70 for (i = 0; keyconn[i].word != 0; i++) 71 { 72 if (!strcmp(keyword, keyconn[i].word)) 73 return keyconn[i].code; 74 } 75 return PDC_KEY_NOTFOUND; 76} 77 78int 79pdc_get_keycode_ci(const char *keyword, const pdc_keyconn *keyconn) 80{ 81 int i; 82 for (i = 0; keyconn[i].word != 0; i++) 83 { 84 if (!pdc_stricmp(keyword, keyconn[i].word)) 85 return keyconn[i].code; 86 } 87 return PDC_KEY_NOTFOUND; 88} 89 90const char * 91pdc_get_keyword(int keycode, const pdc_keyconn *keyconn) 92{ 93 int i; 94 for (i = 0; keyconn[i].word != 0; i++) 95 { 96 if (keycode == keyconn[i].code) 97 return keyconn[i].word; 98 } 99 return NULL; 100} 101 102const char * 103pdc_get_int_keyword(char *keyword, const pdc_keyconn *keyconn) 104{ 105 int i; 106 for (i = 0; keyconn[i].word != 0; i++) 107 { 108 if (!pdc_stricmp(keyword, keyconn[i].word)) 109 return keyconn[i].word; 110 } 111 return NULL; 112} 113 114void 115pdc_cleanup_optstringlist(pdc_core *pdc, char **stringlist, int ns) 116{ 117 int j; 118 119 for (j = 0; j < ns; j++) 120 { 121 if (stringlist[j]) 122 pdc_free(pdc, stringlist[j]); 123 } 124 pdc_free(pdc, stringlist); 125} 126 127static void 128pdc_delete_optvalue(pdc_core *pdc, pdc_resopt *resopt) 129{ 130 if (resopt->val) 131 { 132 if (resopt->defopt->type == pdc_stringlist) 133 { 134 int j; 135 char **s = (char **) resopt->val; 136 for (j = 0; j < resopt->num; j++) 137 if (s[j]) 138 pdc_free(pdc, s[j]); 139 } 140 pdc_free(pdc, resopt->val); 141 resopt->val = NULL; 142 } 143 resopt->num = 0; 144} 145 146static int 147pdc_optname_compare(const void *a, const void *b) 148{ 149 return (strcmp(((pdc_resopt *)a)->defopt->name, 150 ((pdc_resopt *)b)->defopt->name)); 151} 152 153pdc_resopt * 154pdc_parse_optionlist(pdc_core *pdc, const char *optlist, 155 const pdc_defopt *defopt, 156 const pdc_clientdata *clientdata, pdc_bool verbose) 157{ 158 static const char *fn = "pdc_parse_optionlist"; 159 const char *stemp1 = NULL, *stemp2 = NULL, *stemp3 = NULL; 160 char **items = NULL, *keyword = NULL; 161 char **values = NULL, *value = NULL, **strings = NULL; 162 int i, nd, is, iss, it, iv, numdef, nitems = 0, nvalues, errcode = 0; 163 void *resval; 164 double dz, maxval; 165 int iz; 166 size_t len; 167 const pdc_defopt *dopt = NULL; 168 pdc_resopt *resopt = NULL; 169 pdc_bool ignore = pdc_false; 170 pdc_bool boolval = pdc_false; 171 pdc_bool tocheck = pdc_false; 172 pdc_bool issorted = pdc_true; 173 pdc_bool ishandle = pdc_true; 174 pdc_bool hastobepos; 175 176 /* decrement handles */ 177 hastobepos = clientdata && clientdata->hastobepos ? pdc_true : pdc_false; 178 179 /* split option list */ 180 if (optlist) 181 nitems = pdc_split_stringlist(pdc, optlist, PDC_OPT_LISTSEPS, &items); 182 if (nitems < 0) 183 { 184 keyword = (char *) optlist; 185 errcode = PDC_E_OPT_NOTBAL; 186 goto PDC_OPT_SYNTAXERROR; 187 } 188 189 /* initialize result list */ 190 for (numdef = 0; defopt[numdef].name != NULL; numdef++) { 191 /* */ ; 192 } 193 resopt = (pdc_resopt *) pdc_calloc(pdc, numdef * sizeof(pdc_resopt), fn); 194 for (i = 0; i < numdef; i++) 195 { 196 resopt[i].numdef = numdef; 197 resopt[i].defopt = &defopt[i]; 198 199 if (defopt[i].flags & PDC_OPT_IGNOREIF1 || 200 defopt[i].flags & PDC_OPT_IGNOREIF2 || 201 defopt[i].flags & PDC_OPT_REQUIRIF1 || 202 defopt[i].flags & PDC_OPT_REQUIRIF2 || 203 defopt[i].flags & PDC_OPT_REQUIRED) 204 tocheck = pdc_true; 205 206 if (i && issorted) 207 issorted = (strcmp(defopt[i-1].name, defopt[i].name) <= 0) ? 208 pdc_true : pdc_false; 209 } 210 211 /* loop over all option list elements */ 212 for (is = 0; is < nitems; is++) 213 { 214 /* search keyword */ 215 boolval = pdc_undef; 216 keyword = items[is]; 217 for (it = 0; it < numdef; it++) 218 { 219 /* special handling for booleans */ 220 if (defopt[it].type == pdc_booleanlist) 221 { 222 if (!strcmp(keyword, defopt[it].name) || 223 (keyword[1] != 0 && !strcmp(&keyword[2], defopt[it].name))) 224 { 225 iss = is + 1; 226 if (iss == nitems || 227 (strcmp(items[iss], "true") && 228 strcmp(items[iss], "false"))) 229 { 230 if (!strncmp(keyword, "no", 2)) 231 { 232 boolval = pdc_false; 233 break; 234 } 235 else 236 { 237 boolval = pdc_true; 238 break; 239 } 240 } 241 } 242 } 243 244 if (!strcmp(keyword, defopt[it].name)) break; 245 } 246 if (it == numdef) 247 { 248 errcode = PDC_E_OPT_UNKNOWNKEY; 249 goto PDC_OPT_SYNTAXERROR; 250 } 251 252 /* initialize */ 253 dopt = &defopt[it]; 254 ignore = pdc_false; 255 nvalues = 1; 256 values = NULL; 257 ishandle = pdc_true; 258 259 /* compatibility */ 260 if (clientdata && clientdata->compatibility) 261 { 262 int compatibility = clientdata->compatibility; 263 264 for (iv = PDC_1_3; iv < PDC_X_X_LAST; iv++) 265 { 266 if ((dopt->flags & (1L<<iv)) && compatibility < iv) 267 { 268 errcode = PDC_E_OPT_VERSION; 269 stemp2 = pdc_errprintf(pdc, "%d.%d", 270 compatibility / 10, compatibility % 10); 271 goto PDC_OPT_SYNTAXERROR; 272 } 273 } 274 } 275 276 /* not supported */ 277 if (dopt->flags & PDC_OPT_UNSUPP) 278 { 279 ignore = pdc_true; 280 if (verbose) 281 pdc_warning(pdc, PDC_E_OPT_UNSUPP, dopt->name, 0, 0, 0); 282 } 283 284 /* parse values */ 285 if (boolval == pdc_undef) 286 { 287 is++; 288 if (is == nitems) 289 { 290 errcode = PDC_E_OPT_NOVALUES; 291 goto PDC_OPT_SYNTAXERROR; 292 } 293 if (!ignore && 294 (dopt->type != pdc_stringlist || dopt->maxnum > 1)) 295 nvalues = pdc_split_stringlist(pdc, items[is], NULL, &values); 296 } 297 298 /* ignore */ 299 if (ignore) continue; 300 301 /* number of values check */ 302 if (nvalues < dopt->minnum) 303 { 304 stemp2 = pdc_errprintf(pdc, "%d", dopt->minnum); 305 errcode = PDC_E_OPT_TOOFEWVALUES; 306 goto PDC_OPT_SYNTAXERROR; 307 } 308 else if (nvalues > dopt->maxnum) 309 { 310 stemp2 = pdc_errprintf(pdc, "%d", dopt->maxnum); 311 errcode = PDC_E_OPT_TOOMANYVALUES; 312 goto PDC_OPT_SYNTAXERROR; 313 } 314 315 /* option already exists */ 316 if (resopt[it].num) 317 { 318 pdc_delete_optvalue(pdc, &resopt[it]); 319 } 320 321 /* no values */ 322 if (!nvalues ) continue; 323 324 /* maximal value */ 325 switch (dopt->type) 326 { 327 case pdc_colorhandle: 328 maxval = clientdata->maxcolor; 329 break; 330 331 case pdc_documenthandle: 332 maxval = clientdata->maxdocument; 333 break; 334 335 case pdc_fonthandle: 336 maxval = clientdata->maxfont; 337 break; 338 339 case pdc_iccprofilehandle: 340 maxval = clientdata->maxiccprofile; 341 break; 342 343 case pdc_imagehandle: 344 maxval = clientdata->maximage; 345 break; 346 347 case pdc_pagehandle: 348 maxval = clientdata->maxpage; 349 break; 350 351 case pdc_patternhandle: 352 maxval = clientdata->maxpattern; 353 break; 354 355 case pdc_shadinghandle: 356 maxval = clientdata->maxshading; 357 break; 358 359 case pdc_gstatehandle: 360 maxval = clientdata->maxgstate; 361 break; 362 363 default: 364 maxval = dopt->maxval; 365 ishandle = pdc_false; 366 break; 367 } 368 369 /* allocate value array */ 370 resopt[it].val = pdc_calloc(pdc, 371 (size_t) (nvalues * pdc_typesizes[dopt->type]), fn); 372 resopt[it].num = nvalues; 373 374 /* analyze type */ 375 resval = resopt[it].val; 376 for (iv = 0; iv < nvalues; iv++) 377 { 378 errcode = 0; 379 if (dopt->maxnum > 1 && nvalues) 380 value = values[iv]; 381 else 382 value = items[is]; 383 switch (dopt->type) 384 { 385 /* boolean list */ 386 case pdc_booleanlist: 387 if (boolval == pdc_true || !strcmp(value, "true")) 388 { 389 *(pdc_bool *) resval = pdc_true; 390 } 391 else if (boolval == pdc_false || !strcmp(value, "false")) 392 { 393 *(pdc_bool *) resval = pdc_false; 394 } 395 else 396 { 397 errcode = PDC_E_OPT_ILLBOOLEAN; 398 } 399 break; 400 401 /* string list */ 402 case pdc_stringlist: 403 if (dopt->flags & PDC_OPT_NOSPACES) 404 { 405 if (pdc_split_stringlist(pdc, value, NULL, &strings) > 1) 406 errcode = PDC_E_OPT_ILLSPACES; 407 pdc_cleanup_stringlist(pdc, strings); 408 } 409 if (!errcode) 410 { 411 len = strlen(value); 412 dz = (double) len; 413 if (dz < dopt->minval) 414 { 415 stemp3 = pdc_errprintf(pdc, "%d", (int) dopt->minval); 416 errcode = PDC_E_OPT_TOOSHORTSTR; 417 } 418 else if (dz > maxval) 419 { 420 stemp3 = pdc_errprintf(pdc, "%d", (int) maxval); 421 errcode = PDC_E_OPT_TOOLONGSTR; 422 } 423 *((char **) resval) = pdc_strdup(pdc, value); 424 } 425 break; 426 427 /* keyword list */ 428 case pdc_keywordlist: 429 iz = pdc_get_keycode(value, dopt->keylist); 430 if (iz == PDC_KEY_NOTFOUND) 431 { 432 errcode = PDC_E_OPT_ILLKEYWORD; 433 } 434 else 435 { 436 *(int *) resval = iz; 437 } 438 break; 439 440 /* number list */ 441 case pdc_integerlist: 442 case pdc_floatlist: 443 case pdc_doublelist: 444 if (dopt->keylist) 445 { 446 /* optional keyword and/or allowed integer list */ 447 iz = pdc_get_keycode(value, dopt->keylist); 448 if (iz == PDC_KEY_NOTFOUND) 449 { 450 if (dopt->flags & PDC_OPT_INTLIST) 451 { 452 errcode = PDC_E_OPT_ILLINTEGER; 453 break; 454 } 455 } 456 else 457 { 458 switch (dopt->type) 459 { 460 default: 461 case pdc_integerlist: 462 *(int *) resval = iz; 463 break; 464 465 case pdc_floatlist: 466 *(float *) resval = (float) iz; 467 break; 468 469 case pdc_doublelist: 470 *(double *) resval = (double) iz; 471 break; 472 } 473 break; 474 } 475 } 476 case pdc_colorhandle: 477 case pdc_documenthandle: 478 case pdc_fonthandle: 479 case pdc_iccprofilehandle: 480 case pdc_imagehandle: 481 case pdc_pagehandle: 482 case pdc_patternhandle: 483 case pdc_shadinghandle: 484 case pdc_gstatehandle: 485 if (pdc_str2double(value, &dz) == pdc_false) 486 { 487 errcode = PDC_E_OPT_ILLNUMBER; 488 } 489 else 490 { 491 if (ishandle && hastobepos) dz -= 1; 492 if (dz < dopt->minval) 493 { 494 if (ishandle) 495 { 496 stemp3 = pdc_get_keyword(dopt->type, 497 pdc_handletypes); 498 errcode = PDC_E_OPT_ILLHANDLE; 499 } 500 else 501 { 502 stemp3 = pdc_errprintf(pdc, "%g", dopt->minval); 503 errcode = PDC_E_OPT_TOOSMALLVAL; 504 } 505 } 506 else if (dz > maxval) 507 { 508 if (ishandle) 509 { 510 stemp3 = pdc_get_keyword(dopt->type, 511 pdc_handletypes); 512 errcode = PDC_E_OPT_ILLHANDLE; 513 } 514 else 515 { 516 stemp3 = pdc_errprintf(pdc, "%g", maxval); 517 errcode = PDC_E_OPT_TOOBIGVAL; 518 } 519 } 520 else if (dopt->flags & PDC_OPT_NOZERO && 521 fabs(dz) < PDC_FLOAT_PREC) 522 { 523 errcode = PDC_E_OPT_ZEROVAL; 524 } 525 else if (dopt->type == pdc_doublelist) 526 { 527 *(double *) resval = dz; 528 } 529 else if (dopt->type == pdc_floatlist) 530 { 531 *(float *) resval = (float) dz; 532 } 533 else 534 { 535 iz = (int) dz; 536 if ((double) iz != dz) 537 { 538 errcode = PDC_E_OPT_ILLINTEGER; 539 } 540 else 541 { 542 *(int *) resval = iz; 543 } 544 } 545 } 546 break; 547 } 548 549 if (errcode) 550 { 551 stemp2 = pdc_errprintf(pdc, "%s", value); 552 goto PDC_OPT_SYNTAXERROR; 553 } 554 555 /* increment value pointer */ 556 resval = (void *) ((char *)(resval) + pdc_typesizes[dopt->type]); 557 } 558 pdc_cleanup_stringlist(pdc, values); 559 values = NULL; 560 561 /* build OR bit pattern */ 562 if (dopt->flags & PDC_OPT_BUILDOR && nvalues > 1) 563 { 564 int *bcode = (int *) resopt[it].val; 565 for (iv = 1; iv < nvalues; iv++) 566 { 567 bcode[0] |= bcode[iv]; 568 } 569 resopt[it].num = 1; 570 } 571 } 572 pdc_cleanup_stringlist(pdc, items); 573 items = NULL; 574 575 /* required and to be ignored options */ 576 for (is = 0; tocheck && is < numdef; is++) 577 { 578 /* to be ignored option */ 579 if (resopt[is].num) 580 { 581 nd = 0; 582 if (defopt[is].flags & PDC_OPT_IGNOREIF1) nd = 1; 583 if (defopt[is].flags & PDC_OPT_IGNOREIF2) nd = 2; 584 for (it = is - 1; it >= is - nd && it >= 0; it--) 585 { 586 if (resopt[it].num) 587 { 588 pdc_delete_optvalue(pdc, &resopt[is]); 589 if (verbose) 590 pdc_warning(pdc, PDC_E_OPT_IGNORE, defopt[is].name, 591 defopt[it].name, 0, 0); 592 } 593 } 594 } 595 596 /* required option */ 597 if (!resopt[is].num && 598 ((defopt[is].flags & PDC_OPT_REQUIRED) || 599 (defopt[is].flags & PDC_OPT_REQUIRIF1 && resopt[is-1].num) || 600 (defopt[is].flags & PDC_OPT_REQUIRIF2 && 601 (resopt[is-1].num || resopt[is-2].num)))) 602 { 603 keyword = (char *) defopt[is].name; 604 errcode = PDC_E_OPT_NOTFOUND; 605 goto PDC_OPT_SYNTAXERROR; 606 } 607 } 608 609 /* is no sorted */ 610 if (!issorted) 611 { 612 qsort((void *)resopt, (size_t) numdef, sizeof(pdc_resopt), 613 pdc_optname_compare); 614 } 615 616#undef PDC_OPTPARSE 617#ifdef PDC_OPTPARSE 618 printf("\n"); 619 for (is = 0; is < numdef; is++) 620 { 621 printf("[%02d] %s (number = %d, pointer = %p):\n", is, 622 resopt[is].defopt->name, resopt[is].num, resopt[is].val); 623 for (iv = 0; iv < resopt[is].num; iv++) 624 { 625 switch (resopt[is].defopt->type) 626 { 627 case pdc_booleanlist: 628 case pdc_keywordlist: 629 case pdc_integerlist: 630 case pdc_colorhandle: 631 case pdc_documenthandle: 632 case pdc_fonthandle: 633 case pdc_gstatehandle: 634 case pdc_iccprofilehandle: 635 case pdc_imagehandle: 636 case pdc_pagehandle: 637 case pdc_patternhandle: 638 case pdc_shadinghandle: 639 printf(" [%d]: %d\n",iv, *((int *) resopt[is].val + iv)); 640 break; 641 642 case pdc_stringlist: 643 printf(" [%d]: %s\n",iv, *((char **) resopt[is].val + iv)); 644 break; 645 646 case pdc_floatlist: 647 printf(" [%d]: %f\n",iv, *((float *) resopt[is].val + iv)); 648 break; 649 650 case pdc_doublelist: 651 printf(" [%d]: %f\n",iv, *((double *) resopt[is].val + iv)); 652 break; 653 } 654 } 655 } 656 printf("\n"); 657#endif 658 659 return resopt; 660 661 PDC_OPT_SYNTAXERROR: 662 stemp1 = pdc_errprintf(pdc, "%s", keyword); 663 pdc_cleanup_stringlist(pdc, items); 664 pdc_cleanup_stringlist(pdc, values); 665 pdc_cleanup_optionlist(pdc, resopt); 666 667 switch (errcode) 668 { 669 case PDC_E_OPT_UNKNOWNKEY: 670 case PDC_E_OPT_NOVALUES: 671 case PDC_E_OPT_NOTFOUND: 672 case PDC_E_OPT_NOTBAL: 673 pdc_set_errmsg(pdc, errcode, stemp1, 0, 0, 0); 674 break; 675 676 case PDC_E_OPT_TOOFEWVALUES: 677 case PDC_E_OPT_TOOMANYVALUES: 678 case PDC_E_OPT_ILLBOOLEAN: 679 case PDC_E_OPT_ILLKEYWORD: 680 case PDC_E_OPT_ILLINTEGER: 681 case PDC_E_OPT_ILLNUMBER: 682 case PDC_E_OPT_ZEROVAL: 683 case PDC_E_OPT_ILLSPACES: 684 case PDC_E_OPT_VERSION: 685 pdc_set_errmsg(pdc, errcode, stemp1, stemp2, 0, 0); 686 break; 687 688 case PDC_E_OPT_TOOSHORTSTR: 689 case PDC_E_OPT_TOOLONGSTR: 690 case PDC_E_OPT_TOOSMALLVAL: 691 case PDC_E_OPT_TOOBIGVAL: 692 case PDC_E_OPT_ILLHANDLE: 693 pdc_set_errmsg(pdc, errcode, stemp1, stemp2, stemp3, 0); 694 break; 695 } 696 697 if (verbose) 698 pdc_error(pdc, -1, 0, 0, 0, 0); 699 700 return NULL; 701} 702 703int 704pdc_get_optvalues(pdc_core *pdc, const char *keyword, pdc_resopt *resopt, 705 void *lvalues, void **mvalues) 706{ 707 pdc_resopt *ropt = NULL; 708 void *values = NULL; 709 int nvalues = 0; 710 size_t nbytes; 711 if (mvalues) *mvalues = NULL; 712 713 (void) pdc; 714 715 if (resopt) 716 { 717 int i, cmp; 718 int lo = 0; 719 int hi = resopt[0].numdef; 720 721 while (lo < hi) 722 { 723 i = (lo + hi) / 2; 724 cmp = strcmp(keyword, resopt[i].defopt->name); 725 726 /* keyword found */ 727 if (cmp == 0) 728 { 729 ropt = &resopt[i]; 730 nvalues = ropt->num; 731 values = ropt->val; 732 break; 733 } 734 735 if (cmp < 0) 736 hi = i; 737 else 738 lo = i + 1; 739 } 740 } 741 742 if (nvalues) 743 { 744 /* copy values */ 745 if (lvalues) 746 { 747 if (ropt->defopt->type == pdc_stringlist && 748 ropt->defopt->maxnum == 1) 749 { 750 strcpy((char *)lvalues, *((char **) values)); 751 } 752 else 753 { 754 nbytes = (size_t) (nvalues * pdc_typesizes[ropt->defopt->type]); 755 memcpy(lvalues, values, nbytes); 756 } 757 } 758 759 /* copy pointer */ 760 if (mvalues) 761 { 762 *mvalues = values; 763 ropt->val = NULL; 764 } 765 } 766 767 return nvalues; 768} 769 770void 771pdc_cleanup_optionlist(pdc_core *pdc, pdc_resopt *resopt) 772{ 773 if (resopt) 774 { 775 int i; 776 777 for (i = 0; i < resopt[0].numdef; i++) 778 pdc_delete_optvalue(pdc, &resopt[i]); 779 780 pdc_free(pdc, resopt); 781 } 782} 783 784const char * 785pdc_get_handletype(pdc_opttype type) 786{ 787 return pdc_get_keyword(type, pdc_handletypes); 788} 789 790