source: nanovis/trunk/CmdProc.cpp @ 5107

Last change on this file since 5107 was 4060, checked in by ldelgass, 10 years ago

Add separate configure scripts for nanovis and vtkvis, remove them from the
vizservers configure (which now only configures nanoscale and pymolproxy).

  • Property svn:eol-style set to native
File size: 7.0 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (c) 2004-2013  HUBzero Foundation, LLC
4 *
5 */
6#include <string.h>
7
8#include <tcl.h>
9
10#include "CmdProc.h"
11
12using namespace nv;
13
14/**
15 * Performs a linear search on the array of command operation
16 * specifications to find a partial, anchored match for the given
17 * operation string.
18 *
19 * \return If the string matches unambiguously the index of the specification in
20 * the array is returned.  If the string does not match, even as an
21 * abbreviation, any operation, -1 is returned.  If the string matches,
22 * but ambiguously -2 is returned.
23 */
24static int
25LinearOpSearch(CmdSpec *specs, int low, int high, const char *string,
26               int length)
27{
28    CmdSpec *specPtr;
29    char c;
30    int nMatches, last;
31    int i;
32
33    c = string[0];
34    nMatches = 0;
35    last = -1;
36    for (specPtr = specs+low, i = low; i <= high; i++, specPtr++) {
37        if ((c == specPtr->name[0]) &&
38            (strncmp(string, specPtr->name, length) == 0)) {
39            last = i;
40            nMatches++;
41            if (length == specPtr->minChars) {
42                break;
43            }
44        }
45    }
46    if (nMatches > 1) {
47        return -2;      /* Ambiguous operation name */
48    }
49    if (nMatches == 0) {
50        return -1;      /* Can't find operation */
51    }
52    return last;        /* Op found. */
53}
54
55/**
56 * Performs a binary search on the array of command operation
57 * specifications to find a partial, anchored match for the given
58 * operation string.  If we get to a point where the sample string
59 * partially matches an entry, we then revert to a linear search
60 * over the given range to check if it's an exact match or
61 * ambiguous (example: "color" with commands color and colormode).
62 *
63 * \return If the string matches unambiguously the index of the specification in
64 * the array is returned.  If the string does not match, even as an
65 * abbreviation, any operation, -1 is returned.  If the string matches,
66 * but ambiguously -2 is returned.
67 */
68static int
69BinaryOpSearch(CmdSpec *specs, int low, int high, const char *string,
70               int length)
71{
72    char c;
73
74    c = string[0];
75    while (low <= high) {
76        CmdSpec *specPtr;
77        int compare;
78        int median;
79   
80        median = (low + high) >> 1;
81        specPtr = specs + median;
82
83        /* Test the first character */
84        compare = c - specPtr->name[0];
85        if (compare == 0) {
86            /* Now test the entire string */
87            compare = strncmp(string, specPtr->name, length);
88        }
89        if (compare < 0) {
90            high = median - 1;
91        } else if (compare > 0) {
92            low = median + 1;
93        } else {
94            if (length < specPtr->minChars) {
95                /* Verify that the string is either ambiguous or an exact
96                 * match of another command by doing a linear search over the
97                 * given interval. */
98                return LinearOpSearch(specs, low, high, string, length);
99            }
100            return median;  /* Op found. */
101        }
102    }
103    return -1;          /* Can't find operation */
104}
105
106/**
107 * \brief Find the command operation given a string name.
108 *
109 * This is useful where a group of command operations have the same argument
110 * signature.
111 *
112 * \return If found, a pointer to the procedure (function pointer) is returned.
113 * Otherwise NULL is returned and an error message containing a list of
114 * the possible commands is returned in interp->result.
115 */
116Tcl_ObjCmdProc *
117nv::GetOpFromObj(Tcl_Interp *interp,    /* Interpreter to report errors to */
118                 int nSpecs,            /* Number of specifications in array */
119                 CmdSpec *specs,        /* Op specification array */
120                 int operPos,           /* Position of operation in argument
121                                         * list. */
122                 int objc,              /* Number of arguments in the argument
123                                         * vector.  This includes any prefixed
124                                         * arguments */
125                 Tcl_Obj *const *objv,  /* Argument vector */
126                 int flags)
127{
128    CmdSpec *specPtr;
129    char *string;
130    int length;
131    int n;
132
133    if (objc <= operPos) {  /* No operation argument */
134        if (interp != NULL) {
135            Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL);
136        usage:
137            Tcl_AppendResult(interp, "should be one of...", (char *)NULL);
138            for (n = 0; n < nSpecs; n++) {
139                int i;
140
141                Tcl_AppendResult(interp, "\n  ", (char *)NULL);
142                for (i = 0; i < operPos; i++) {
143                    Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ",
144                                     (char *)NULL);
145                }
146                specPtr = specs + n;
147                Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage,
148                                 (char *)NULL);
149            }
150        }
151        return NULL;
152    }
153    string = Tcl_GetStringFromObj(objv[operPos], &length);
154    if (flags & CMDSPEC_LINEAR_SEARCH) {
155        n = LinearOpSearch(specs, 0, nSpecs - 1, string, length);
156    } else {
157        n = BinaryOpSearch(specs, 0, nSpecs - 1, string, length);
158    }
159    if (n == -2) {
160        if (interp != NULL) {
161            char c;
162
163            Tcl_AppendResult(interp, "ambiguous", (char *)NULL);
164            if (operPos > 2) {
165                Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]),
166                                 (char *)NULL);
167            }
168            Tcl_AppendResult(interp, " operation \"", string, "\" matches: ",
169                             (char *)NULL);
170
171            c = string[0];
172            length = strlen(string);
173            for (n = 0; n < nSpecs; n++) {
174                specPtr = specs + n;
175                if ((c == specPtr->name[0]) &&
176                    (strncmp(string, specPtr->name, length) == 0)) {
177                    Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL);
178                }
179            }
180        }
181        return NULL;
182
183    } else if (n == -1) {   /* Can't find operation, display help */
184        if (interp != NULL) {
185            Tcl_AppendResult(interp, "bad", (char *)NULL);
186            if (operPos > 2) {
187                Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]),
188                                 (char *)NULL);
189            }
190            Tcl_AppendResult(interp, " operation \"", string, "\": ", (char *)NULL);
191            goto usage;
192        }
193        return NULL;
194    }
195    specPtr = specs + n;
196    if ((objc < specPtr->minArgs) ||
197        ((specPtr->maxArgs > 0) && (objc > specPtr->maxArgs))) {
198        if (interp != NULL) {
199            int i;
200
201            Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL);
202            for (i = 0; i < operPos; i++) {
203                Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ",
204                                 (char *)NULL);
205            }
206            Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"",
207                             (char *)NULL);
208        }
209        return NULL;
210    }
211    return specPtr->proc;
212}
Note: See TracBrowser for help on using the repository browser.