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