source: nanovis/trunk/Switch.cpp @ 4827

Last change on this file since 4827 was 4056, checked in by ldelgass, 10 years ago

Use nv namespace for internal classes (not Rappture) to make it easier to find
dependencies on the Rappture libraries.

  • Property svn:eol-style set to native
File size: 12.7 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Switch.cpp --
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 <malloc.h>
35#include <tcl.h>
36#include "Switch.h"
37
38namespace nv {
39
40static void
41DoHelp(Tcl_Interp *interp, SwitchSpec *specs)
42{
43    Tcl_DString ds;
44    SwitchSpec *sp;
45
46    Tcl_DStringInit(&ds);
47    Tcl_DStringAppend(&ds, "following switches are available:", -1);
48    for (sp = specs; sp->type != SWITCH_END; sp++) {
49        Tcl_DStringAppend(&ds, "\n    ", 4);
50        Tcl_DStringAppend(&ds, sp->switchName, -1);
51        Tcl_DStringAppend(&ds, " ", 1);
52        Tcl_DStringAppend(&ds, sp->help, -1);
53    }
54    Tcl_AppendResult(interp, Tcl_DStringValue(&ds), (char *)NULL);
55    Tcl_DStringFree(&ds);
56}
57
58/*
59 *---------------------------------------------------------------------------
60 *
61 * FindSwitchSpec --
62 *
63 *      Search through a table of configuration specs, looking for one that
64 *      matches a given argvName.
65 *
66 * Results:
67 *      The return value is a pointer to the matching entry, or NULL if
68 *      nothing matched.  In that case an error message is left in the
69 *      interp's result.
70 *
71 * Side effects:
72 *      None.
73 *
74 *---------------------------------------------------------------------------
75 */
76static SwitchSpec *
77FindSwitchSpec(
78    Tcl_Interp *interp,         /* Used for reporting errors. */
79    SwitchSpec *specs,          /* Pointer to table of configuration
80                                 * specifications for a widget. */
81    const char *name,           /* Name identifying a particular switch. */
82    int length,                 /* Length of name. */
83    int needFlags,              /* Flags that must be present in matching
84                                 * entry. */
85    int hateFlags)              /* Flags that must NOT be present in matching
86                                 * entry. */
87{
88    SwitchSpec *sp;
89    char c;                     /* First character of current argument. */
90    SwitchSpec *matchPtr;       /* Matching spec, or NULL. */
91
92    c = name[1];
93    matchPtr = NULL;
94    for (sp = specs; sp->type != SWITCH_END; sp++) {
95        if (sp->switchName == NULL) {
96            continue;
97        }
98        if (((sp->flags & needFlags) != needFlags) || (sp->flags & hateFlags)) {
99            continue;
100        }
101        if ((sp->switchName[1] != c) ||
102            (strncmp(sp->switchName, name, length) != 0)) {
103            continue;
104        }
105        if (sp->switchName[length] == '\0') {
106            return sp;          /* Stop on a perfect match. */
107        }
108        if (matchPtr != NULL) {
109            Tcl_AppendResult(interp, "ambiguous switch \"", name, "\"\n",
110                (char *) NULL);
111            DoHelp(interp, specs);
112            return NULL;
113        }
114        matchPtr = sp;
115    }
116    if (strcmp(name, "-help") == 0) {
117        DoHelp(interp, specs);
118        return NULL;
119    }
120    if (matchPtr == NULL) {
121        Tcl_AppendResult(interp, "unknown switch \"", name, "\"\n",
122                         (char *)NULL);
123        DoHelp(interp, specs);
124        return NULL;
125    }
126    return matchPtr;
127}
128
129/*
130 *---------------------------------------------------------------------------
131 *
132 * DoSwitch --
133 *
134 *      This procedure applies a single configuration switch to a widget
135 *      record.
136 *
137 * Results:
138 *      A standard Tcl return value.
139 *
140 * Side effects:
141 *      WidgRec is modified as indicated by specPtr and value.  The old value
142 *      is recycled, if that is appropriate for the value type.
143 *
144 *---------------------------------------------------------------------------
145 */
146static int
147DoSwitch(
148    Tcl_Interp *interp,         /* Interpreter for error reporting. */
149    SwitchSpec *sp,             /* Specifier to apply. */
150    Tcl_Obj *objPtr,            /* Value to use to fill in widgRec. */
151    void *record)               /* Record whose fields are to be modified.
152                                 * Values must be properly initialized. */
153{
154    do {
155        char *ptr;
156
157        ptr = (char *)record + sp->offset;
158        switch (sp->type) {
159        case SWITCH_BOOLEAN:
160            {
161                int value;
162
163                if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) {
164                    return TCL_ERROR;
165                }
166                if (sp->mask > 0) {
167                    if (value) {
168                        *((int *)ptr) |= sp->mask;
169                    } else {
170                        *((int *)ptr) &= ~sp->mask;
171                    }
172                } else {
173                    *((int *)ptr) = value;
174                }
175            }
176            break;
177
178        case SWITCH_DOUBLE:
179            if (Tcl_GetDoubleFromObj(interp, objPtr, (double *)ptr) != TCL_OK) {
180                return TCL_ERROR;
181            }
182            break;
183
184        case SWITCH_OBJ:
185            *(Tcl_Obj **)ptr = objPtr;
186            break;
187
188        case SWITCH_FLOAT:
189            {
190                double value;
191
192                if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
193                    return TCL_ERROR;
194                }
195                *(float *)ptr = (float)value;
196            }
197            break;
198
199        case SWITCH_INT:
200            if (Tcl_GetIntFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
201                return TCL_ERROR;
202            }
203            break;
204
205        case SWITCH_LIST:
206            {
207                int argc;
208
209                if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc,
210                                  (const char ***)ptr) != TCL_OK) {
211                    return TCL_ERROR;
212                }
213            }
214            break;
215
216        case SWITCH_LONG:
217            if (Tcl_GetLongFromObj(interp, objPtr, (long *)ptr) != TCL_OK) {
218                return TCL_ERROR;
219            }
220            break;
221
222        case SWITCH_STRING:
223            {
224                char *oldPtr, *newPtr, **strPtr;
225                char *string;
226
227                string = Tcl_GetString(objPtr);
228                strPtr = (char **)ptr;
229                newPtr = ((*string == '\0') && (sp->flags & SWITCH_NULL_OK))
230                    ? NULL : strdup(string);
231                oldPtr = *strPtr;
232                if (oldPtr != NULL) {
233                    free(oldPtr);
234                }
235                *strPtr = newPtr;
236            }
237            break;
238
239        case SWITCH_CUSTOM:
240            assert(sp->customPtr != NULL);
241            if ((*sp->customPtr->parseProc)(sp->customPtr->clientData, interp,
242                sp->switchName, objPtr, (char *)record, sp->offset, sp->flags)
243                != TCL_OK) {
244                return TCL_ERROR;
245            }
246            break;
247
248        default:
249            Tcl_AppendResult(interp, "bad switch table: unknown type",
250                             (char *)NULL);
251            return TCL_ERROR;
252        }
253        sp++;
254    } while ((sp->switchName == NULL) && (sp->type != SWITCH_END));
255    return TCL_OK;
256}
257
258/*
259 *---------------------------------------------------------------------------
260 *
261 * ParseSwitches --
262 *
263 *      Process command-line switches to fill in fields of a record with
264 *      resources and other parameters.
265 *
266 * Results:
267 *      Returns the number of arguments comsumed by parsing the command line.
268 *      If an error occurred, -1 will be returned and an error messages can be
269 *      found as the interpreter result.
270 *
271 * Side effects:
272 *      The fields of widgRec get filled in with information from argc/argv.
273 *      Old information in widgRec's fields gets recycled.
274 *
275 *---------------------------------------------------------------------------
276 */
277int
278ParseSwitches(
279    Tcl_Interp *interp,         /* Interpreter for error reporting. */
280    SwitchSpec *specs,          /* Describes legal switches. */
281    int objc,                   /* Number of elements in argv. */
282    Tcl_Obj *const *objv,       /* Command-line switches. */
283    void *record,               /* Record whose fields are to be modified.
284                                 * Values must be properly initialized. */
285    int flags)                  /* Used to specify additional flags that must
286                                 * be present in switch specs for them to be
287                                 * considered.  */
288{
289    SwitchSpec *sp;
290    int count;
291    int needFlags;              /* Specs must contain this set of flags or
292                                 * else they are not considered. */
293    int hateFlags;              /* If a spec contains any bits here, it's not
294                                 * considered. */
295
296    needFlags = flags & ~(SWITCH_USER_BIT - 1);
297    hateFlags = 0;
298
299    /*
300     * Pass 1:  Clear the change flags on all the specs so that we
301     *          can check it later.
302     */
303    for (sp = specs; sp->type != SWITCH_END; sp++) {
304        sp->flags &= ~SWITCH_SPECIFIED;
305    }
306    /*
307     * Pass 2:  Process the arguments that match entries in the specs.
308     *          It's an error if the argument doesn't match anything.
309     */
310    for (count = 0; count < objc; count++) {
311        char *arg;
312        int length;
313
314        arg = Tcl_GetStringFromObj(objv[count], &length);
315        if (flags & SWITCH_OBJV_PARTIAL) {
316            /*
317             * If the argument doesn't start with a '-' (not a switch) or is
318             * '--', stop processing and return the number of arguments
319             * comsumed.
320             */
321            if (arg[0] != '-') {
322                return count;
323            }
324            if ((arg[1] == '-') && (arg[2] == '\0')) {
325                return count + 1; /* include the "--" in the count. */
326            }
327        }
328        sp = FindSwitchSpec(interp, specs, arg, length, needFlags, hateFlags);
329        if (sp == NULL) {
330            return -1;
331        }
332        if (sp->type == SWITCH_BITMASK) {
333            char *ptr;
334
335            ptr = (char *)record + sp->offset;
336            *((int *)ptr) |= sp->mask;
337        } else if (sp->type == SWITCH_BITMASK_NEG) {
338            char *ptr;
339           
340            ptr = (char *)record + sp->offset;
341            *((int *)ptr) &= ~sp->mask;
342        } else if (sp->type == SWITCH_VALUE) {
343            char *ptr;
344           
345            ptr = (char *)record + sp->offset;
346            *((int *)ptr) = sp->mask;
347        } else {
348            count++;
349            if (count == objc) {
350                Tcl_AppendResult(interp, "value for \"", arg, "\" missing",
351                                 (char *) NULL);
352                return -1;
353            }
354            if (DoSwitch(interp, sp, objv[count], record) != TCL_OK) {
355                char msg[200];
356
357                sprintf(msg, "\n    (processing \"%.40s\" switch)",
358                        sp->switchName);
359                Tcl_AddErrorInfo(interp, msg);
360                return -1;
361            }
362        }
363        sp->flags |= SWITCH_SPECIFIED;
364    }
365    return count;
366}
367
368/*
369 *---------------------------------------------------------------------------
370 *
371 * FreeSwitches --
372 *
373 *      Free up all resources associated with switches.
374 *
375 * Results:
376 *      None.
377 *
378 *---------------------------------------------------------------------------
379 */
380void
381FreeSwitches(
382    SwitchSpec *specs,          /* Describes legal switches. */
383    void *record,               /* Record whose fields contain current values
384                                 * for switches. */
385    int needFlags)              /* Used to specify additional flags that must
386                                 * be present in config specs for them to be
387                                 * considered. */
388{
389    SwitchSpec *sp;
390
391    for (sp = specs; sp->type != SWITCH_END; sp++) {
392        if ((sp->flags & needFlags) == needFlags) {
393            char *ptr;
394
395            ptr = (char *)record + sp->offset;
396            switch (sp->type) {
397            case SWITCH_STRING:
398            case SWITCH_LIST:
399                if (*((char **) ptr) != NULL) {
400                    free(*((char **) ptr));
401                    *((char **) ptr) = NULL;
402                }
403                break;
404
405            case SWITCH_CUSTOM:
406                assert(sp->customPtr != NULL);
407                if ((*(char **)ptr != NULL) &&
408                    (sp->customPtr->freeProc != NULL)) {
409                    (*sp->customPtr->freeProc)((char *)record, sp->offset,
410                        sp->flags);
411                }
412                break;
413
414            default:
415                break;
416            }
417        }
418    }
419}
420
421/*
422 *---------------------------------------------------------------------------
423 *
424 * SwitchModified --
425 *
426 *      Given the configuration specifications and one or more switch patterns
427 *      (terminated by a NULL), indicate if any of the matching switches has
428 *      been reset.
429 *
430 * Results:
431 *      Returns 1 if one of the switches have changed, 0 otherwise.
432 *
433 *---------------------------------------------------------------------------
434 */
435int
436SwitchChanged TCL_VARARGS_DEF(SwitchSpec *, arg1)
437{
438    va_list argList;
439    SwitchSpec *specs;
440    SwitchSpec *sp;
441    char *switchName;
442
443    specs = TCL_VARARGS_START(SwitchSpec *, arg1, argList);
444    while ((switchName = va_arg(argList, char *)) != NULL) {
445        for (sp = specs; sp->type != SWITCH_END; sp++) {
446            if ((Tcl_StringMatch(sp->switchName, switchName)) &&
447                (sp->flags & SWITCH_SPECIFIED)) {
448                va_end(argList);
449                return 1;
450            }
451        }
452    }
453    va_end(argList);
454    return 0;
455}
456
457int
458ExprDoubleFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, double *valuePtr)
459{
460    /* First try to extract the value as a double precision number. */
461    if (Tcl_GetDoubleFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) {
462        return TCL_OK;
463    }
464    /* Then try to parse it as an expression. */
465    if (Tcl_ExprDouble(interp, Tcl_GetString(objPtr), valuePtr) == TCL_OK) {
466        return TCL_OK;
467    }
468    return TCL_ERROR;
469}
470
471int
472ExprIntFromObj(
473    Tcl_Interp *interp,
474    Tcl_Obj *objPtr,
475    int *valuePtr)
476{
477    long lvalue;
478
479    /* First try to extract the value as a simple integer. */
480    if (Tcl_GetIntFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) {
481        return TCL_OK;
482    }
483    /* Otherwise try to parse it as an expression. */
484    if (Tcl_ExprLong(interp, Tcl_GetString(objPtr), &lvalue) == TCL_OK) {
485        *valuePtr = lvalue;
486        return TCL_OK;
487    }
488    return TCL_ERROR;
489}
490
491}
Note: See TracBrowser for help on using the repository browser.