OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [generic/] [tclCmdMZ.c] - Blame information for rev 1780

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclCmdMZ.c --
3
 *
4
 *      This file contains the top-level command routines for most of
5
 *      the Tcl built-in commands whose names begin with the letters
6
 *      M to Z.  It contains only commands in the generic core (i.e.
7
 *      those that don't depend much upon UNIX facilities).
8
 *
9
 * Copyright (c) 1987-1993 The Regents of the University of California.
10
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
11
 *
12
 * See the file "license.terms" for information on usage and redistribution
13
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14
 *
15
 * RCS: @(#) $Id: tclCmdMZ.c,v 1.1.1.1 2002-01-16 10:25:25 markom Exp $
16
 */
17
 
18
#include "tclInt.h"
19
#include "tclPort.h"
20
#include "tclCompile.h"
21
 
22
/*
23
 * Structure used to hold information about variable traces:
24
 */
25
 
26
typedef struct {
27
    int flags;                  /* Operations for which Tcl command is
28
                                 * to be invoked. */
29
    char *errMsg;               /* Error message returned from Tcl command,
30
                                 * or NULL.  Malloc'ed. */
31
    int length;                 /* Number of non-NULL chars. in command. */
32
    char command[4];            /* Space for Tcl command to invoke.  Actual
33
                                 * size will be as large as necessary to
34
                                 * hold command.  This field must be the
35
                                 * last in the structure, so that it can
36
                                 * be larger than 4 bytes. */
37
} TraceVarInfo;
38
 
39
/*
40
 * Forward declarations for procedures defined in this file:
41
 */
42
 
43
static char *           TraceVarProc _ANSI_ARGS_((ClientData clientData,
44
                            Tcl_Interp *interp, char *name1, char *name2,
45
                            int flags));
46
 
47
/*
48
 *----------------------------------------------------------------------
49
 *
50
 * Tcl_PwdCmd --
51
 *
52
 *      This procedure is invoked to process the "pwd" Tcl command.
53
 *      See the user documentation for details on what it does.
54
 *
55
 * Results:
56
 *      A standard Tcl result.
57
 *
58
 * Side effects:
59
 *      See the user documentation.
60
 *
61
 *----------------------------------------------------------------------
62
 */
63
 
64
        /* ARGSUSED */
65
int
66
Tcl_PwdCmd(dummy, interp, argc, argv)
67
    ClientData dummy;                   /* Not used. */
68
    Tcl_Interp *interp;                 /* Current interpreter. */
69
    int argc;                           /* Number of arguments. */
70
    char **argv;                        /* Argument strings. */
71
{
72
    char *dirName;
73
 
74
    if (argc != 1) {
75
        Tcl_AppendResult(interp, "wrong # args: should be \"",
76
                argv[0], "\"", (char *) NULL);
77
        return TCL_ERROR;
78
    }
79
 
80
    dirName = TclGetCwd(interp);
81
    if (dirName == NULL) {
82
        return TCL_ERROR;
83
    }
84
    Tcl_SetResult(interp, dirName, TCL_VOLATILE);
85
    return TCL_OK;
86
}
87
 
88
/*
89
 *----------------------------------------------------------------------
90
 *
91
 * Tcl_RegexpCmd --
92
 *
93
 *      This procedure is invoked to process the "regexp" Tcl command.
94
 *      See the user documentation for details on what it does.
95
 *
96
 * Results:
97
 *      A standard Tcl result.
98
 *
99
 * Side effects:
100
 *      See the user documentation.
101
 *
102
 *----------------------------------------------------------------------
103
 */
104
 
105
        /* ARGSUSED */
106
int
107
Tcl_RegexpCmd(dummy, interp, argc, argv)
108
    ClientData dummy;                   /* Not used. */
109
    Tcl_Interp *interp;                 /* Current interpreter. */
110
    int argc;                           /* Number of arguments. */
111
    char **argv;                        /* Argument strings. */
112
{
113
    int noCase = 0;
114
    int indices = 0;
115
    Tcl_RegExp regExpr;
116
    char **argPtr, *string, *pattern, *start, *end;
117
    int match = 0;                       /* Initialization needed only to
118
                                         * prevent compiler warning. */
119
    int i;
120
    Tcl_DString stringDString, patternDString;
121
 
122
    if (argc < 3) {
123
        wrongNumArgs:
124
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
125
                " ?switches? exp string ?matchVar? ?subMatchVar ",
126
                "subMatchVar ...?\"", (char *) NULL);
127
        return TCL_ERROR;
128
    }
129
    argPtr = argv+1;
130
    argc--;
131
    while ((argc > 0) && (argPtr[0][0] == '-')) {
132
        if (strcmp(argPtr[0], "-indices") == 0) {
133
            indices = 1;
134
        } else if (strcmp(argPtr[0], "-nocase") == 0) {
135
            noCase = 1;
136
        } else if (strcmp(argPtr[0], "--") == 0) {
137
            argPtr++;
138
            argc--;
139
            break;
140
        } else {
141
            Tcl_AppendResult(interp, "bad switch \"", argPtr[0],
142
                    "\": must be -indices, -nocase, or --", (char *) NULL);
143
            return TCL_ERROR;
144
        }
145
        argPtr++;
146
        argc--;
147
    }
148
    if (argc < 2) {
149
        goto wrongNumArgs;
150
    }
151
 
152
    /*
153
     * Convert the string and pattern to lower case, if desired, and
154
     * perform the matching operation.
155
     */
156
 
157
    if (noCase) {
158
        register char *p;
159
 
160
        Tcl_DStringInit(&patternDString);
161
        Tcl_DStringAppend(&patternDString, argPtr[0], -1);
162
        pattern = Tcl_DStringValue(&patternDString);
163
        for (p = pattern; *p != 0; p++) {
164
            if (isupper(UCHAR(*p))) {
165
                *p = (char)tolower(UCHAR(*p));
166
            }
167
        }
168
        Tcl_DStringInit(&stringDString);
169
        Tcl_DStringAppend(&stringDString, argPtr[1], -1);
170
        string = Tcl_DStringValue(&stringDString);
171
        for (p = string; *p != 0; p++) {
172
            if (isupper(UCHAR(*p))) {
173
                *p = (char)tolower(UCHAR(*p));
174
            }
175
        }
176
    } else {
177
        pattern = argPtr[0];
178
        string = argPtr[1];
179
    }
180
    regExpr = Tcl_RegExpCompile(interp, pattern);
181
    if (regExpr != NULL) {
182
        match = Tcl_RegExpExec(interp, regExpr, string, string);
183
    }
184
    if (noCase) {
185
        Tcl_DStringFree(&stringDString);
186
        Tcl_DStringFree(&patternDString);
187
    }
188
    if (regExpr == NULL) {
189
        return TCL_ERROR;
190
    }
191
    if (match < 0) {
192
        return TCL_ERROR;
193
    }
194
    if (!match) {
195
        Tcl_SetResult(interp, "0", TCL_STATIC);
196
        return TCL_OK;
197
    }
198
 
199
    /*
200
     * If additional variable names have been specified, return
201
     * index information in those variables.
202
     */
203
 
204
    argc -= 2;
205
    for (i = 0; i < argc; i++) {
206
        char *result, info[50];
207
 
208
        Tcl_RegExpRange(regExpr, i, &start, &end);
209
        if (start == NULL) {
210
            if (indices) {
211
                result = Tcl_SetVar(interp, argPtr[i+2], "-1 -1", 0);
212
            } else {
213
                result = Tcl_SetVar(interp, argPtr[i+2], "", 0);
214
            }
215
        } else {
216
            if (indices) {
217
                sprintf(info, "%d %d", (int)(start - string),
218
                        (int)(end - string - 1));
219
                result = Tcl_SetVar(interp, argPtr[i+2], info, 0);
220
            } else {
221
                char savedChar, *first, *last;
222
 
223
                first = argPtr[1] + (start - string);
224
                last = argPtr[1] + (end - string);
225
                if (first == last) { /* don't modify argument */
226
                    result = Tcl_SetVar(interp, argPtr[i+2], "", 0);
227
                } else {
228
                    savedChar = *last;
229
                    *last = 0;
230
                    result = Tcl_SetVar(interp, argPtr[i+2], first, 0);
231
                    *last = savedChar;
232
                }
233
            }
234
        }
235
        if (result == NULL) {
236
            Tcl_AppendResult(interp, "couldn't set variable \"",
237
                    argPtr[i+2], "\"", (char *) NULL);
238
            return TCL_ERROR;
239
        }
240
    }
241
    Tcl_SetResult(interp, "1", TCL_STATIC);
242
    return TCL_OK;
243
}
244
 
245
/*
246
 *----------------------------------------------------------------------
247
 *
248
 * Tcl_RegsubCmd --
249
 *
250
 *      This procedure is invoked to process the "regsub" Tcl command.
251
 *      See the user documentation for details on what it does.
252
 *
253
 * Results:
254
 *      A standard Tcl result.
255
 *
256
 * Side effects:
257
 *      See the user documentation.
258
 *
259
 *----------------------------------------------------------------------
260
 */
261
 
262
        /* ARGSUSED */
263
int
264
Tcl_RegsubCmd(dummy, interp, argc, argv)
265
    ClientData dummy;                   /* Not used. */
266
    Tcl_Interp *interp;                 /* Current interpreter. */
267
    int argc;                           /* Number of arguments. */
268
    char **argv;                        /* Argument strings. */
269
{
270
    int noCase = 0, all = 0;
271
    Tcl_RegExp regExpr;
272
    char *string, *pattern, *p, *firstChar, **argPtr;
273
    int match, code, numMatches;
274
    char *start, *end, *subStart, *subEnd;
275
    register char *src, c;
276
    Tcl_DString stringDString, patternDString, resultDString;
277
 
278
    if (argc < 5) {
279
        wrongNumArgs:
280
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
281
                " ?switches? exp string subSpec varName\"", (char *) NULL);
282
        return TCL_ERROR;
283
    }
284
    argPtr = argv+1;
285
    argc--;
286
    while (argPtr[0][0] == '-') {
287
        if (strcmp(argPtr[0], "-nocase") == 0) {
288
            noCase = 1;
289
        } else if (strcmp(argPtr[0], "-all") == 0) {
290
            all = 1;
291
        } else if (strcmp(argPtr[0], "--") == 0) {
292
            argPtr++;
293
            argc--;
294
            break;
295
        } else {
296
            Tcl_AppendResult(interp, "bad switch \"", argPtr[0],
297
                    "\": must be -all, -nocase, or --", (char *) NULL);
298
            return TCL_ERROR;
299
        }
300
        argPtr++;
301
        argc--;
302
    }
303
    if (argc != 4) {
304
        goto wrongNumArgs;
305
    }
306
 
307
    /*
308
     * Convert the string and pattern to lower case, if desired.
309
     */
310
 
311
    if (noCase) {
312
        Tcl_DStringInit(&patternDString);
313
        Tcl_DStringAppend(&patternDString, argPtr[0], -1);
314
        pattern = Tcl_DStringValue(&patternDString);
315
        for (p = pattern; *p != 0; p++) {
316
            if (isupper(UCHAR(*p))) {
317
                *p = (char)tolower(UCHAR(*p));
318
            }
319
        }
320
        Tcl_DStringInit(&stringDString);
321
        Tcl_DStringAppend(&stringDString, argPtr[1], -1);
322
        string = Tcl_DStringValue(&stringDString);
323
        for (p = string; *p != 0; p++) {
324
            if (isupper(UCHAR(*p))) {
325
                *p = (char)tolower(UCHAR(*p));
326
            }
327
        }
328
    } else {
329
        pattern = argPtr[0];
330
        string = argPtr[1];
331
    }
332
    Tcl_DStringInit(&resultDString);
333
    regExpr = Tcl_RegExpCompile(interp, pattern);
334
    if (regExpr == NULL) {
335
        code = TCL_ERROR;
336
        goto done;
337
    }
338
 
339
    /*
340
     * The following loop is to handle multiple matches within the
341
     * same source string;  each iteration handles one match and its
342
     * corresponding substitution.  If "-all" hasn't been specified
343
     * then the loop body only gets executed once.
344
     */
345
 
346
    numMatches = 0;
347
    for (p = string; *p != 0; ) {
348
        match = Tcl_RegExpExec(interp, regExpr, p, string);
349
        if (match < 0) {
350
            code = TCL_ERROR;
351
            goto done;
352
        }
353
        if (!match) {
354
            break;
355
        }
356
        numMatches += 1;
357
 
358
        /*
359
         * Copy the portion of the source string before the match to the
360
         * result variable.
361
         */
362
 
363
        Tcl_RegExpRange(regExpr, 0, &start, &end);
364
        Tcl_DStringAppend(&resultDString, argPtr[1] + (p - string), start - p);
365
 
366
        /*
367
         * Append the subSpec argument to the variable, making appropriate
368
         * substitutions.  This code is a bit hairy because of the backslash
369
         * conventions and because the code saves up ranges of characters in
370
         * subSpec to reduce the number of calls to Tcl_SetVar.
371
         */
372
 
373
        for (src = firstChar = argPtr[2], c = *src; c != 0; src++, c = *src) {
374
            int index;
375
 
376
            if (c == '&') {
377
                index = 0;
378
            } else if (c == '\\') {
379
                c = src[1];
380
                if ((c >= '0') && (c <= '9')) {
381
                    index = c - '0';
382
                } else if ((c == '\\') || (c == '&')) {
383
                    *src = c;
384
                    src[1] = 0;
385
                    Tcl_DStringAppend(&resultDString, firstChar, -1);
386
                    *src = '\\';
387
                    src[1] = c;
388
                    firstChar = src+2;
389
                    src++;
390
                    continue;
391
                } else {
392
                    continue;
393
                }
394
            } else {
395
                continue;
396
            }
397
            if (firstChar != src) {
398
                c = *src;
399
                *src = 0;
400
                Tcl_DStringAppend(&resultDString, firstChar, -1);
401
                *src = c;
402
            }
403
            Tcl_RegExpRange(regExpr, index, &subStart, &subEnd);
404
            if ((subStart != NULL) && (subEnd != NULL)) {
405
                char *first, *last, saved;
406
 
407
                first = argPtr[1] + (subStart - string);
408
                last = argPtr[1] + (subEnd - string);
409
                saved = *last;
410
                *last = 0;
411
                Tcl_DStringAppend(&resultDString, first, -1);
412
                *last = saved;
413
            }
414
            if (*src == '\\') {
415
                src++;
416
            }
417
            firstChar = src+1;
418
        }
419
        if (firstChar != src) {
420
            Tcl_DStringAppend(&resultDString, firstChar, -1);
421
        }
422
        if (end == p) {
423
 
424
            /*
425
             * Always consume at least one character of the input string
426
             * in order to prevent infinite loops.
427
             */
428
 
429
            Tcl_DStringAppend(&resultDString, argPtr[1] + (p - string), 1);
430
            p = end + 1;
431
        } else {
432
            p = end;
433
        }
434
        if (!all) {
435
            break;
436
        }
437
    }
438
 
439
    /*
440
     * Copy the portion of the source string after the last match to the
441
     * result variable.
442
     */
443
 
444
    if ((*p != 0) || (numMatches == 0)) {
445
        Tcl_DStringAppend(&resultDString, argPtr[1] + (p - string), -1);
446
    }
447
    if (Tcl_SetVar(interp, argPtr[3], Tcl_DStringValue(&resultDString), 0)
448
             == NULL) {
449
        Tcl_AppendResult(interp,
450
                "couldn't set variable \"", argPtr[3], "\"",
451
                (char *) NULL);
452
        code = TCL_ERROR;
453
    } else {
454
        char buf[40];
455
 
456
        TclFormatInt(buf, numMatches);
457
        Tcl_SetResult(interp, buf, TCL_VOLATILE);
458
        code = TCL_OK;
459
    }
460
 
461
    done:
462
    if (noCase) {
463
        Tcl_DStringFree(&stringDString);
464
        Tcl_DStringFree(&patternDString);
465
    }
466
    Tcl_DStringFree(&resultDString);
467
    return code;
468
}
469
 
470
/*
471
 *----------------------------------------------------------------------
472
 *
473
 * Tcl_RenameObjCmd --
474
 *
475
 *      This procedure is invoked to process the "rename" Tcl command.
476
 *      See the user documentation for details on what it does.
477
 *
478
 * Results:
479
 *      A standard Tcl object result.
480
 *
481
 * Side effects:
482
 *      See the user documentation.
483
 *
484
 *----------------------------------------------------------------------
485
 */
486
 
487
        /* ARGSUSED */
488
int
489
Tcl_RenameObjCmd(dummy, interp, objc, objv)
490
    ClientData dummy;           /* Arbitrary value passed to the command. */
491
    Tcl_Interp *interp;         /* Current interpreter. */
492
    int objc;                   /* Number of arguments. */
493
    Tcl_Obj *CONST objv[];      /* Argument objects. */
494
{
495
    char *oldName, *newName;
496
 
497
    if (objc != 3) {
498
        Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
499
        return TCL_ERROR;
500
    }
501
 
502
    oldName = Tcl_GetStringFromObj(objv[1], (int *) NULL);
503
    newName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
504
    return TclRenameCommand(interp, oldName, newName);
505
}
506
 
507
/*
508
 *----------------------------------------------------------------------
509
 *
510
 * Tcl_ReturnObjCmd --
511
 *
512
 *      This object-based procedure is invoked to process the "return" Tcl
513
 *      command. See the user documentation for details on what it does.
514
 *
515
 * Results:
516
 *      A standard Tcl object result.
517
 *
518
 * Side effects:
519
 *      See the user documentation.
520
 *
521
 *----------------------------------------------------------------------
522
 */
523
 
524
        /* ARGSUSED */
525
int
526
Tcl_ReturnObjCmd(dummy, interp, objc, objv)
527
    ClientData dummy;           /* Not used. */
528
    Tcl_Interp *interp;         /* Current interpreter. */
529
    int objc;                   /* Number of arguments. */
530
    Tcl_Obj *CONST objv[];      /* Argument objects. */
531
{
532
    Interp *iPtr = (Interp *) interp;
533
    int optionLen, argLen, code, result;
534
 
535
    if (iPtr->errorInfo != NULL) {
536
        ckfree(iPtr->errorInfo);
537
        iPtr->errorInfo = NULL;
538
    }
539
    if (iPtr->errorCode != NULL) {
540
        ckfree(iPtr->errorCode);
541
        iPtr->errorCode = NULL;
542
    }
543
    code = TCL_OK;
544
 
545
   /*
546
    * THIS FAILS IF AN OBJECT CONTAINS AN EMBEDDED NULL.
547
    */
548
 
549
    for (objv++, objc--;  objc > 1;  objv += 2, objc -= 2) {
550
        char *option = Tcl_GetStringFromObj(objv[0], &optionLen);
551
        char *arg = Tcl_GetStringFromObj(objv[1], &argLen);
552
 
553
        if (strcmp(option, "-code") == 0) {
554
            register int c = arg[0];
555
            if ((c == 'o') && (strcmp(arg, "ok") == 0)) {
556
                code = TCL_OK;
557
            } else if ((c == 'e') && (strcmp(arg, "error") == 0)) {
558
                code = TCL_ERROR;
559
            } else if ((c == 'r') && (strcmp(arg, "return") == 0)) {
560
                code = TCL_RETURN;
561
            } else if ((c == 'b') && (strcmp(arg, "break") == 0)) {
562
                code = TCL_BREAK;
563
            } else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {
564
                code = TCL_CONTINUE;
565
            } else {
566
                result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],
567
                        &code);
568
                if (result != TCL_OK) {
569
                    Tcl_ResetResult(interp);
570
                    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
571
                            "bad completion code \"",
572
                            Tcl_GetStringFromObj(objv[1], (int *) NULL),
573
                            "\": must be ok, error, return, break, ",
574
                            "continue, or an integer", (char *) NULL);
575
                    return result;
576
                }
577
            }
578
        } else if (strcmp(option, "-errorinfo") == 0) {
579
            iPtr->errorInfo =
580
                (char *) ckalloc((unsigned) (strlen(arg) + 1));
581
            strcpy(iPtr->errorInfo, arg);
582
        } else if (strcmp(option, "-errorcode") == 0) {
583
            iPtr->errorCode =
584
                (char *) ckalloc((unsigned) (strlen(arg) + 1));
585
            strcpy(iPtr->errorCode, arg);
586
        } else {
587
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
588
                    "bad option \"", option,
589
                    "\": must be -code, -errorcode, or -errorinfo",
590
                    (char *) NULL);
591
            return TCL_ERROR;
592
        }
593
    }
594
 
595
    if (objc == 1) {
596
        /*
597
         * Set the interpreter's object result. An inline version of
598
         * Tcl_SetObjResult.
599
         */
600
 
601
        Tcl_SetObjResult(interp, objv[0]);
602
    }
603
    iPtr->returnCode = code;
604
    return TCL_RETURN;
605
}
606
 
607
/*
608
 *----------------------------------------------------------------------
609
 *
610
 * Tcl_ScanCmd --
611
 *
612
 *      This procedure is invoked to process the "scan" Tcl command.
613
 *      See the user documentation for details on what it does.
614
 *
615
 * Results:
616
 *      A standard Tcl result.
617
 *
618
 * Side effects:
619
 *      See the user documentation.
620
 *
621
 *----------------------------------------------------------------------
622
 */
623
 
624
        /* ARGSUSED */
625
int
626
Tcl_ScanCmd(dummy, interp, argc, argv)
627
    ClientData dummy;                   /* Not used. */
628
    Tcl_Interp *interp;                 /* Current interpreter. */
629
    int argc;                           /* Number of arguments. */
630
    char **argv;                        /* Argument strings. */
631
{
632
#   define MAX_FIELDS 20
633
    typedef struct {
634
        char fmt;                       /* Format for field. */
635
        int size;                       /* How many bytes to allow for
636
                                         * field. */
637
        char *location;                 /* Where field will be stored. */
638
    } Field;
639
    Field fields[MAX_FIELDS];           /* Info about all the fields in the
640
                                         * format string. */
641
    register Field *curField;
642
    int numFields = 0;                   /* Number of fields actually
643
                                         * specified. */
644
    int suppress;                       /* Current field is assignment-
645
                                         * suppressed. */
646
    int totalSize = 0;                   /* Number of bytes needed to store
647
                                         * all results combined. */
648
    char *results;                      /* Where scanned output goes.
649
                                         * Malloced; NULL means not allocated
650
                                         * yet. */
651
    int numScanned;                     /* sscanf's result. */
652
    register char *fmt;
653
    int i, widthSpecified, length, code;
654
    char buf[40];
655
 
656
    /*
657
     * The variables below are used to hold a copy of the format
658
     * string, so that we can replace format specifiers like "%f"
659
     * and "%F" with specifiers like "%lf"
660
     */
661
 
662
#   define STATIC_SIZE 5
663
    char copyBuf[STATIC_SIZE], *fmtCopy;
664
    register char *dst;
665
 
666
    if (argc < 3) {
667
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
668
                " string format ?varName varName ...?\"", (char *) NULL);
669
        return TCL_ERROR;
670
    }
671
 
672
    /*
673
     * This procedure operates in four stages:
674
     * 1. Scan the format string, collecting information about each field.
675
     * 2. Allocate an array to hold all of the scanned fields.
676
     * 3. Call sscanf to do all the dirty work, and have it store the
677
     *    parsed fields in the array.
678
     * 4. Pick off the fields from the array and assign them to variables.
679
     */
680
 
681
    code = TCL_OK;
682
    results = NULL;
683
    length = strlen(argv[2]) * 2 + 1;
684
    if (length < STATIC_SIZE) {
685
        fmtCopy = copyBuf;
686
    } else {
687
        fmtCopy = (char *) ckalloc((unsigned) length);
688
    }
689
    dst = fmtCopy;
690
    for (fmt = argv[2]; *fmt != 0; fmt++) {
691
        *dst = *fmt;
692
        dst++;
693
        if (*fmt != '%') {
694
            continue;
695
        }
696
        fmt++;
697
        if (*fmt == '%') {
698
            *dst = *fmt;
699
            dst++;
700
            continue;
701
        }
702
        if (*fmt == '*') {
703
            suppress = 1;
704
            *dst = *fmt;
705
            dst++;
706
            fmt++;
707
        } else {
708
            suppress = 0;
709
        }
710
        widthSpecified = 0;
711
        while (isdigit(UCHAR(*fmt))) {
712
            widthSpecified = 1;
713
            *dst = *fmt;
714
            dst++;
715
            fmt++;
716
        }
717
        if ((*fmt == 'l') || (*fmt == 'h') || (*fmt == 'L')) {
718
            fmt++;
719
        }
720
        *dst = *fmt;
721
        dst++;
722
        if (suppress) {
723
            continue;
724
        }
725
        if (numFields == MAX_FIELDS) {
726
            Tcl_SetResult(interp, "too many fields to scan", TCL_STATIC);
727
            code = TCL_ERROR;
728
            goto done;
729
        }
730
        curField = &fields[numFields];
731
        numFields++;
732
        switch (*fmt) {
733
            case 'd':
734
            case 'i':
735
            case 'o':
736
            case 'x':
737
                curField->fmt = 'd';
738
                curField->size = sizeof(int);
739
                break;
740
 
741
            case 'u':
742
                curField->fmt = 'u';
743
                curField->size = sizeof(int);
744
                break;
745
 
746
            case 's':
747
                curField->fmt = 's';
748
                curField->size = strlen(argv[1]) + 1;
749
                break;
750
 
751
            case 'c':
752
                if (widthSpecified) {
753
                    Tcl_SetResult(interp,
754
                            "field width may not be specified in %c conversion",
755
                            TCL_STATIC);
756
                    code = TCL_ERROR;
757
                    goto done;
758
                }
759
                curField->fmt = 'c';
760
                curField->size = sizeof(int);
761
                break;
762
 
763
            case 'e':
764
            case 'f':
765
            case 'g':
766
                dst[-1] = 'l';
767
                dst[0] = 'f';
768
                dst++;
769
                curField->fmt = 'f';
770
                curField->size = sizeof(double);
771
                break;
772
 
773
            case '[':
774
                curField->fmt = 's';
775
                curField->size = strlen(argv[1]) + 1;
776
                do {
777
                    fmt++;
778
                    if (*fmt == 0) {
779
                        Tcl_SetResult(interp,
780
                                "unmatched [ in format string", TCL_STATIC);
781
                        code = TCL_ERROR;
782
                        goto done;
783
                    }
784
                    *dst = *fmt;
785
                    dst++;
786
                } while (*fmt != ']');
787
                break;
788
 
789
            default:
790
                {
791
                    char buf[50];
792
 
793
                    sprintf(buf, "bad scan conversion character \"%c\"", *fmt);
794
                    Tcl_SetResult(interp, buf, TCL_VOLATILE);
795
                    code = TCL_ERROR;
796
                    goto done;
797
                }
798
        }
799
        curField->size = TCL_ALIGN(curField->size);
800
        totalSize += curField->size;
801
    }
802
    *dst = 0;
803
 
804
    if (numFields != (argc-3)) {
805
        Tcl_SetResult(interp,
806
                "different numbers of variable names and field specifiers",
807
                TCL_STATIC);
808
        code = TCL_ERROR;
809
        goto done;
810
    }
811
 
812
    /*
813
     * Step 2:
814
     */
815
 
816
    results = (char *) ckalloc((unsigned) totalSize);
817
    for (i = 0, totalSize = 0, curField = fields;
818
            i < numFields; i++, curField++) {
819
        curField->location = results + totalSize;
820
        totalSize += curField->size;
821
    }
822
 
823
    /*
824
     * Fill in the remaining fields with NULL;  the only purpose of
825
     * this is to keep some memory analyzers, like Purify, from
826
     * complaining.
827
     */
828
 
829
    for ( ; i < MAX_FIELDS; i++, curField++) {
830
        curField->location = NULL;
831
    }
832
 
833
    /*
834
     * Step 3:
835
     */
836
 
837
    numScanned = sscanf(argv[1], fmtCopy,
838
            fields[0].location, fields[1].location, fields[2].location,
839
            fields[3].location, fields[4].location, fields[5].location,
840
            fields[6].location, fields[7].location, fields[8].location,
841
            fields[9].location, fields[10].location, fields[11].location,
842
            fields[12].location, fields[13].location, fields[14].location,
843
            fields[15].location, fields[16].location, fields[17].location,
844
            fields[18].location, fields[19].location);
845
 
846
    /*
847
     * Step 4:
848
     */
849
 
850
    if (numScanned < numFields) {
851
        numFields = numScanned;
852
    }
853
    for (i = 0, curField = fields; i < numFields; i++, curField++) {
854
        switch (curField->fmt) {
855
            char string[TCL_DOUBLE_SPACE];
856
 
857
            case 'd':
858
                TclFormatInt(string, *((int *) curField->location));
859
                if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
860
                    storeError:
861
                    Tcl_AppendResult(interp,
862
                            "couldn't set variable \"", argv[i+3], "\"",
863
                            (char *) NULL);
864
                    code = TCL_ERROR;
865
                    goto done;
866
                }
867
                break;
868
 
869
            case 'u':
870
                sprintf(string, "%u", *((int *) curField->location));
871
                if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
872
                    goto storeError;
873
                }
874
                break;
875
 
876
            case 'c':
877
                TclFormatInt(string, *((char *) curField->location) & 0xff);
878
                if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
879
                    goto storeError;
880
                }
881
                break;
882
 
883
            case 's':
884
                if (Tcl_SetVar(interp, argv[i+3], curField->location, 0)
885
                        == NULL) {
886
                    goto storeError;
887
                }
888
                break;
889
 
890
            case 'f':
891
                Tcl_PrintDouble((Tcl_Interp *) NULL,
892
                        *((double *) curField->location), string);
893
                if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
894
                    goto storeError;
895
                }
896
                break;
897
        }
898
    }
899
    TclFormatInt(buf, numScanned);
900
    Tcl_SetResult(interp, buf, TCL_VOLATILE);
901
    done:
902
    if (results != NULL) {
903
        ckfree(results);
904
    }
905
    if (fmtCopy != copyBuf) {
906
        ckfree(fmtCopy);
907
    }
908
    return code;
909
}
910
 
911
/*
912
 *----------------------------------------------------------------------
913
 *
914
 * Tcl_SourceObjCmd --
915
 *
916
 *      This procedure is invoked to process the "source" Tcl command.
917
 *      See the user documentation for details on what it does.
918
 *
919
 * Results:
920
 *      A standard Tcl object result.
921
 *
922
 * Side effects:
923
 *      See the user documentation.
924
 *
925
 *----------------------------------------------------------------------
926
 */
927
 
928
        /* ARGSUSED */
929
int
930
Tcl_SourceObjCmd(dummy, interp, objc, objv)
931
    ClientData dummy;           /* Not used. */
932
    Tcl_Interp *interp;         /* Current interpreter. */
933
    int objc;                   /* Number of arguments. */
934
    Tcl_Obj *CONST objv[];      /* Argument objects. */
935
{
936
    char *bytes;
937
    int result;
938
 
939
    if (objc != 2) {
940
        Tcl_WrongNumArgs(interp, 1, objv, "fileName");
941
        return TCL_ERROR;
942
    }
943
 
944
    /*
945
     * THIS FAILS IF THE OBJECT'S STRING REP CONTAINS A NULL.
946
     */
947
 
948
    bytes = Tcl_GetStringFromObj(objv[1], (int *) NULL);
949
    result = Tcl_EvalFile(interp, bytes);
950
    return result;
951
}
952
 
953
/*
954
 *----------------------------------------------------------------------
955
 *
956
 * Tcl_SplitObjCmd --
957
 *
958
 *      This procedure is invoked to process the "split" Tcl command.
959
 *      See the user documentation for details on what it does.
960
 *
961
 * Results:
962
 *      A standard Tcl result.
963
 *
964
 * Side effects:
965
 *      See the user documentation.
966
 *
967
 *----------------------------------------------------------------------
968
 */
969
 
970
        /* ARGSUSED */
971
int
972
Tcl_SplitObjCmd(dummy, interp, objc, objv)
973
    ClientData dummy;           /* Not used. */
974
    Tcl_Interp *interp;         /* Current interpreter. */
975
    int objc;                   /* Number of arguments. */
976
    Tcl_Obj *CONST objv[];      /* Argument objects. */
977
{
978
    register char *p, *p2;
979
    char *splitChars, *string, *elementStart;
980
    int splitCharLen, stringLen, i, j;
981
    Tcl_Obj *listPtr;
982
 
983
    if (objc == 2) {
984
        splitChars = " \n\t\r";
985
        splitCharLen = 4;
986
    } else if (objc == 3) {
987
        splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);
988
    } else {
989
        Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");
990
        return TCL_ERROR;
991
    }
992
 
993
    string = Tcl_GetStringFromObj(objv[1], &stringLen);
994
    listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
995
 
996
    /*
997
     * Handle the special case of splitting on every character.
998
     */
999
 
1000
    if (splitCharLen == 0) {
1001
        for (i = 0, p = string;  i < stringLen;  i++, p++) {
1002
            Tcl_ListObjAppendElement(interp, listPtr,
1003
                    Tcl_NewStringObj(p, 1));
1004
        }
1005
    } else {
1006
        /*
1007
         * Normal case: split on any of a given set of characters.
1008
         * Discard instances of the split characters.
1009
         */
1010
 
1011
        for (i = 0, p = elementStart = string;  i < stringLen;  i++, p++) {
1012
            for (j = 0, p2 = splitChars;  j < splitCharLen;  j++, p2++) {
1013
                if (*p2 == *p) {
1014
                    Tcl_ListObjAppendElement(interp, listPtr,
1015
                            Tcl_NewStringObj(elementStart, (p-elementStart)));
1016
                    elementStart = p+1;
1017
                    break;
1018
                }
1019
            }
1020
        }
1021
        if (p != string) {
1022
            int remainingChars = stringLen - (elementStart-string);
1023
            Tcl_ListObjAppendElement(interp, listPtr,
1024
                    Tcl_NewStringObj(elementStart, remainingChars));
1025
        }
1026
    }
1027
 
1028
    Tcl_SetObjResult(interp, listPtr);
1029
    return TCL_OK;
1030
}
1031
 
1032
/*
1033
 *----------------------------------------------------------------------
1034
 *
1035
 * Tcl_StringObjCmd --
1036
 *
1037
 *      This procedure is invoked to process the "string" Tcl command.
1038
 *      See the user documentation for details on what it does.
1039
 *
1040
 * Results:
1041
 *      A standard Tcl result.
1042
 *
1043
 * Side effects:
1044
 *      See the user documentation.
1045
 *
1046
 *----------------------------------------------------------------------
1047
 */
1048
 
1049
        /* ARGSUSED */
1050
int
1051
Tcl_StringObjCmd(dummy, interp, objc, objv)
1052
    ClientData dummy;           /* Not used. */
1053
    Tcl_Interp *interp;         /* Current interpreter. */
1054
    int objc;                   /* Number of arguments. */
1055
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1056
{
1057
    int index, left, right;
1058
    Tcl_Obj *resultPtr;
1059
    char *string1, *string2;
1060
    int length1, length2;
1061
    static char *options[] = {
1062
        "compare",      "first",        "index",        "last",
1063
        "length",       "match",        "range",        "tolower",
1064
        "toupper",      "trim",         "trimleft",     "trimright",
1065
        "wordend",      "wordstart",    NULL
1066
    };
1067
    enum options {
1068
        STR_COMPARE,    STR_FIRST,      STR_INDEX,      STR_LAST,
1069
        STR_LENGTH,     STR_MATCH,      STR_RANGE,      STR_TOLOWER,
1070
        STR_TOUPPER,    STR_TRIM,       STR_TRIMLEFT,   STR_TRIMRIGHT,
1071
        STR_WORDEND,    STR_WORDSTART
1072
    };
1073
 
1074
    if (objc < 2) {
1075
        Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
1076
        return TCL_ERROR;
1077
    }
1078
 
1079
    if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
1080
            &index) != TCL_OK) {
1081
        return TCL_ERROR;
1082
    }
1083
 
1084
    resultPtr = Tcl_GetObjResult(interp);
1085
    switch ((enum options) index) {
1086
        case STR_COMPARE: {
1087
            int match, length;
1088
 
1089
            if (objc != 4) {
1090
                Tcl_WrongNumArgs(interp, 2, objv, "string1 string2");
1091
                return TCL_ERROR;
1092
            }
1093
 
1094
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1095
            string2 = Tcl_GetStringFromObj(objv[3], &length2);
1096
 
1097
            length = (length1 < length2) ? length1 : length2;
1098
            match = memcmp(string1, string2, (unsigned) length);
1099
            if (match == 0) {
1100
                match = length1 - length2;
1101
            }
1102
            Tcl_SetIntObj(resultPtr, (match > 0) ? 1 : (match < 0) ? -1 : 0);
1103
            break;
1104
        }
1105
        case STR_FIRST: {
1106
            register char *p, *end;
1107
            int match;
1108
 
1109
            if (objc != 4) {
1110
                badFirstLastArgs:
1111
                Tcl_WrongNumArgs(interp, 2, objv, "string1 string2");
1112
                return TCL_ERROR;
1113
            }
1114
 
1115
            match = -1;
1116
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1117
            string2 = Tcl_GetStringFromObj(objv[3], &length2);
1118
            if (length1 > 0) {
1119
                end = string2 + length2 - length1 + 1;
1120
                for (p = string2;  p < end;  p++) {
1121
                  /*
1122
                   * Scan forward to find the first character.
1123
                   */
1124
 
1125
                  p = memchr(p, *string1, (unsigned) (end - p));
1126
                  if (p == NULL) {
1127
                      break;
1128
                  }
1129
                  if (memcmp(string1, p, (unsigned) length1) == 0) {
1130
                      match = p - string2;
1131
                      break;
1132
                  }
1133
                }
1134
            }
1135
            Tcl_SetIntObj(resultPtr, match);
1136
            break;
1137
        }
1138
        case STR_INDEX: {
1139
            int index;
1140
 
1141
            if (objc != 4) {
1142
                Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
1143
                return TCL_ERROR;
1144
            }
1145
 
1146
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1147
            if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK) {
1148
                return TCL_ERROR;
1149
            }
1150
            if ((index >= 0) && (index < length1)) {
1151
                Tcl_SetStringObj(resultPtr, string1 + index, 1);
1152
            }
1153
            break;
1154
        }
1155
        case STR_LAST: {
1156
            register char *p;
1157
            int match;
1158
 
1159
            if (objc != 4) {
1160
                goto badFirstLastArgs;
1161
            }
1162
 
1163
            match = -1;
1164
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1165
            string2 = Tcl_GetStringFromObj(objv[3], &length2);
1166
            if (length1 > 0) {
1167
                for (p = string2 + length2 - length1;  p >= string2;  p--) {
1168
                    /*
1169
                     * Scan backwards to find the first character.
1170
                     */
1171
 
1172
                    while ((p != string2) && (*p != *string1)) {
1173
                        p--;
1174
                    }
1175
                    if (memcmp(string1, p, (unsigned) length1) == 0) {
1176
                        match = p - string2;
1177
                        break;
1178
                    }
1179
                }
1180
            }
1181
            Tcl_SetIntObj(resultPtr, match);
1182
            break;
1183
        }
1184
        case STR_LENGTH: {
1185
            if (objc != 3) {
1186
                Tcl_WrongNumArgs(interp, 2, objv, "string");
1187
                return TCL_ERROR;
1188
            }
1189
 
1190
            (void) Tcl_GetStringFromObj(objv[2], &length1);
1191
            Tcl_SetIntObj(resultPtr, length1);
1192
            break;
1193
        }
1194
        case STR_MATCH: {
1195
            if (objc != 4) {
1196
                Tcl_WrongNumArgs(interp, 2, objv, "pattern string");
1197
                return TCL_ERROR;
1198
            }
1199
 
1200
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1201
            string2 = Tcl_GetStringFromObj(objv[3], &length2);
1202
            Tcl_SetBooleanObj(resultPtr, Tcl_StringMatch(string2, string1));
1203
            break;
1204
        }
1205
        case STR_RANGE: {
1206
            int first, last;
1207
 
1208
            if (objc != 5) {
1209
                Tcl_WrongNumArgs(interp, 2, objv, "string first last");
1210
                return TCL_ERROR;
1211
            }
1212
 
1213
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1214
            if (TclGetIntForIndex(interp, objv[3], length1 - 1,
1215
                    &first) != TCL_OK) {
1216
                return TCL_ERROR;
1217
            }
1218
            if (TclGetIntForIndex(interp, objv[4], length1 - 1,
1219
                    &last) != TCL_OK) {
1220
                return TCL_ERROR;
1221
            }
1222
            if (first < 0) {
1223
                first = 0;
1224
            }
1225
            if (last >= length1 - 1) {
1226
                last = length1 - 1;
1227
            }
1228
            if (last >= first) {
1229
                Tcl_SetStringObj(resultPtr, string1 + first, last - first + 1);
1230
            }
1231
            break;
1232
        }
1233
        case STR_TOLOWER: {
1234
            register char *p, *end;
1235
 
1236
            if (objc != 3) {
1237
                Tcl_WrongNumArgs(interp, 2, objv, "string");
1238
                return TCL_ERROR;
1239
            }
1240
 
1241
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1242
 
1243
            /*
1244
             * Since I know resultPtr is not a shared object, I can reach
1245
             * in and diddle the bytes in its string rep to convert them in
1246
             * place to lower case.
1247
             */
1248
 
1249
            Tcl_SetStringObj(resultPtr, string1, length1);
1250
            string1 = Tcl_GetStringFromObj(resultPtr, &length1);
1251
            end = string1 + length1;
1252
            for (p = string1; p < end; p++) {
1253
                if (isupper(UCHAR(*p))) {
1254
                    *p = (char) tolower(UCHAR(*p));
1255
                }
1256
            }
1257
            break;
1258
        }
1259
        case STR_TOUPPER: {
1260
            register char *p, *end;
1261
 
1262
            if (objc != 3) {
1263
                Tcl_WrongNumArgs(interp, 2, objv, "string");
1264
                return TCL_ERROR;
1265
            }
1266
 
1267
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1268
 
1269
            /*
1270
             * Since I know resultPtr is not a shared object, I can reach
1271
             * in and diddle the bytes in its string rep to convert them in
1272
             * place to upper case.
1273
             */
1274
 
1275
            Tcl_SetStringObj(resultPtr, string1, length1);
1276
            string1 = Tcl_GetStringFromObj(resultPtr, &length1);
1277
            end = string1 + length1;
1278
            for (p = string1; p < end; p++) {
1279
                if (islower(UCHAR(*p))) {
1280
                    *p = (char) toupper(UCHAR(*p));
1281
                }
1282
            }
1283
            break;
1284
        }
1285
        case STR_TRIM: {
1286
            char ch;
1287
            register char *p, *end;
1288
            char *check, *checkEnd;
1289
 
1290
            left = 1;
1291
            right = 1;
1292
 
1293
            trim:
1294
            if (objc == 4) {
1295
                string2 = Tcl_GetStringFromObj(objv[3], &length2);
1296
            } else if (objc == 3) {
1297
                string2 = " \t\n\r";
1298
                length2 = strlen(string2);
1299
            } else {
1300
                Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");
1301
                return TCL_ERROR;
1302
            }
1303
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1304
            checkEnd = string2 + length2;
1305
 
1306
            if (left) {
1307
                end = string1 + length1;
1308
                for (p = string1; p < end; p++) {
1309
                    ch = *p;
1310
                    for (check = string2; ; check++) {
1311
                        if (check >= checkEnd) {
1312
                            p = end;
1313
                            break;
1314
                        }
1315
                        if (ch == *check) {
1316
                            length1--;
1317
                            string1++;
1318
                            break;
1319
                        }
1320
                    }
1321
                }
1322
            }
1323
            if (right) {
1324
                end = string1;
1325
                for (p = string1 + length1; p > end; ) {
1326
                    p--;
1327
                    ch = *p;
1328
                    for (check = string2; ; check++) {
1329
                        if (check >= checkEnd) {
1330
                            p = end;
1331
                            break;
1332
                        }
1333
                        if (ch == *check) {
1334
                            length1--;
1335
                            break;
1336
                        }
1337
                    }
1338
                }
1339
            }
1340
            Tcl_SetStringObj(resultPtr, string1, length1);
1341
            break;
1342
        }
1343
        case STR_TRIMLEFT: {
1344
            left = 1;
1345
            right = 0;
1346
            goto trim;
1347
        }
1348
        case STR_TRIMRIGHT: {
1349
            left = 0;
1350
            right = 1;
1351
            goto trim;
1352
        }
1353
        case STR_WORDEND: {
1354
            int cur, c;
1355
 
1356
            if (objc != 4) {
1357
                Tcl_WrongNumArgs(interp, 2, objv, "string index");
1358
                return TCL_ERROR;
1359
            }
1360
 
1361
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1362
            if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK) {
1363
                return TCL_ERROR;
1364
            }
1365
            if (index < 0) {
1366
                index = 0;
1367
            }
1368
            cur = length1;
1369
            if (index < length1) {
1370
                for (cur = index; cur < length1; cur++) {
1371
                    c = UCHAR(string1[cur]);
1372
                    if (!isalnum(c) && (c != '_')) {
1373
                        break;
1374
                    }
1375
                }
1376
                if (cur == index) {
1377
                    cur = index + 1;
1378
                }
1379
            }
1380
            Tcl_SetIntObj(resultPtr, cur);
1381
            break;
1382
        }
1383
        case STR_WORDSTART: {
1384
            int cur, c;
1385
 
1386
            if (objc != 4) {
1387
                Tcl_WrongNumArgs(interp, 2, objv, "string index");
1388
                return TCL_ERROR;
1389
            }
1390
 
1391
            string1 = Tcl_GetStringFromObj(objv[2], &length1);
1392
            if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK) {
1393
                return TCL_ERROR;
1394
            }
1395
            if (index >= length1) {
1396
                index = length1 - 1;
1397
            }
1398
            cur = 0;
1399
            if (index > 0) {
1400
                for (cur = index; cur >= 0; cur--) {
1401
                    c = UCHAR(string1[cur]);
1402
                    if (!isalnum(c) && (c != '_')) {
1403
                        break;
1404
                    }
1405
                }
1406
                if (cur != index) {
1407
                    cur += 1;
1408
                }
1409
            }
1410
            Tcl_SetIntObj(resultPtr, cur);
1411
            break;
1412
        }
1413
    }
1414
    return TCL_OK;
1415
}
1416
 
1417
/*
1418
 *----------------------------------------------------------------------
1419
 *
1420
 * Tcl_SubstCmd --
1421
 *
1422
 *      This procedure is invoked to process the "subst" Tcl command.
1423
 *      See the user documentation for details on what it does.  This
1424
 *      command is an almost direct copy of an implementation by
1425
 *      Andrew Payne.
1426
 *
1427
 * Results:
1428
 *      A standard Tcl result.
1429
 *
1430
 * Side effects:
1431
 *      See the user documentation.
1432
 *
1433
 *----------------------------------------------------------------------
1434
 */
1435
 
1436
        /* ARGSUSED */
1437
int
1438
Tcl_SubstCmd(dummy, interp, argc, argv)
1439
    ClientData dummy;                   /* Not used. */
1440
    Tcl_Interp *interp;                 /* Current interpreter. */
1441
    int argc;                           /* Number of arguments. */
1442
    char **argv;                        /* Argument strings. */
1443
{
1444
    Interp *iPtr = (Interp *) interp;
1445
    Tcl_DString result;
1446
    char *p, *old, *value;
1447
    int code, count, doVars, doCmds, doBackslashes, i;
1448
    size_t length;
1449
    char c;
1450
 
1451
    /*
1452
     * Parse command-line options.
1453
     */
1454
 
1455
    doVars = doCmds = doBackslashes = 1;
1456
    for (i = 1; i < (argc-1); i++) {
1457
        p = argv[i];
1458
        if (*p != '-') {
1459
            break;
1460
        }
1461
        length = strlen(p);
1462
        if (length < 4) {
1463
            badSwitch:
1464
            Tcl_AppendResult(interp, "bad switch \"", p,
1465
                    "\": must be -nobackslashes, -nocommands, ",
1466
                    "or -novariables", (char *) NULL);
1467
            return TCL_ERROR;
1468
        }
1469
        if ((p[3] == 'b') && (strncmp(p, "-nobackslashes", length) == 0)) {
1470
            doBackslashes = 0;
1471
        } else if ((p[3] == 'c') && (strncmp(p, "-nocommands", length) == 0)) {
1472
            doCmds = 0;
1473
        } else if ((p[3] == 'v') && (strncmp(p, "-novariables", length) == 0)) {
1474
            doVars = 0;
1475
        } else {
1476
            goto badSwitch;
1477
        }
1478
    }
1479
    if (i != (argc-1)) {
1480
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1481
                " ?-nobackslashes? ?-nocommands? ?-novariables? string\"",
1482
                (char *) NULL);
1483
        return TCL_ERROR;
1484
    }
1485
 
1486
    /*
1487
     * Scan through the string one character at a time, performing
1488
     * command, variable, and backslash substitutions.
1489
     */
1490
 
1491
    Tcl_DStringInit(&result);
1492
    old = p = argv[i];
1493
    while (*p != 0) {
1494
        switch (*p) {
1495
            case '\\':
1496
                if (doBackslashes) {
1497
                    if (p != old) {
1498
                        Tcl_DStringAppend(&result, old, p-old);
1499
                    }
1500
                    c = Tcl_Backslash(p, &count);
1501
                    Tcl_DStringAppend(&result, &c, 1);
1502
                    p += count;
1503
                    old = p;
1504
                } else {
1505
                    p++;
1506
                }
1507
                break;
1508
 
1509
            case '$':
1510
                if (doVars) {
1511
                    if (p != old) {
1512
                        Tcl_DStringAppend(&result, old, p-old);
1513
                    }
1514
                    value = Tcl_ParseVar(interp, p, &p);
1515
                    if (value == NULL) {
1516
                        Tcl_DStringFree(&result);
1517
                        return TCL_ERROR;
1518
                    }
1519
                    Tcl_DStringAppend(&result, value, -1);
1520
                    old = p;
1521
                } else {
1522
                    p++;
1523
                }
1524
                break;
1525
 
1526
            case '[':
1527
                if (doCmds) {
1528
                    if (p != old) {
1529
                        Tcl_DStringAppend(&result, old, p-old);
1530
                    }
1531
                    iPtr->evalFlags = TCL_BRACKET_TERM;
1532
                    code = Tcl_Eval(interp, p+1);
1533
                    if (code == TCL_ERROR) {
1534
                        Tcl_DStringFree(&result);
1535
                        return code;
1536
                    }
1537
                    old = p = (p+1 + iPtr->termOffset+1);
1538
                    Tcl_DStringAppend(&result, iPtr->result, -1);
1539
                    Tcl_ResetResult(interp);
1540
                } else {
1541
                    p++;
1542
                }
1543
                break;
1544
 
1545
            default:
1546
                p++;
1547
                break;
1548
        }
1549
    }
1550
    if (p != old) {
1551
        Tcl_DStringAppend(&result, old, p-old);
1552
    }
1553
    Tcl_DStringResult(interp, &result);
1554
    return TCL_OK;
1555
}
1556
 
1557
/*
1558
 *----------------------------------------------------------------------
1559
 *
1560
 * Tcl_SwitchObjCmd --
1561
 *
1562
 *      This object-based procedure is invoked to process the "switch" Tcl
1563
 *      command. See the user documentation for details on what it does.
1564
 *
1565
 * Results:
1566
 *      A standard Tcl object result.
1567
 *
1568
 * Side effects:
1569
 *      See the user documentation.
1570
 *
1571
 *----------------------------------------------------------------------
1572
 */
1573
 
1574
        /* ARGSUSED */
1575
int
1576
Tcl_SwitchObjCmd(dummy, interp, objc, objv)
1577
    ClientData dummy;           /* Not used. */
1578
    Tcl_Interp *interp;         /* Current interpreter. */
1579
    int objc;                   /* Number of arguments. */
1580
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1581
{
1582
#define EXACT   0
1583
#define GLOB    1
1584
#define REGEXP  2
1585
    int switchObjc, index;
1586
    Tcl_Obj *CONST *switchObjv;
1587
    Tcl_Obj *patternObj, *bodyObj;
1588
    char *string, *pattern, *body;
1589
    int splitObjs, length, patternLen, i, code, mode, matched, bodyIdx;
1590
    static char *switches[] =
1591
            {"-exact", "-glob", "-regexp", "--", (char *) NULL};
1592
 
1593
    switchObjc = objc-1;
1594
    switchObjv = objv+1;
1595
    mode = EXACT;
1596
 
1597
    while (switchObjc > 0) {
1598
        string = Tcl_GetStringFromObj(switchObjv[0], &length);
1599
        if (*string != '-') {
1600
            break;
1601
        }
1602
        if (Tcl_GetIndexFromObj(interp, switchObjv[0], switches,
1603
                "option", 0, &index) != TCL_OK) {
1604
            return TCL_ERROR;
1605
        }
1606
        switch (index) {
1607
            case 0:                      /* -exact */
1608
                mode = EXACT;
1609
                break;
1610
            case 1:                     /* -glob */
1611
                mode = GLOB;
1612
                break;
1613
            case 2:                     /* -regexp */
1614
                mode = REGEXP;
1615
                break;
1616
            case 3:                     /* -- */
1617
                switchObjc--;
1618
                switchObjv++;
1619
                goto doneWithSwitches;
1620
        }
1621
        switchObjc--;
1622
        switchObjv++;
1623
    }
1624
 
1625
    doneWithSwitches:
1626
    if (switchObjc < 2) {
1627
        Tcl_WrongNumArgs(interp, 1, objv,
1628
                "?switches? string pattern body ... ?default body?");
1629
        return TCL_ERROR;
1630
    }
1631
 
1632
    string = Tcl_GetStringFromObj(switchObjv[0], &length);
1633
    switchObjc--;
1634
    switchObjv++;
1635
 
1636
    /*
1637
     * If all of the pattern/command pairs are lumped into a single
1638
     * argument, split them out again.
1639
     */
1640
 
1641
    splitObjs = 0;
1642
    if (switchObjc == 1) {
1643
        code = Tcl_ListObjLength(interp, switchObjv[0], &switchObjc);
1644
        if (code != TCL_OK) {
1645
            return code;
1646
        }
1647
        splitObjs = 1;
1648
    }
1649
 
1650
    for (i = 0;  i < switchObjc;  i += 2) {
1651
        if (i == (switchObjc-1)) {
1652
            Tcl_ResetResult(interp);
1653
            Tcl_AppendToObj(Tcl_GetObjResult(interp),
1654
                    "extra switch pattern with no body", -1);
1655
            code = TCL_ERROR;
1656
            goto done;
1657
        }
1658
 
1659
        /*
1660
         * See if the pattern matches the string.
1661
         */
1662
 
1663
        if (splitObjs) {
1664
            code = Tcl_ListObjIndex(interp, switchObjv[0], i, &patternObj);
1665
            if (code != TCL_OK) {
1666
                return code;
1667
            }
1668
            pattern = Tcl_GetStringFromObj(patternObj, &patternLen);
1669
        } else {
1670
            pattern = Tcl_GetStringFromObj(switchObjv[i], &patternLen);
1671
        }
1672
 
1673
        matched = 0;
1674
        if ((*pattern == 'd') && (i == switchObjc-2)
1675
                && (strcmp(pattern, "default") == 0)) {
1676
            matched = 1;
1677
        } else {
1678
            /*
1679
             * THIS FAILS IF AN OBJECT'S STRING REP HAS A NULL.
1680
             */
1681
            switch (mode) {
1682
                case EXACT:
1683
                    matched = (strcmp(string, pattern) == 0);
1684
                    break;
1685
                case GLOB:
1686
                    matched = Tcl_StringMatch(string, pattern);
1687
                    break;
1688
                case REGEXP:
1689
                    matched = Tcl_RegExpMatch(interp, string, pattern);
1690
                    if (matched < 0) {
1691
                        code = TCL_ERROR;
1692
                        goto done;
1693
                    }
1694
                    break;
1695
            }
1696
        }
1697
        if (!matched) {
1698
            continue;
1699
        }
1700
 
1701
        /*
1702
         * We've got a match. Find a body to execute, skipping bodies
1703
         * that are "-".
1704
         */
1705
 
1706
        for (bodyIdx = i+1;  ;  bodyIdx += 2) {
1707
            if (bodyIdx >= switchObjc) {
1708
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1709
                        "no body specified for pattern \"", pattern,
1710
                        "\"", (char *) NULL);
1711
                code = TCL_ERROR;
1712
                goto done;
1713
            }
1714
 
1715
            if (splitObjs) {
1716
                code = Tcl_ListObjIndex(interp, switchObjv[0], bodyIdx,
1717
                        &bodyObj);
1718
                if (code != TCL_OK) {
1719
                    return code;
1720
                }
1721
            } else {
1722
                bodyObj = switchObjv[bodyIdx];
1723
            }
1724
            /*
1725
             * THIS FAILS IF AN OBJECT'S STRING REP HAS A NULL.
1726
             */
1727
            body = Tcl_GetStringFromObj(bodyObj, &length);
1728
            if ((length != 1) || (body[0] != '-')) {
1729
                break;
1730
            }
1731
        }
1732
        code = Tcl_EvalObj(interp, bodyObj);
1733
        if (code == TCL_ERROR) {
1734
            char msg[100];
1735
            sprintf(msg, "\n    (\"%.50s\" arm line %d)", pattern,
1736
                    interp->errorLine);
1737
            Tcl_AddObjErrorInfo(interp, msg, -1);
1738
        }
1739
        goto done;
1740
    }
1741
 
1742
    /*
1743
     * Nothing matched:  return nothing.
1744
     */
1745
 
1746
    code = TCL_OK;
1747
 
1748
    done:
1749
    return code;
1750
#undef EXACT
1751
#undef GLOB
1752
#undef REGEXP
1753
}
1754
 
1755
/*
1756
 *----------------------------------------------------------------------
1757
 *
1758
 * Tcl_TimeObjCmd --
1759
 *
1760
 *      This object-based procedure is invoked to process the "time" Tcl
1761
 *      command.  See the user documentation for details on what it does.
1762
 *
1763
 * Results:
1764
 *      A standard Tcl object result.
1765
 *
1766
 * Side effects:
1767
 *      See the user documentation.
1768
 *
1769
 *----------------------------------------------------------------------
1770
 */
1771
 
1772
        /* ARGSUSED */
1773
int
1774
Tcl_TimeObjCmd(dummy, interp, objc, objv)
1775
    ClientData dummy;           /* Not used. */
1776
    Tcl_Interp *interp;         /* Current interpreter. */
1777
    int objc;                   /* Number of arguments. */
1778
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1779
{
1780
    register Tcl_Obj *objPtr;
1781
    register int i, result;
1782
    int count;
1783
    double totalMicroSec;
1784
    Tcl_Time start, stop;
1785
    char buf[100];
1786
 
1787
    if (objc == 2) {
1788
        count = 1;
1789
    } else if (objc == 3) {
1790
        result = Tcl_GetIntFromObj(interp, objv[2], &count);
1791
        if (result != TCL_OK) {
1792
            return result;
1793
        }
1794
    } else {
1795
        Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");
1796
        return TCL_ERROR;
1797
    }
1798
 
1799
    objPtr = objv[1];
1800
    i = count;
1801
    TclpGetTime(&start);
1802
    while (i-- > 0) {
1803
        result = Tcl_EvalObj(interp, objPtr);
1804
        if (result != TCL_OK) {
1805
            return result;
1806
        }
1807
    }
1808
    TclpGetTime(&stop);
1809
 
1810
    totalMicroSec =
1811
        (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
1812
    sprintf(buf, "%.0f microseconds per iteration",
1813
        ((count <= 0) ? 0 : totalMicroSec/count));
1814
    Tcl_ResetResult(interp);
1815
    Tcl_AppendToObj(Tcl_GetObjResult(interp), buf, -1);
1816
    return TCL_OK;
1817
}
1818
 
1819
/*
1820
 *----------------------------------------------------------------------
1821
 *
1822
 * Tcl_TraceCmd --
1823
 *
1824
 *      This procedure is invoked to process the "trace" Tcl command.
1825
 *      See the user documentation for details on what it does.
1826
 *
1827
 * Results:
1828
 *      A standard Tcl result.
1829
 *
1830
 * Side effects:
1831
 *      See the user documentation.
1832
 *
1833
 *----------------------------------------------------------------------
1834
 */
1835
 
1836
        /* ARGSUSED */
1837
int
1838
Tcl_TraceCmd(dummy, interp, argc, argv)
1839
    ClientData dummy;                   /* Not used. */
1840
    Tcl_Interp *interp;                 /* Current interpreter. */
1841
    int argc;                           /* Number of arguments. */
1842
    char **argv;                        /* Argument strings. */
1843
{
1844
    int c;
1845
    size_t length;
1846
 
1847
    if (argc < 2) {
1848
        Tcl_AppendResult(interp, "too few args: should be \"",
1849
                argv[0], " option [arg arg ...]\"", (char *) NULL);
1850
        return TCL_ERROR;
1851
    }
1852
    c = argv[1][1];
1853
    length = strlen(argv[1]);
1854
    if ((c == 'a') && (strncmp(argv[1], "variable", length) == 0)
1855
            && (length >= 2)) {
1856
        char *p;
1857
        int flags, length;
1858
        TraceVarInfo *tvarPtr;
1859
 
1860
        if (argc != 5) {
1861
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1862
                    argv[0], " variable name ops command\"", (char *) NULL);
1863
            return TCL_ERROR;
1864
        }
1865
 
1866
        flags = 0;
1867
        for (p = argv[3] ; *p != 0; p++) {
1868
            if (*p == 'r') {
1869
                flags |= TCL_TRACE_READS;
1870
            } else if (*p == 'w') {
1871
                flags |= TCL_TRACE_WRITES;
1872
            } else if (*p == 'u') {
1873
                flags |= TCL_TRACE_UNSETS;
1874
            } else {
1875
                goto badOps;
1876
            }
1877
        }
1878
        if (flags == 0) {
1879
            goto badOps;
1880
        }
1881
 
1882
        length = strlen(argv[4]);
1883
        tvarPtr = (TraceVarInfo *) ckalloc((unsigned)
1884
                (sizeof(TraceVarInfo) - sizeof(tvarPtr->command) + length + 1));
1885
        tvarPtr->flags = flags;
1886
        tvarPtr->errMsg = NULL;
1887
        tvarPtr->length = length;
1888
        flags |= TCL_TRACE_UNSETS;
1889
        strcpy(tvarPtr->command, argv[4]);
1890
        if (Tcl_TraceVar(interp, argv[2], flags, TraceVarProc,
1891
                (ClientData) tvarPtr) != TCL_OK) {
1892
            ckfree((char *) tvarPtr);
1893
            return TCL_ERROR;
1894
        }
1895
    } else if ((c == 'd') && (strncmp(argv[1], "vdelete", length)
1896
            && (length >= 2)) == 0) {
1897
        char *p;
1898
        int flags, length;
1899
        TraceVarInfo *tvarPtr;
1900
        ClientData clientData;
1901
 
1902
        if (argc != 5) {
1903
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1904
                    argv[0], " vdelete name ops command\"", (char *) NULL);
1905
            return TCL_ERROR;
1906
        }
1907
 
1908
        flags = 0;
1909
        for (p = argv[3] ; *p != 0; p++) {
1910
            if (*p == 'r') {
1911
                flags |= TCL_TRACE_READS;
1912
            } else if (*p == 'w') {
1913
                flags |= TCL_TRACE_WRITES;
1914
            } else if (*p == 'u') {
1915
                flags |= TCL_TRACE_UNSETS;
1916
            } else {
1917
                goto badOps;
1918
            }
1919
        }
1920
        if (flags == 0) {
1921
            goto badOps;
1922
        }
1923
 
1924
        /*
1925
         * Search through all of our traces on this variable to
1926
         * see if there's one with the given command.  If so, then
1927
         * delete the first one that matches.
1928
         */
1929
 
1930
        length = strlen(argv[4]);
1931
        clientData = 0;
1932
        while ((clientData = Tcl_VarTraceInfo(interp, argv[2], 0,
1933
                TraceVarProc, clientData)) != 0) {
1934
            tvarPtr = (TraceVarInfo *) clientData;
1935
            if ((tvarPtr->length == length) && (tvarPtr->flags == flags)
1936
                    && (strncmp(argv[4], tvarPtr->command,
1937
                    (size_t) length) == 0)) {
1938
                Tcl_UntraceVar(interp, argv[2], flags | TCL_TRACE_UNSETS,
1939
                        TraceVarProc, clientData);
1940
                if (tvarPtr->errMsg != NULL) {
1941
                    ckfree(tvarPtr->errMsg);
1942
                }
1943
                ckfree((char *) tvarPtr);
1944
                break;
1945
            }
1946
        }
1947
    } else if ((c == 'i') && (strncmp(argv[1], "vinfo", length) == 0)
1948
            && (length >= 2)) {
1949
        ClientData clientData;
1950
        char ops[4], *p;
1951
        char *prefix = "{";
1952
 
1953
        if (argc != 3) {
1954
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1955
                    argv[0], " vinfo name\"", (char *) NULL);
1956
            return TCL_ERROR;
1957
        }
1958
        clientData = 0;
1959
        while ((clientData = Tcl_VarTraceInfo(interp, argv[2], 0,
1960
                TraceVarProc, clientData)) != 0) {
1961
            TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
1962
            p = ops;
1963
            if (tvarPtr->flags & TCL_TRACE_READS) {
1964
                *p = 'r';
1965
                p++;
1966
            }
1967
            if (tvarPtr->flags & TCL_TRACE_WRITES) {
1968
                *p = 'w';
1969
                p++;
1970
            }
1971
            if (tvarPtr->flags & TCL_TRACE_UNSETS) {
1972
                *p = 'u';
1973
                p++;
1974
            }
1975
            *p = '\0';
1976
            Tcl_AppendResult(interp, prefix, (char *) NULL);
1977
            Tcl_AppendElement(interp, ops);
1978
            Tcl_AppendElement(interp, tvarPtr->command);
1979
            Tcl_AppendResult(interp, "}", (char *) NULL);
1980
            prefix = " {";
1981
        }
1982
    } else {
1983
        Tcl_AppendResult(interp, "bad option \"", argv[1],
1984
                "\": should be variable, vdelete, or vinfo",
1985
                (char *) NULL);
1986
        return TCL_ERROR;
1987
    }
1988
    return TCL_OK;
1989
 
1990
    badOps:
1991
    Tcl_AppendResult(interp, "bad operations \"", argv[3],
1992
            "\": should be one or more of rwu", (char *) NULL);
1993
    return TCL_ERROR;
1994
}
1995
 
1996
/*
1997
 *----------------------------------------------------------------------
1998
 *
1999
 * TraceVarProc --
2000
 *
2001
 *      This procedure is called to handle variable accesses that have
2002
 *      been traced using the "trace" command.
2003
 *
2004
 * Results:
2005
 *      Normally returns NULL.  If the trace command returns an error,
2006
 *      then this procedure returns an error string.
2007
 *
2008
 * Side effects:
2009
 *      Depends on the command associated with the trace.
2010
 *
2011
 *----------------------------------------------------------------------
2012
 */
2013
 
2014
        /* ARGSUSED */
2015
static char *
2016
TraceVarProc(clientData, interp, name1, name2, flags)
2017
    ClientData clientData;      /* Information about the variable trace. */
2018
    Tcl_Interp *interp;         /* Interpreter containing variable. */
2019
    char *name1;                /* Name of variable or array. */
2020
    char *name2;                /* Name of element within array;  NULL means
2021
                                 * scalar variable is being referenced. */
2022
    int flags;                  /* OR-ed bits giving operation and other
2023
                                 * information. */
2024
{
2025
    Interp *iPtr = (Interp *) interp;
2026
    TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
2027
    char *result;
2028
    int code;
2029
    Interp dummy;
2030
    Tcl_DString cmd;
2031
    Tcl_Obj *saveObjPtr, *oldObjResultPtr;
2032
 
2033
    result = NULL;
2034
    if (tvarPtr->errMsg != NULL) {
2035
        ckfree(tvarPtr->errMsg);
2036
        tvarPtr->errMsg = NULL;
2037
    }
2038
    if ((tvarPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
2039
 
2040
        /*
2041
         * Generate a command to execute by appending list elements
2042
         * for the two variable names and the operation.  The five
2043
         * extra characters are for three space, the opcode character,
2044
         * and the terminating null.
2045
         */
2046
 
2047
        if (name2 == NULL) {
2048
            name2 = "";
2049
        }
2050
        Tcl_DStringInit(&cmd);
2051
        Tcl_DStringAppend(&cmd, tvarPtr->command, tvarPtr->length);
2052
        Tcl_DStringAppendElement(&cmd, name1);
2053
        Tcl_DStringAppendElement(&cmd, name2);
2054
        if (flags & TCL_TRACE_READS) {
2055
            Tcl_DStringAppend(&cmd, " r", 2);
2056
        } else if (flags & TCL_TRACE_WRITES) {
2057
            Tcl_DStringAppend(&cmd, " w", 2);
2058
        } else if (flags & TCL_TRACE_UNSETS) {
2059
            Tcl_DStringAppend(&cmd, " u", 2);
2060
        }
2061
 
2062
        /*
2063
         * Execute the command.  Be careful to save and restore both the
2064
         * string and object results from the interpreter used for
2065
         * the command. We discard any object result the command returns.
2066
         */
2067
 
2068
        dummy.objResultPtr = Tcl_NewObj();
2069
        Tcl_IncrRefCount(dummy.objResultPtr);
2070
        if (interp->freeProc == 0) {
2071
            dummy.freeProc = (Tcl_FreeProc *) 0;
2072
            dummy.result = "";
2073
            Tcl_SetResult((Tcl_Interp *) &dummy, interp->result,
2074
                    TCL_VOLATILE);
2075
        } else {
2076
            dummy.freeProc = interp->freeProc;
2077
            dummy.result = interp->result;
2078
            interp->freeProc = (Tcl_FreeProc *) 0;
2079
        }
2080
 
2081
        saveObjPtr = Tcl_GetObjResult(interp);
2082
        Tcl_IncrRefCount(saveObjPtr);
2083
 
2084
        code = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
2085
        if (code != TCL_OK) {        /* copy error msg to result */
2086
            tvarPtr->errMsg = (char *)
2087
                    ckalloc((unsigned) (strlen(interp->result) + 1));
2088
            strcpy(tvarPtr->errMsg, interp->result);
2089
            result = tvarPtr->errMsg;
2090
            Tcl_ResetResult(interp); /* must clear error state. */
2091
        }
2092
 
2093
        /*
2094
         * Restore the interpreter's string result.
2095
         */
2096
 
2097
        Tcl_SetResult(interp, dummy.result,
2098
                (dummy.freeProc == 0) ? TCL_VOLATILE : dummy.freeProc);
2099
 
2100
        /*
2101
         * Restore the interpreter's object result from saveObjPtr.
2102
         */
2103
 
2104
        oldObjResultPtr = iPtr->objResultPtr;
2105
        iPtr->objResultPtr = saveObjPtr;  /* was incremented above */
2106
        Tcl_DecrRefCount(oldObjResultPtr);
2107
 
2108
        Tcl_DecrRefCount(dummy.objResultPtr);
2109
        dummy.objResultPtr = NULL;
2110
        Tcl_DStringFree(&cmd);
2111
    }
2112
    if (flags & TCL_TRACE_DESTROYED) {
2113
        result = NULL;
2114
        if (tvarPtr->errMsg != NULL) {
2115
            ckfree(tvarPtr->errMsg);
2116
        }
2117
        ckfree((char *) tvarPtr);
2118
    }
2119
    return result;
2120
}
2121
 
2122
/*
2123
 *----------------------------------------------------------------------
2124
 *
2125
 * Tcl_WhileCmd --
2126
 *
2127
 *      This procedure is invoked to process the "while" Tcl command.
2128
 *      See the user documentation for details on what it does.
2129
 *
2130
 *      With the bytecode compiler, this procedure is only called when
2131
 *      a command name is computed at runtime, and is "while" or the name
2132
 *      to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
2133
 *
2134
 * Results:
2135
 *      A standard Tcl result.
2136
 *
2137
 * Side effects:
2138
 *      See the user documentation.
2139
 *
2140
 *----------------------------------------------------------------------
2141
 */
2142
 
2143
        /* ARGSUSED */
2144
int
2145
Tcl_WhileCmd(dummy, interp, argc, argv)
2146
    ClientData dummy;                   /* Not used. */
2147
    Tcl_Interp *interp;                 /* Current interpreter. */
2148
    int argc;                           /* Number of arguments. */
2149
    char **argv;                        /* Argument strings. */
2150
{
2151
    int result, value;
2152
 
2153
    if (argc != 3) {
2154
        Tcl_AppendResult(interp, "wrong # args: should be \"",
2155
                argv[0], " test command\"", (char *) NULL);
2156
        return TCL_ERROR;
2157
    }
2158
 
2159
    while (1) {
2160
        result = Tcl_ExprBoolean(interp, argv[1], &value);
2161
        if (result != TCL_OK) {
2162
            return result;
2163
        }
2164
        if (!value) {
2165
            break;
2166
        }
2167
        result = Tcl_Eval(interp, argv[2]);
2168
        if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
2169
            if (result == TCL_ERROR) {
2170
                char msg[60];
2171
                sprintf(msg, "\n    (\"while\" body line %d)",
2172
                        interp->errorLine);
2173
                Tcl_AddErrorInfo(interp, msg);
2174
            }
2175
            break;
2176
        }
2177
    }
2178
    if (result == TCL_BREAK) {
2179
        result = TCL_OK;
2180
    }
2181
    if (result == TCL_OK) {
2182
        Tcl_ResetResult(interp);
2183
    }
2184
    return result;
2185
}
2186
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.