source: trunk/packages/vizservers/geovis/CmdProc.cpp @ 3998

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

Add prelimilary skeleton for geovis map rendering server. Not functional, not
integrated into configure, etc.

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 Rappture;
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 *
118Rappture::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.