source: geovis/trunk/CmdProc.cpp @ 6673

Last change on this file since 6673 was 4057, checked in by ldelgass, 11 years ago

Remove references to Rappture, as it isn't a dependency of geovis

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