source: branches/blt4/packages/vizservers/vtkvis/CmdProc.cpp @ 2542

Last change on this file since 2542 was 2542, checked in by gah, 13 years ago

update from trunk

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