Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclIndexObj.c @ 63

Last change on this file since 63 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 19.0 KB
Line 
1/*
2 * tclIndexObj.c --
3 *
4 *      This file implements objects of type "index". This object type is used
5 *      to lookup a keyword in a table of valid values and cache the index of
6 *      the matching entry.
7 *
8 * Copyright (c) 1997 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tclIndexObj.c,v 1.38 2007/12/13 15:23:18 dgp Exp $
14 */
15
16#include "tclInt.h"
17
18/*
19 * Prototypes for functions defined later in this file:
20 */
21
22static int              SetIndexFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
23static void             UpdateStringOfIndex(Tcl_Obj *objPtr);
24static void             DupIndex(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr);
25static void             FreeIndex(Tcl_Obj *objPtr);
26
27/*
28 * The structure below defines the index Tcl object type by means of functions
29 * that can be invoked by generic object code.
30 */
31
32static Tcl_ObjType indexType = {
33    "index",                            /* name */
34    FreeIndex,                          /* freeIntRepProc */
35    DupIndex,                           /* dupIntRepProc */
36    UpdateStringOfIndex,                /* updateStringProc */
37    SetIndexFromAny                     /* setFromAnyProc */
38};
39
40/*
41 * The definition of the internal representation of the "index" object; The
42 * internalRep.otherValuePtr field of an object of "index" type will be a
43 * pointer to one of these structures.
44 *
45 * Keep this structure declaration in sync with tclTestObj.c
46 */
47
48typedef struct {
49    void *tablePtr;                     /* Pointer to the table of strings */
50    int offset;                         /* Offset between table entries */
51    int index;                          /* Selected index into table. */
52} IndexRep;
53
54/*
55 * The following macros greatly simplify moving through a table...
56 */
57
58#define STRING_AT(table, offset, index) \
59        (*((const char *const *)(((char *)(table)) + ((offset) * (index)))))
60#define NEXT_ENTRY(table, offset) \
61        (&(STRING_AT(table, offset, 1)))
62#define EXPAND_OF(indexRep) \
63        STRING_AT((indexRep)->tablePtr, (indexRep)->offset, (indexRep)->index)
64
65/*
66 *----------------------------------------------------------------------
67 *
68 * Tcl_GetIndexFromObj --
69 *
70 *      This function looks up an object's value in a table of strings and
71 *      returns the index of the matching string, if any.
72 *
73 * Results:
74 *      If the value of objPtr is identical to or a unique abbreviation for
75 *      one of the entries in objPtr, then the return value is TCL_OK and the
76 *      index of the matching entry is stored at *indexPtr. If there isn't a
77 *      proper match, then TCL_ERROR is returned and an error message is left
78 *      in interp's result (unless interp is NULL). The msg argument is used
79 *      in the error message; for example, if msg has the value "option" then
80 *      the error message will say something flag 'bad option "foo": must be
81 *      ...'
82 *
83 * Side effects:
84 *      The result of the lookup is cached as the internal rep of objPtr, so
85 *      that repeated lookups can be done quickly.
86 *
87 *----------------------------------------------------------------------
88 */
89
90int
91Tcl_GetIndexFromObj(
92    Tcl_Interp *interp,         /* Used for error reporting if not NULL. */
93    Tcl_Obj *objPtr,            /* Object containing the string to lookup. */
94    const char **tablePtr,      /* Array of strings to compare against the
95                                 * value of objPtr; last entry must be NULL
96                                 * and there must not be duplicate entries. */
97    const char *msg,            /* Identifying word to use in error
98                                 * messages. */
99    int flags,                  /* 0 or TCL_EXACT */
100    int *indexPtr)              /* Place to store resulting integer index. */
101{
102
103    /*
104     * See if there is a valid cached result from a previous lookup (doing the
105     * check here saves the overhead of calling Tcl_GetIndexFromObjStruct in
106     * the common case where the result is cached).
107     */
108
109    if (objPtr->typePtr == &indexType) {
110        IndexRep *indexRep = objPtr->internalRep.otherValuePtr;
111
112        /*
113         * Here's hoping we don't get hit by unfortunate packing constraints
114         * on odd platforms like a Cray PVP...
115         */
116
117        if (indexRep->tablePtr == (void *) tablePtr
118                && indexRep->offset == sizeof(char *)) {
119            *indexPtr = indexRep->index;
120            return TCL_OK;
121        }
122    }
123    return Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, sizeof(char *),
124            msg, flags, indexPtr);
125}
126
127/*
128 *----------------------------------------------------------------------
129 *
130 * Tcl_GetIndexFromObjStruct --
131 *
132 *      This function looks up an object's value given a starting string and
133 *      an offset for the amount of space between strings. This is useful when
134 *      the strings are embedded in some other kind of array.
135 *
136 * Results:
137 *      If the value of objPtr is identical to or a unique abbreviation for
138 *      one of the entries in objPtr, then the return value is TCL_OK and the
139 *      index of the matching entry is stored at *indexPtr. If there isn't a
140 *      proper match, then TCL_ERROR is returned and an error message is left
141 *      in interp's result (unless interp is NULL). The msg argument is used
142 *      in the error message; for example, if msg has the value "option" then
143 *      the error message will say something flag 'bad option "foo": must be
144 *      ...'
145 *
146 * Side effects:
147 *      The result of the lookup is cached as the internal rep of objPtr, so
148 *      that repeated lookups can be done quickly.
149 *
150 *----------------------------------------------------------------------
151 */
152
153int
154Tcl_GetIndexFromObjStruct(
155    Tcl_Interp *interp,         /* Used for error reporting if not NULL. */
156    Tcl_Obj *objPtr,            /* Object containing the string to lookup. */
157    const void *tablePtr,       /* The first string in the table. The second
158                                 * string will be at this address plus the
159                                 * offset, the third plus the offset again,
160                                 * etc. The last entry must be NULL and there
161                                 * must not be duplicate entries. */
162    int offset,                 /* The number of bytes between entries */
163    const char *msg,            /* Identifying word to use in error
164                                 * messages. */
165    int flags,                  /* 0 or TCL_EXACT */
166    int *indexPtr)              /* Place to store resulting integer index. */
167{
168    int index, idx, numAbbrev;
169    char *key, *p1;
170    const char *p2;
171    const char *const *entryPtr;
172    Tcl_Obj *resultPtr;
173    IndexRep *indexRep;
174
175    /*
176     * See if there is a valid cached result from a previous lookup.
177     */
178
179    if (objPtr->typePtr == &indexType) {
180        indexRep = objPtr->internalRep.otherValuePtr;
181        if (indexRep->tablePtr==tablePtr && indexRep->offset==offset) {
182            *indexPtr = indexRep->index;
183            return TCL_OK;
184        }
185    }
186
187    /*
188     * Lookup the value of the object in the table. Accept unique
189     * abbreviations unless TCL_EXACT is set in flags.
190     */
191
192    key = TclGetString(objPtr);
193    index = -1;
194    numAbbrev = 0;
195
196    /*
197     * Scan the table looking for one of:
198     *  - An exact match (always preferred)
199     *  - A single abbreviation (allowed depending on flags)
200     *  - Several abbreviations (never allowed, but overridden by exact match)
201     */
202
203    for (entryPtr = tablePtr, idx = 0; *entryPtr != NULL;
204            entryPtr = NEXT_ENTRY(entryPtr, offset), idx++) {
205        for (p1 = key, p2 = *entryPtr; *p1 == *p2; p1++, p2++) {
206            if (*p1 == '\0') {
207                index = idx;
208                goto done;
209            }
210        }
211        if (*p1 == '\0') {
212            /*
213             * The value is an abbreviation for this entry. Continue checking
214             * other entries to make sure it's unique. If we get more than one
215             * unique abbreviation, keep searching to see if there is an exact
216             * match, but remember the number of unique abbreviations and
217             * don't allow either.
218             */
219
220            numAbbrev++;
221            index = idx;
222        }
223    }
224
225    /*
226     * Check if we were instructed to disallow abbreviations.
227     */
228
229    if ((flags & TCL_EXACT) || (key[0] == '\0') || (numAbbrev != 1)) {
230        goto error;
231    }
232
233  done:
234    /*
235     * Cache the found representation. Note that we want to avoid allocating a
236     * new internal-rep if at all possible since that is potentially a slow
237     * operation.
238     */
239
240    if (objPtr->typePtr == &indexType) {
241        indexRep = objPtr->internalRep.otherValuePtr;
242    } else {
243        TclFreeIntRep(objPtr);
244        indexRep = (IndexRep *) ckalloc(sizeof(IndexRep));
245        objPtr->internalRep.otherValuePtr = indexRep;
246        objPtr->typePtr = &indexType;
247    }
248    indexRep->tablePtr = (void *) tablePtr;
249    indexRep->offset = offset;
250    indexRep->index = index;
251
252    *indexPtr = index;
253    return TCL_OK;
254
255  error:
256    if (interp != NULL) {
257        /*
258         * Produce a fancy error message.
259         */
260
261        int count;
262
263        TclNewObj(resultPtr);
264        Tcl_SetObjResult(interp, resultPtr);
265        Tcl_AppendStringsToObj(resultPtr, (numAbbrev > 1) &&
266                !(flags & TCL_EXACT) ? "ambiguous " : "bad ", msg, " \"", key,
267                "\": must be ", STRING_AT(tablePtr, offset, 0), NULL);
268        for (entryPtr = NEXT_ENTRY(tablePtr, offset), count = 0;
269                *entryPtr != NULL;
270                entryPtr = NEXT_ENTRY(entryPtr, offset), count++) {
271            if (*NEXT_ENTRY(entryPtr, offset) == NULL) {
272                Tcl_AppendStringsToObj(resultPtr, ((count > 0) ? "," : ""),
273                        " or ", *entryPtr, NULL);
274            } else {
275                Tcl_AppendStringsToObj(resultPtr, ", ", *entryPtr, NULL);
276            }
277        }
278        Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", msg, key, NULL);
279    }
280    return TCL_ERROR;
281}
282
283/*
284 *----------------------------------------------------------------------
285 *
286 * SetIndexFromAny --
287 *
288 *      This function is called to convert a Tcl object to index internal
289 *      form. However, this doesn't make sense (need to have a table of
290 *      keywords in order to do the conversion) so the function always
291 *      generates an error.
292 *
293 * Results:
294 *      The return value is always TCL_ERROR, and an error message is left in
295 *      interp's result if interp isn't NULL.
296 *
297 * Side effects:
298 *      None.
299 *
300 *----------------------------------------------------------------------
301 */
302
303static int
304SetIndexFromAny(
305    Tcl_Interp *interp,         /* Used for error reporting if not NULL. */
306    register Tcl_Obj *objPtr)   /* The object to convert. */
307{
308    Tcl_SetObjResult(interp, Tcl_NewStringObj(
309            "can't convert value to index except via Tcl_GetIndexFromObj API",
310            -1));
311    return TCL_ERROR;
312}
313
314/*
315 *----------------------------------------------------------------------
316 *
317 * UpdateStringOfIndex --
318 *
319 *      This function is called to convert a Tcl object from index internal
320 *      form to its string form. No abbreviation is ever generated.
321 *
322 * Results:
323 *      None.
324 *
325 * Side effects:
326 *      The string representation of the object is updated.
327 *
328 *----------------------------------------------------------------------
329 */
330
331static void
332UpdateStringOfIndex(
333    Tcl_Obj *objPtr)
334{
335    IndexRep *indexRep = objPtr->internalRep.otherValuePtr;
336    register char *buf;
337    register unsigned len;
338    register const char *indexStr = EXPAND_OF(indexRep);
339
340    len = strlen(indexStr);
341    buf = (char *) ckalloc(len + 1);
342    memcpy(buf, indexStr, len+1);
343    objPtr->bytes = buf;
344    objPtr->length = len;
345}
346
347/*
348 *----------------------------------------------------------------------
349 *
350 * DupIndex --
351 *
352 *      This function is called to copy the internal rep of an index Tcl
353 *      object from to another object.
354 *
355 * Results:
356 *      None.
357 *
358 * Side effects:
359 *      The internal representation of the target object is updated and the
360 *      type is set.
361 *
362 *----------------------------------------------------------------------
363 */
364
365static void
366DupIndex(
367    Tcl_Obj *srcPtr,
368    Tcl_Obj *dupPtr)
369{
370    IndexRep *srcIndexRep = srcPtr->internalRep.otherValuePtr;
371    IndexRep *dupIndexRep = (IndexRep *) ckalloc(sizeof(IndexRep));
372
373    memcpy(dupIndexRep, srcIndexRep, sizeof(IndexRep));
374    dupPtr->internalRep.otherValuePtr = dupIndexRep;
375    dupPtr->typePtr = &indexType;
376}
377
378/*
379 *----------------------------------------------------------------------
380 *
381 * FreeIndex --
382 *
383 *      This function is called to delete the internal rep of an index Tcl
384 *      object.
385 *
386 * Results:
387 *      None.
388 *
389 * Side effects:
390 *      The internal representation of the target object is deleted.
391 *
392 *----------------------------------------------------------------------
393 */
394
395static void
396FreeIndex(
397    Tcl_Obj *objPtr)
398{
399    ckfree((char *) objPtr->internalRep.otherValuePtr);
400}
401
402/*
403 *----------------------------------------------------------------------
404 *
405 * Tcl_WrongNumArgs --
406 *
407 *      This function generates a "wrong # args" error message in an
408 *      interpreter. It is used as a utility function by many command
409 *      functions, including the function that implements procedures.
410 *
411 * Results:
412 *      None.
413 *
414 * Side effects:
415 *      An error message is generated in interp's result object to indicate
416 *      that a command was invoked with the wrong number of arguments. The
417 *      message has the form
418 *              wrong # args: should be "foo bar additional stuff"
419 *      where "foo" and "bar" are the initial objects in objv (objc determines
420 *      how many of these are printed) and "additional stuff" is the contents
421 *      of the message argument.
422 *
423 *      The message printed is modified somewhat if the command is wrapped
424 *      inside an ensemble. In that case, the error message generated is
425 *      rewritten in such a way that it appears to be generated from the
426 *      user-visible command and not how that command is actually implemented,
427 *      giving a better overall user experience.
428 *
429 *      Internally, the Tcl core may set the flag INTERP_ALTERNATE_WRONG_ARGS
430 *      in the interpreter to generate complex multi-part messages by calling
431 *      this function repeatedly. This allows the code that knows how to
432 *      handle ensemble-related error messages to be kept here while still
433 *      generating suitable error messages for commands like [read] and
434 *      [socket]. Ideally, this would be done through an extra flags argument,
435 *      but that wouldn't be source-compatible with the existing API and it's
436 *      a fairly rare requirement anyway.
437 *
438 *----------------------------------------------------------------------
439 */
440
441void
442Tcl_WrongNumArgs(
443    Tcl_Interp *interp,         /* Current interpreter. */
444    int objc,                   /* Number of arguments to print from objv. */
445    Tcl_Obj *const objv[],      /* Initial argument objects, which should be
446                                 * included in the error message. */
447    const char *message)        /* Error message to print after the leading
448                                 * objects in objv. The message may be
449                                 * NULL. */
450{
451    Tcl_Obj *objPtr;
452    int i, len, elemLen, flags;
453    Interp *iPtr = (Interp *) interp;
454    const char *elementStr;
455
456    /*
457     * [incr Tcl] does something fairly horrific when generating error
458     * messages for its ensembles; it passes the whole set of ensemble
459     * arguments as a list in the first argument. This means that this code
460     * causes a problem in iTcl if it attempts to correctly quote all
461     * arguments, which would be the correct thing to do. We work around this
462     * nasty behaviour for now, and hope that we can remove it all in the
463     * future...
464     */
465
466#ifndef AVOID_HACKS_FOR_ITCL
467    int isFirst = 1;            /* Special flag used to inhibit the treating
468                                 * of the first word as a list element so the
469                                 * hacky way Itcl generates error messages for
470                                 * its ensembles will still work. [Bug
471                                 * 1066837] */
472#   define MAY_QUOTE_WORD       (!isFirst)
473#   define AFTER_FIRST_WORD     (isFirst = 0)
474#else /* !AVOID_HACKS_FOR_ITCL */
475#   define MAY_QUOTE_WORD       1
476#   define AFTER_FIRST_WORD     (void) 0
477#endif /* AVOID_HACKS_FOR_ITCL */
478
479    TclNewObj(objPtr);
480    if (iPtr->flags & INTERP_ALTERNATE_WRONG_ARGS) {
481        Tcl_AppendObjToObj(objPtr, Tcl_GetObjResult(interp));
482        Tcl_AppendToObj(objPtr, " or \"", -1);
483    } else {
484        Tcl_AppendToObj(objPtr, "wrong # args: should be \"", -1);
485    }
486
487    /*
488     * Check to see if we are processing an ensemble implementation, and if so
489     * rewrite the results in terms of how the ensemble was invoked.
490     */
491
492    if (iPtr->ensembleRewrite.sourceObjs != NULL) {
493        int toSkip = iPtr->ensembleRewrite.numInsertedObjs;
494        int toPrint = iPtr->ensembleRewrite.numRemovedObjs;
495        Tcl_Obj *const *origObjv = iPtr->ensembleRewrite.sourceObjs;
496
497        /*
498         * We only know how to do rewriting if all the replaced objects are
499         * actually arguments (in objv) to this function. Otherwise it just
500         * gets too complicated and we'd be better off just giving a slightly
501         * confusing error message...
502         */
503
504        if (objc < toSkip) {
505            goto addNormalArgumentsToMessage;
506        }
507
508        /*
509         * Strip out the actual arguments that the ensemble inserted.
510         */
511
512        objv += toSkip;
513        objc -= toSkip;
514
515        /*
516         * We assume no object is of index type.
517         */
518
519        for (i=0 ; i<toPrint ; i++) {
520            /*
521             * Add the element, quoting it if necessary.
522             */
523
524            if (origObjv[i]->typePtr == &indexType) {
525                register IndexRep *indexRep =
526                        origObjv[i]->internalRep.otherValuePtr;
527
528                elementStr = EXPAND_OF(indexRep);
529                elemLen = strlen(elementStr);
530            } else if (origObjv[i]->typePtr == &tclEnsembleCmdType) {
531                register EnsembleCmdRep *ecrPtr =
532                        origObjv[i]->internalRep.otherValuePtr;
533
534                elementStr = ecrPtr->fullSubcmdName;
535                elemLen = strlen(elementStr);
536            } else {
537                elementStr = TclGetStringFromObj(origObjv[i], &elemLen);
538            }
539            len = Tcl_ScanCountedElement(elementStr, elemLen, &flags);
540
541            if (MAY_QUOTE_WORD && len != elemLen) {
542                char *quotedElementStr = TclStackAlloc(interp, (unsigned)len);
543
544                len = Tcl_ConvertCountedElement(elementStr, elemLen,
545                        quotedElementStr, flags);
546                Tcl_AppendToObj(objPtr, quotedElementStr, len);
547                TclStackFree(interp, quotedElementStr);
548            } else {
549                Tcl_AppendToObj(objPtr, elementStr, elemLen);
550            }
551
552            AFTER_FIRST_WORD;
553
554            /*
555             * Add a space if the word is not the last one (which has a
556             * moderately complex condition here).
557             */
558
559            if (i<toPrint-1 || objc!=0 || message!=NULL) {
560                Tcl_AppendStringsToObj(objPtr, " ", NULL);
561            }
562        }
563    }
564
565    /*
566     * Now add the arguments (other than those rewritten) that the caller took
567     * from its calling context.
568     */
569
570  addNormalArgumentsToMessage:
571    for (i = 0; i < objc; i++) {
572        /*
573         * If the object is an index type use the index table which allows for
574         * the correct error message even if the subcommand was abbreviated.
575         * Otherwise, just use the string rep.
576         */
577
578        if (objv[i]->typePtr == &indexType) {
579            register IndexRep *indexRep = objv[i]->internalRep.otherValuePtr;
580
581            Tcl_AppendStringsToObj(objPtr, EXPAND_OF(indexRep), NULL);
582        } else if (objv[i]->typePtr == &tclEnsembleCmdType) {
583            register EnsembleCmdRep *ecrPtr =
584                    objv[i]->internalRep.otherValuePtr;
585
586            Tcl_AppendStringsToObj(objPtr, ecrPtr->fullSubcmdName, NULL);
587        } else {
588            /*
589             * Quote the argument if it contains spaces (Bug 942757).
590             */
591
592            elementStr = TclGetStringFromObj(objv[i], &elemLen);
593            len = Tcl_ScanCountedElement(elementStr, elemLen, &flags);
594
595            if (MAY_QUOTE_WORD && len != elemLen) {
596                char *quotedElementStr = TclStackAlloc(interp,(unsigned) len);
597
598                len = Tcl_ConvertCountedElement(elementStr, elemLen,
599                        quotedElementStr, flags);
600                Tcl_AppendToObj(objPtr, quotedElementStr, len);
601                TclStackFree(interp, quotedElementStr);
602            } else {
603                Tcl_AppendToObj(objPtr, elementStr, elemLen);
604            }
605        }
606
607        AFTER_FIRST_WORD;
608
609        /*
610         * Append a space character (" ") if there is more text to follow
611         * (either another element from objv, or the message string).
612         */
613
614        if (i<objc-1 || message!=NULL) {
615            Tcl_AppendStringsToObj(objPtr, " ", NULL);
616        }
617    }
618
619    /*
620     * Add any trailing message bits and set the resulting string as the
621     * interpreter result. Caller is responsible for reporting this as an
622     * actual error.
623     */
624
625    if (message != NULL) {
626        Tcl_AppendStringsToObj(objPtr, message, NULL);
627    }
628    Tcl_AppendStringsToObj(objPtr, "\"", NULL);
629    Tcl_SetObjResult(interp, objPtr);
630#undef MAY_QUOTE_WORD
631#undef AFTER_FIRST_WORD
632}
633
634/*
635 * Local Variables:
636 * mode: c
637 * c-basic-offset: 4
638 * fill-column: 78
639 * End:
640 */
Note: See TracBrowser for help on using the repository browser.