source: nanovis/branches/1.1/Switch.cpp @ 4889

Last change on this file since 4889 was 3362, checked in by ldelgass, 11 years ago

Merge nanovis2 branch to trunk

  • 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 Rappture {
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.