source: branches/r9/lang/tcl/src/RpSwitch.c @ 4870

Last change on this file since 4870 was 4870, checked in by gah, 6 years ago
File size: 11.8 KB
Line 
1
2/*
3 * RpSwitch.c --
4 *
5 * This module implements command/argument switch parsing procedures for the
6 * BLT toolkit.
7 *
8 *      Copyright 1991-2004 George A Howlett.
9 *
10 *      Permission is hereby granted, free of charge, to any person obtaining
11 *      a copy of this software and associated documentation files (the
12 *      "Software"), to deal in the Software without restriction, including
13 *      without limitation the rights to use, copy, modify, merge, publish,
14 *      distribute, sublicense, and/or sell copies of the Software, and to
15 *      permit persons to whom the Software is furnished to do so, subject to
16 *      the following conditions:
17 *
18 *      The above copyright notice and this permission notice shall be
19 *      included in all copies or substantial portions of the Software.
20 *
21 *      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 *      EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 *      MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 *      NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 *      LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 *      OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 *      WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30#include <assert.h>
31#include <stdarg.h>
32#include <string.h>
33#include <unistd.h>
34#include <stdlib.h>
35#include <tcl.h>
36#include "RpSwitch.h"
37
38static void
39DoHelp(Tcl_Interp *interp, SwitchSpec *specs)
40{
41    Tcl_DString ds;
42    SwitchSpec *sp;
43
44    Tcl_DStringInit(&ds);
45    Tcl_DStringAppend(&ds, "following switches are available:", -1);
46    for (sp = specs; sp->type != SWITCH_END; sp++) {
47        Tcl_DStringAppend(&ds, "\n    ", 4);
48        Tcl_DStringAppend(&ds, sp->switchName, -1);
49        Tcl_DStringAppend(&ds, " ", 1);
50        Tcl_DStringAppend(&ds, sp->help, -1);
51    }
52    Tcl_AppendResult(interp, Tcl_DStringValue(&ds), (char *)NULL);
53    Tcl_DStringFree(&ds);
54}
55
56/*
57 *---------------------------------------------------------------------------
58 *
59 * FindSwitchSpec --
60 *
61 *      Search through a table of configuration specs, looking for one that
62 *      matches a given argvName.
63 *
64 * Results:
65 *      The return value is a pointer to the matching entry, or NULL if
66 *      nothing matched.  In that case an error message is left in the
67 *      interp's result.
68 *
69 * Side effects:
70 *      None.
71 *
72 *---------------------------------------------------------------------------
73 */
74static SwitchSpec *
75FindSwitchSpec(
76    Tcl_Interp *interp,         /* Used for reporting errors. */
77    SwitchSpec *specs,          /* Pointer to table of configuration
78                                 * specifications for a widget. */
79    const char *name,           /* Name identifying a particular switch. */
80    int length,                 /* Length of name. */
81    int needFlags,              /* Flags that must be present in matching
82                                 * entry. */
83    int hateFlags)              /* Flags that must NOT be present in matching
84                                 * entry. */
85{
86    SwitchSpec *sp;
87    char c;                     /* First character of current argument. */
88    SwitchSpec *matchPtr;       /* Matching spec, or NULL. */
89
90    c = name[1];
91    matchPtr = NULL;
92    for (sp = specs; sp->type != SWITCH_END; sp++) {
93        if (sp->switchName == NULL) {
94            continue;
95        }
96        if (((sp->flags & needFlags) != needFlags) || (sp->flags & hateFlags)) {
97            continue;
98        }
99        if ((sp->switchName[1] != c) ||
100            (strncmp(sp->switchName, name, length) != 0)) {
101            continue;
102        }
103        if (sp->switchName[length] == '\0') {
104            return sp;          /* Stop on a perfect match. */
105        }
106        if (matchPtr != NULL) {
107            Tcl_AppendResult(interp, "ambiguous switch \"", name, "\"\n",
108                (char *) NULL);
109            DoHelp(interp, specs);
110            return NULL;
111        }
112        matchPtr = sp;
113    }
114    if (strcmp(name, "-help") == 0) {
115        DoHelp(interp, specs);
116        return NULL;
117    }
118    if (matchPtr == NULL) {
119        Tcl_AppendResult(interp, "unknown switch \"", name, "\"\n",
120                         (char *)NULL);
121        DoHelp(interp, specs);
122        return NULL;
123    }
124    return matchPtr;
125}
126
127/*
128 *---------------------------------------------------------------------------
129 *
130 * DoSwitch --
131 *
132 *      This procedure applies a single configuration switch to a widget
133 *      record.
134 *
135 * Results:
136 *      A standard Tcl return value.
137 *
138 * Side effects:
139 *      WidgRec is modified as indicated by specPtr and value.  The old value
140 *      is recycled, if that is appropriate for the value type.
141 *
142 *---------------------------------------------------------------------------
143 */
144static int
145DoSwitch(
146    Tcl_Interp *interp,         /* Interpreter for error reporting. */
147    SwitchSpec *sp,             /* Specifier to apply. */
148    Tcl_Obj *objPtr,            /* Value to use to fill in widgRec. */
149    void *record)               /* Record whose fields are to be modified.
150                                 * Values must be properly initialized. */
151{
152    do {
153        char *ptr;
154
155        ptr = (char *)record + sp->offset;
156        switch (sp->type) {
157        case SWITCH_BOOLEAN:
158            {
159                int value;
160
161                if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) {
162                    return TCL_ERROR;
163                }
164                if (sp->mask > 0) {
165                    if (value) {
166                        *((int *)ptr) |= sp->mask;
167                    } else {
168                        *((int *)ptr) &= ~sp->mask;
169                    }
170                } else {
171                    *((int *)ptr) = value;
172                }
173            }
174            break;
175
176        case SWITCH_DOUBLE:
177            if (Tcl_GetDoubleFromObj(interp, objPtr, (double *)ptr) != TCL_OK) {
178                return TCL_ERROR;
179            }
180            break;
181
182        case SWITCH_OBJ:
183            *(Tcl_Obj **)ptr = objPtr;
184            break;
185
186        case SWITCH_FLOAT:
187            {
188                double value;
189
190                if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
191                    return TCL_ERROR;
192                }
193                *(float *)ptr = (float)value;
194            }
195            break;
196
197        case SWITCH_INT:
198            if (Tcl_GetIntFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
199                return TCL_ERROR;
200            }
201            break;
202
203        case SWITCH_LIST:
204            {
205                int argc;
206
207                if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc,
208                                  (const char ***)ptr) != TCL_OK) {
209                    return TCL_ERROR;
210                }
211            }
212            break;
213
214        case SWITCH_LONG:
215            if (Tcl_GetLongFromObj(interp, objPtr, (long *)ptr) != TCL_OK) {
216                return TCL_ERROR;
217            }
218            break;
219
220        case SWITCH_STRING:
221            {
222                char *oldPtr, *newPtr, **strPtr;
223                char *string;
224
225                string = Tcl_GetString(objPtr);
226                strPtr = (char **)ptr;
227                newPtr = ((*string == '\0') && (sp->flags & SWITCH_NULL_OK))
228                    ? NULL : strdup(string);
229                oldPtr = *strPtr;
230                if (oldPtr != NULL) {
231                    free(oldPtr);
232                }
233                *strPtr = newPtr;
234            }
235            break;
236
237        case SWITCH_CUSTOM:
238            assert(sp->customPtr != NULL);
239            if ((*sp->customPtr->parseProc)(sp->customPtr->clientData, interp,
240                sp->switchName, objPtr, (char *)record, sp->offset, sp->flags)
241                != TCL_OK) {
242                return TCL_ERROR;
243            }
244            break;
245
246        default:
247            Tcl_AppendResult(interp, "bad switch table: unknown type",
248                             (char *)NULL);
249            return TCL_ERROR;
250        }
251        sp++;
252    } while ((sp->switchName == NULL) && (sp->type != SWITCH_END));
253    return TCL_OK;
254}
255
256/*
257 *---------------------------------------------------------------------------
258 *
259 * RpParseSwitches --
260 *
261 *      Process command-line switches to fill in fields of a record with
262 *      resources and other parameters.
263 *
264 * Results:
265 *      Returns the number of arguments comsumed by parsing the command line.
266 *      If an error occurred, -1 will be returned and an error messages can be
267 *      found as the interpreter result.
268 *
269 * Side effects:
270 *      The fields of widgRec get filled in with information from argc/argv.
271 *      Old information in widgRec's fields gets recycled.
272 *
273 *---------------------------------------------------------------------------
274 */
275int
276RpParseSwitches(
277    Tcl_Interp *interp,         /* Interpreter for error reporting. */
278    SwitchSpec *specs,          /* Describes legal switches. */
279    int objc,                   /* Number of elements in argv. */
280    Tcl_Obj *const *objv,       /* Command-line switches. */
281    void *record,               /* Record whose fields are to be modified.
282                                 * Values must be properly initialized. */
283    int flags)                  /* Used to specify additional flags that must
284                                 * be present in switch specs for them to be
285                                 * considered.  */
286{
287    SwitchSpec *sp;
288    int count;
289    int needFlags;              /* Specs must contain this set of flags or
290                                 * else they are not considered. */
291    int hateFlags;              /* If a spec contains any bits here, it's not
292                                 * considered. */
293
294    needFlags = flags & ~(SWITCH_USER_BIT - 1);
295    hateFlags = 0;
296
297    /*
298     * Pass 1:  Clear the change flags on all the specs so that we
299     *          can check it later.
300     */
301    for (sp = specs; sp->type != SWITCH_END; sp++) {
302        sp->flags &= ~SWITCH_SPECIFIED;
303    }
304    /*
305     * Pass 2:  Process the arguments that match entries in the specs.
306     *          It's an error if the argument doesn't match anything.
307     */
308    for (count = 0; count < objc; count++) {
309        char *arg;
310        int length;
311
312        arg = Tcl_GetStringFromObj(objv[count], &length);
313        if (flags & SWITCH_OBJV_PARTIAL) {
314            /*
315             * If the argument doesn't start with a '-' (not a switch) or is
316             * '--', stop processing and return the number of arguments
317             * comsumed.
318             */
319            if (arg[0] != '-') {
320                return count;
321            }
322            if ((arg[1] == '-') && (arg[2] == '\0')) {
323                return count + 1; /* include the "--" in the count. */
324            }
325        }
326        sp = FindSwitchSpec(interp, specs, arg, length, needFlags, hateFlags);
327        if (sp == NULL) {
328            return -1;
329        }
330        if (sp->type == SWITCH_BITMASK) {
331            char *ptr;
332
333            ptr = (char *)record + sp->offset;
334            *((int *)ptr) |= sp->mask;
335        } else if (sp->type == SWITCH_BITMASK_NEG) {
336            char *ptr;
337           
338            ptr = (char *)record + sp->offset;
339            *((int *)ptr) &= ~sp->mask;
340        } else if (sp->type == SWITCH_VALUE) {
341            char *ptr;
342           
343            ptr = (char *)record + sp->offset;
344            *((int *)ptr) = sp->mask;
345        } else {
346            count++;
347            if (count == objc) {
348                Tcl_AppendResult(interp, "value for \"", arg, "\" missing",
349                                 (char *) NULL);
350                return -1;
351            }
352            if (DoSwitch(interp, sp, objv[count], record) != TCL_OK) {
353                char msg[200];
354
355                sprintf(msg, "\n    (processing \"%.40s\" switch)",
356                        sp->switchName);
357                Tcl_AddErrorInfo(interp, msg);
358                return -1;
359            }
360        }
361        sp->flags |= SWITCH_SPECIFIED;
362    }
363    return count;
364}
365
366/*
367 *---------------------------------------------------------------------------
368 *
369 * RpFreeSwitches --
370 *
371 *      Free up all resources associated with switches.
372 *
373 * Results:
374 *      None.
375 *
376 *---------------------------------------------------------------------------
377 */
378/* ARGSUSED */
379void
380RpFreeSwitches(
381    SwitchSpec *specs,          /* Describes legal switches. */
382    void *record,               /* Record whose fields contain current values
383                                 * for switches. */
384    int needFlags)              /* Used to specify additional flags that must
385                                 * be present in config specs for them to be
386                                 * considered. */
387{
388    SwitchSpec *sp;
389
390    for (sp = specs; sp->type != SWITCH_END; sp++) {
391        if ((sp->flags & needFlags) == needFlags) {
392            char *ptr;
393
394            ptr = (char *)record + sp->offset;
395            switch (sp->type) {
396            case SWITCH_STRING:
397            case SWITCH_LIST:
398                if (*((char **) ptr) != NULL) {
399                    free(*((char **) ptr));
400                    *((char **) ptr) = NULL;
401                }
402                break;
403
404            case SWITCH_CUSTOM:
405                assert(sp->customPtr != NULL);
406                if ((*(char **)ptr != NULL) &&
407                    (sp->customPtr->freeProc != NULL)) {
408                    (*sp->customPtr->freeProc)((char *)record, sp->offset,
409                        sp->flags);
410                }
411                break;
412
413            default:
414                break;
415            }
416        }
417    }
418}
419
420/*
421 *---------------------------------------------------------------------------
422 *
423 * RpSwitchChanged --
424 *
425 *      Given the configuration specifications and one or more switch patterns
426 *      (terminated by a NULL), indicate if any of the matching switches has
427 *      been reset.
428 *
429 * Results:
430 *      Returns 1 if one of the switches have changed, 0 otherwise.
431 *
432 *---------------------------------------------------------------------------
433 */
434int
435RpSwitchChanged TCL_VARARGS_DEF(SwitchSpec *, arg1)
436{
437    va_list argList;
438    SwitchSpec *specs;
439    SwitchSpec *sp;
440    char *switchName;
441
442    specs = TCL_VARARGS_START(SwitchSpec *, arg1, argList);
443    while ((switchName = va_arg(argList, char *)) != NULL) {
444        for (sp = specs; sp->type != SWITCH_END; sp++) {
445            if ((Tcl_StringMatch(sp->switchName, switchName)) &&
446                (sp->flags & SWITCH_SPECIFIED)) {
447                va_end(argList);
448                return 1;
449            }
450        }
451    }
452    va_end(argList);
453    return 0;
454}
455
Note: See TracBrowser for help on using the repository browser.