/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Switch.cpp -- * * This module implements command/argument switch parsing procedures for the * BLT toolkit. * * Copyright 1991-2004 George A Howlett. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include "Switch.h" namespace nv { static void DoHelp(Tcl_Interp *interp, SwitchSpec *specs) { Tcl_DString ds; SwitchSpec *sp; Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, "following switches are available:", -1); for (sp = specs; sp->type != SWITCH_END; sp++) { Tcl_DStringAppend(&ds, "\n ", 4); Tcl_DStringAppend(&ds, sp->switchName, -1); Tcl_DStringAppend(&ds, " ", 1); Tcl_DStringAppend(&ds, sp->help, -1); } Tcl_AppendResult(interp, Tcl_DStringValue(&ds), (char *)NULL); Tcl_DStringFree(&ds); } /* *--------------------------------------------------------------------------- * * FindSwitchSpec -- * * Search through a table of configuration specs, looking for one that * matches a given argvName. * * Results: * The return value is a pointer to the matching entry, or NULL if * nothing matched. In that case an error message is left in the * interp's result. * * Side effects: * None. * *--------------------------------------------------------------------------- */ static SwitchSpec * FindSwitchSpec( Tcl_Interp *interp, /* Used for reporting errors. */ SwitchSpec *specs, /* Pointer to table of configuration * specifications for a widget. */ const char *name, /* Name identifying a particular switch. */ int length, /* Length of name. */ int needFlags, /* Flags that must be present in matching * entry. */ int hateFlags) /* Flags that must NOT be present in matching * entry. */ { SwitchSpec *sp; char c; /* First character of current argument. */ SwitchSpec *matchPtr; /* Matching spec, or NULL. */ c = name[1]; matchPtr = NULL; for (sp = specs; sp->type != SWITCH_END; sp++) { if (sp->switchName == NULL) { continue; } if (((sp->flags & needFlags) != needFlags) || (sp->flags & hateFlags)) { continue; } if ((sp->switchName[1] != c) || (strncmp(sp->switchName, name, length) != 0)) { continue; } if (sp->switchName[length] == '\0') { return sp; /* Stop on a perfect match. */ } if (matchPtr != NULL) { Tcl_AppendResult(interp, "ambiguous switch \"", name, "\"\n", (char *) NULL); DoHelp(interp, specs); return NULL; } matchPtr = sp; } if (strcmp(name, "-help") == 0) { DoHelp(interp, specs); return NULL; } if (matchPtr == NULL) { Tcl_AppendResult(interp, "unknown switch \"", name, "\"\n", (char *)NULL); DoHelp(interp, specs); return NULL; } return matchPtr; } /* *--------------------------------------------------------------------------- * * DoSwitch -- * * This procedure applies a single configuration switch to a widget * record. * * Results: * A standard Tcl return value. * * Side effects: * WidgRec is modified as indicated by specPtr and value. The old value * is recycled, if that is appropriate for the value type. * *--------------------------------------------------------------------------- */ static int DoSwitch( Tcl_Interp *interp, /* Interpreter for error reporting. */ SwitchSpec *sp, /* Specifier to apply. */ Tcl_Obj *objPtr, /* Value to use to fill in widgRec. */ void *record) /* Record whose fields are to be modified. * Values must be properly initialized. */ { do { char *ptr; ptr = (char *)record + sp->offset; switch (sp->type) { case SWITCH_BOOLEAN: { int value; if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) { return TCL_ERROR; } if (sp->mask > 0) { if (value) { *((int *)ptr) |= sp->mask; } else { *((int *)ptr) &= ~sp->mask; } } else { *((int *)ptr) = value; } } break; case SWITCH_DOUBLE: if (Tcl_GetDoubleFromObj(interp, objPtr, (double *)ptr) != TCL_OK) { return TCL_ERROR; } break; case SWITCH_OBJ: *(Tcl_Obj **)ptr = objPtr; break; case SWITCH_FLOAT: { double value; if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) { return TCL_ERROR; } *(float *)ptr = (float)value; } break; case SWITCH_INT: if (Tcl_GetIntFromObj(interp, objPtr, (int *)ptr) != TCL_OK) { return TCL_ERROR; } break; case SWITCH_LIST: { int argc; if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, (const char ***)ptr) != TCL_OK) { return TCL_ERROR; } } break; case SWITCH_LONG: if (Tcl_GetLongFromObj(interp, objPtr, (long *)ptr) != TCL_OK) { return TCL_ERROR; } break; case SWITCH_STRING: { char *oldPtr, *newPtr, **strPtr; char *string; string = Tcl_GetString(objPtr); strPtr = (char **)ptr; newPtr = ((*string == '\0') && (sp->flags & SWITCH_NULL_OK)) ? NULL : strdup(string); oldPtr = *strPtr; if (oldPtr != NULL) { free(oldPtr); } *strPtr = newPtr; } break; case SWITCH_CUSTOM: assert(sp->customPtr != NULL); if ((*sp->customPtr->parseProc)(sp->customPtr->clientData, interp, sp->switchName, objPtr, (char *)record, sp->offset, sp->flags) != TCL_OK) { return TCL_ERROR; } break; default: Tcl_AppendResult(interp, "bad switch table: unknown type", (char *)NULL); return TCL_ERROR; } sp++; } while ((sp->switchName == NULL) && (sp->type != SWITCH_END)); return TCL_OK; } /* *--------------------------------------------------------------------------- * * ParseSwitches -- * * Process command-line switches to fill in fields of a record with * resources and other parameters. * * Results: * Returns the number of arguments comsumed by parsing the command line. * If an error occurred, -1 will be returned and an error messages can be * found as the interpreter result. * * Side effects: * The fields of widgRec get filled in with information from argc/argv. * Old information in widgRec's fields gets recycled. * *--------------------------------------------------------------------------- */ int ParseSwitches( Tcl_Interp *interp, /* Interpreter for error reporting. */ SwitchSpec *specs, /* Describes legal switches. */ int objc, /* Number of elements in argv. */ Tcl_Obj *const *objv, /* Command-line switches. */ void *record, /* Record whose fields are to be modified. * Values must be properly initialized. */ int flags) /* Used to specify additional flags that must * be present in switch specs for them to be * considered. */ { SwitchSpec *sp; int count; int needFlags; /* Specs must contain this set of flags or * else they are not considered. */ int hateFlags; /* If a spec contains any bits here, it's not * considered. */ needFlags = flags & ~(SWITCH_USER_BIT - 1); hateFlags = 0; /* * Pass 1: Clear the change flags on all the specs so that we * can check it later. */ for (sp = specs; sp->type != SWITCH_END; sp++) { sp->flags &= ~SWITCH_SPECIFIED; } /* * Pass 2: Process the arguments that match entries in the specs. * It's an error if the argument doesn't match anything. */ for (count = 0; count < objc; count++) { char *arg; int length; arg = Tcl_GetStringFromObj(objv[count], &length); if (flags & SWITCH_OBJV_PARTIAL) { /* * If the argument doesn't start with a '-' (not a switch) or is * '--', stop processing and return the number of arguments * comsumed. */ if (arg[0] != '-') { return count; } if ((arg[1] == '-') && (arg[2] == '\0')) { return count + 1; /* include the "--" in the count. */ } } sp = FindSwitchSpec(interp, specs, arg, length, needFlags, hateFlags); if (sp == NULL) { return -1; } if (sp->type == SWITCH_BITMASK) { char *ptr; ptr = (char *)record + sp->offset; *((int *)ptr) |= sp->mask; } else if (sp->type == SWITCH_BITMASK_NEG) { char *ptr; ptr = (char *)record + sp->offset; *((int *)ptr) &= ~sp->mask; } else if (sp->type == SWITCH_VALUE) { char *ptr; ptr = (char *)record + sp->offset; *((int *)ptr) = sp->mask; } else { count++; if (count == objc) { Tcl_AppendResult(interp, "value for \"", arg, "\" missing", (char *) NULL); return -1; } if (DoSwitch(interp, sp, objv[count], record) != TCL_OK) { char msg[200]; sprintf(msg, "\n (processing \"%.40s\" switch)", sp->switchName); Tcl_AddErrorInfo(interp, msg); return -1; } } sp->flags |= SWITCH_SPECIFIED; } return count; } /* *--------------------------------------------------------------------------- * * FreeSwitches -- * * Free up all resources associated with switches. * * Results: * None. * *--------------------------------------------------------------------------- */ void FreeSwitches( SwitchSpec *specs, /* Describes legal switches. */ void *record, /* Record whose fields contain current values * for switches. */ int needFlags) /* Used to specify additional flags that must * be present in config specs for them to be * considered. */ { SwitchSpec *sp; for (sp = specs; sp->type != SWITCH_END; sp++) { if ((sp->flags & needFlags) == needFlags) { char *ptr; ptr = (char *)record + sp->offset; switch (sp->type) { case SWITCH_STRING: case SWITCH_LIST: if (*((char **) ptr) != NULL) { free(*((char **) ptr)); *((char **) ptr) = NULL; } break; case SWITCH_CUSTOM: assert(sp->customPtr != NULL); if ((*(char **)ptr != NULL) && (sp->customPtr->freeProc != NULL)) { (*sp->customPtr->freeProc)((char *)record, sp->offset, sp->flags); } break; default: break; } } } } /* *--------------------------------------------------------------------------- * * SwitchModified -- * * Given the configuration specifications and one or more switch patterns * (terminated by a NULL), indicate if any of the matching switches has * been reset. * * Results: * Returns 1 if one of the switches have changed, 0 otherwise. * *--------------------------------------------------------------------------- */ int SwitchChanged TCL_VARARGS_DEF(SwitchSpec *, arg1) { va_list argList; SwitchSpec *specs; SwitchSpec *sp; char *switchName; specs = TCL_VARARGS_START(SwitchSpec *, arg1, argList); while ((switchName = va_arg(argList, char *)) != NULL) { for (sp = specs; sp->type != SWITCH_END; sp++) { if ((Tcl_StringMatch(sp->switchName, switchName)) && (sp->flags & SWITCH_SPECIFIED)) { va_end(argList); return 1; } } } va_end(argList); return 0; } int ExprDoubleFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, double *valuePtr) { /* First try to extract the value as a double precision number. */ if (Tcl_GetDoubleFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) { return TCL_OK; } /* Then try to parse it as an expression. */ if (Tcl_ExprDouble(interp, Tcl_GetString(objPtr), valuePtr) == TCL_OK) { return TCL_OK; } return TCL_ERROR; } int ExprIntFromObj( Tcl_Interp *interp, Tcl_Obj *objPtr, int *valuePtr) { long lvalue; /* First try to extract the value as a simple integer. */ if (Tcl_GetIntFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) { return TCL_OK; } /* Otherwise try to parse it as an expression. */ if (Tcl_ExprLong(interp, Tcl_GetString(objPtr), &lvalue) == TCL_OK) { *valuePtr = lvalue; return TCL_OK; } return TCL_ERROR; } }