source: nanovis/branches/1.1/CmdProc.cpp @ 4923

Last change on this file since 4923 was 4904, checked in by ldelgass, 9 years ago

Merge serveral changes from trunk. Does not include threading, world space
changes, etc.

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