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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [generic/] [tclCmdAH.c] - Blame information for rev 1774

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

Line No. Rev Author Line
1 578 markom
/*
2
 * tclCmdAH.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
 *      A to H.
7
 *
8
 * Copyright (c) 1987-1993 The Regents of the University of California.
9
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10
 *
11
 * See the file "license.terms" for information on usage and redistribution
12
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
 *
14
 * RCS: @(#) $Id: tclCmdAH.c,v 1.1.1.1 2002-01-16 10:25:25 markom Exp $
15
 */
16
 
17
#include "tclInt.h"
18
#include "tclPort.h"
19
 
20
/*
21
 * Prototypes for local procedures defined in this file:
22
 */
23
 
24
static char *           GetTypeFromMode _ANSI_ARGS_((int mode));
25
static int              StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
26
                            char *varName, struct stat *statPtr));
27
 
28
/*
29
 *----------------------------------------------------------------------
30
 *
31
 * Tcl_BreakCmd --
32
 *
33
 *      This procedure is invoked to process the "break" Tcl command.
34
 *      See the user documentation for details on what it does.
35
 *
36
 *      With the bytecode compiler, this procedure is only called when
37
 *      a command name is computed at runtime, and is "break" or the name
38
 *      to which "break" was renamed: e.g., "set z break; $z"
39
 *
40
 * Results:
41
 *      A standard Tcl result.
42
 *
43
 * Side effects:
44
 *      See the user documentation.
45
 *
46
 *----------------------------------------------------------------------
47
 */
48
 
49
        /* ARGSUSED */
50
int
51
Tcl_BreakCmd(dummy, interp, argc, argv)
52
    ClientData dummy;                   /* Not used. */
53
    Tcl_Interp *interp;                 /* Current interpreter. */
54
    int argc;                           /* Number of arguments. */
55
    char **argv;                        /* Argument strings. */
56
{
57
    if (argc != 1) {
58
        Tcl_AppendResult(interp, "wrong # args: should be \"",
59
                argv[0], "\"", (char *) NULL);
60
        return TCL_ERROR;
61
    }
62
    return TCL_BREAK;
63
}
64
 
65
/*
66
 *----------------------------------------------------------------------
67
 *
68
 * Tcl_CaseObjCmd --
69
 *
70
 *      This procedure is invoked to process the "case" Tcl command.
71
 *      See the user documentation for details on what it does.
72
 *
73
 * Results:
74
 *      A standard Tcl object result.
75
 *
76
 * Side effects:
77
 *      See the user documentation.
78
 *
79
 *----------------------------------------------------------------------
80
 */
81
 
82
        /* ARGSUSED */
83
int
84
Tcl_CaseObjCmd(dummy, interp, objc, objv)
85
    ClientData dummy;           /* Not used. */
86
    Tcl_Interp *interp;         /* Current interpreter. */
87
    int objc;                   /* Number of arguments. */
88
    Tcl_Obj *CONST objv[];      /* Argument objects. */
89
{
90
    register int i;
91
    int body, result;
92
    char *string, *arg;
93
    int argLen, caseObjc;
94
    Tcl_Obj *CONST *caseObjv;
95
    Tcl_Obj *armPtr;
96
 
97
    if (objc < 3) {
98
        Tcl_WrongNumArgs(interp, 1, objv,
99
                "string ?in? patList body ... ?default body?");
100
        return TCL_ERROR;
101
    }
102
 
103
    /*
104
     * THIS FAILS IF AN OBJECT'S STRING REP HAS A NULL BYTE.
105
     */
106
 
107
    string = Tcl_GetStringFromObj(objv[1], &argLen);
108
    body = -1;
109
 
110
    arg = Tcl_GetStringFromObj(objv[2], &argLen);
111
    if (strcmp(arg, "in") == 0) {
112
        i = 3;
113
    } else {
114
        i = 2;
115
    }
116
    caseObjc = objc - i;
117
    caseObjv = objv + i;
118
 
119
    /*
120
     * If all of the pattern/command pairs are lumped into a single
121
     * argument, split them out again.
122
     * THIS FAILS IF THE ARG'S STRING REP CONTAINS A NULL
123
     */
124
 
125
    if (caseObjc == 1) {
126
        Tcl_Obj **newObjv;
127
 
128
        Tcl_ListObjGetElements(interp, caseObjv[0], &caseObjc, &newObjv);
129
        caseObjv = newObjv;
130
    }
131
 
132
    for (i = 0;  i < caseObjc;  i += 2) {
133
        int patObjc, j;
134
        char **patObjv;
135
        char *pat;
136
        register char *p;
137
 
138
        if (i == (caseObjc-1)) {
139
            Tcl_ResetResult(interp);
140
            Tcl_AppendToObj(Tcl_GetObjResult(interp),
141
                    "extra case pattern with no body", -1);
142
            return TCL_ERROR;
143
        }
144
 
145
        /*
146
         * Check for special case of single pattern (no list) with
147
         * no backslash sequences.
148
         */
149
 
150
        pat = Tcl_GetStringFromObj(caseObjv[i], &argLen);
151
        for (p = pat;  *p != 0;  p++) {  /* FAILS IF NULL BYTE */
152
            if (isspace(UCHAR(*p)) || (*p == '\\')) {
153
                break;
154
            }
155
        }
156
        if (*p == 0) {
157
            if ((*pat == 'd') && (strcmp(pat, "default") == 0)) {
158
                body = i+1;
159
            }
160
            if (Tcl_StringMatch(string, pat)) {
161
                body = i+1;
162
                goto match;
163
            }
164
            continue;
165
        }
166
 
167
 
168
        /*
169
         * Break up pattern lists, then check each of the patterns
170
         * in the list.
171
         */
172
 
173
        result = Tcl_SplitList(interp, pat, &patObjc, &patObjv);
174
        if (result != TCL_OK) {
175
            return result;
176
        }
177
        for (j = 0; j < patObjc; j++) {
178
            if (Tcl_StringMatch(string, patObjv[j])) {
179
                body = i+1;
180
                break;
181
            }
182
        }
183
        ckfree((char *) patObjv);
184
        if (j < patObjc) {
185
            break;
186
        }
187
    }
188
 
189
    match:
190
    if (body != -1) {
191
        armPtr = caseObjv[body-1];
192
        result = Tcl_EvalObj(interp, caseObjv[body]);
193
        if (result == TCL_ERROR) {
194
            char msg[100];
195
 
196
            arg = Tcl_GetStringFromObj(armPtr, &argLen);
197
            sprintf(msg, "\n    (\"%.*s\" arm line %d)", argLen, arg,
198
                    interp->errorLine);
199
            Tcl_AddObjErrorInfo(interp, msg, -1);
200
        }
201
        return result;
202
    }
203
 
204
    /*
205
     * Nothing matched: return nothing.
206
     */
207
 
208
    return TCL_OK;
209
}
210
 
211
/*
212
 *----------------------------------------------------------------------
213
 *
214
 * Tcl_CatchObjCmd --
215
 *
216
 *      This object-based procedure is invoked to process the "catch" Tcl
217
 *      command. See the user documentation for details on what it does.
218
 *
219
 * Results:
220
 *      A standard Tcl object result.
221
 *
222
 * Side effects:
223
 *      See the user documentation.
224
 *
225
 *----------------------------------------------------------------------
226
 */
227
 
228
        /* ARGSUSED */
229
int
230
Tcl_CatchObjCmd(dummy, interp, objc, objv)
231
    ClientData dummy;           /* Not used. */
232
    Tcl_Interp *interp;         /* Current interpreter. */
233
    int objc;                   /* Number of arguments. */
234
    Tcl_Obj *CONST objv[];      /* Argument objects. */
235
{
236
    Tcl_Obj *varNamePtr = NULL;
237
    int result;
238
 
239
    if ((objc != 2) && (objc != 3)) {
240
        Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?");
241
        return TCL_ERROR;
242
    }
243
 
244
    /*
245
     * Save a pointer to the variable name object, if any, in case the
246
     * Tcl_EvalObj reallocates the bytecode interpreter's evaluation
247
     * stack rendering objv invalid.
248
     */
249
 
250
    if (objc == 3) {
251
        varNamePtr = objv[2];
252
    }
253
 
254
    result = Tcl_EvalObj(interp, objv[1]);
255
 
256
    if (objc == 3) {
257
        if (Tcl_ObjSetVar2(interp, varNamePtr, NULL,
258
                    Tcl_GetObjResult(interp), TCL_PARSE_PART1) == NULL) {
259
            Tcl_ResetResult(interp);
260
            Tcl_AppendToObj(Tcl_GetObjResult(interp),
261
                    "couldn't save command result in variable", -1);
262
            return TCL_ERROR;
263
        }
264
    }
265
 
266
    /*
267
     * Set the interpreter's object result to an integer object holding the
268
     * integer Tcl_EvalObj result. Note that we don't bother generating a
269
     * string representation. We reset the interpreter's object result
270
     * to an unshared empty object and then set it to be an integer object.
271
     */
272
 
273
    Tcl_ResetResult(interp);
274
    Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
275
    return TCL_OK;
276
}
277
 
278
/*
279
 *----------------------------------------------------------------------
280
 *
281
 * Tcl_CdObjCmd --
282
 *
283
 *      This procedure is invoked to process the "cd" Tcl command.
284
 *      See the user documentation for details on what it does.
285
 *
286
 * Results:
287
 *      A standard Tcl result.
288
 *
289
 * Side effects:
290
 *      See the user documentation.
291
 *
292
 *----------------------------------------------------------------------
293
 */
294
 
295
        /* ARGSUSED */
296
int
297
Tcl_CdObjCmd(dummy, interp, objc, objv)
298
    ClientData dummy;           /* Not used. */
299
    Tcl_Interp *interp;         /* Current interpreter. */
300
    int objc;                   /* Number of arguments. */
301
    Tcl_Obj *CONST objv[];      /* Argument objects. */
302
{
303
    char *dirName;
304
    int dirLength;
305
    Tcl_DString buffer;
306
    int result;
307
 
308
    if (objc > 2) {
309
        Tcl_WrongNumArgs(interp, 1, objv, "dirName");
310
        return TCL_ERROR;
311
    }
312
 
313
    if (objc == 2) {
314
        dirName = Tcl_GetStringFromObj(objv[1], &dirLength);
315
    } else {
316
        dirName = "~";
317
    }
318
    dirName = Tcl_TranslateFileName(interp, dirName, &buffer);
319
    if (dirName == NULL) {
320
        return TCL_ERROR;
321
    }
322
    result = TclChdir(interp, dirName);
323
    Tcl_DStringFree(&buffer);
324
    return result;
325
}
326
 
327
/*
328
 *----------------------------------------------------------------------
329
 *
330
 * Tcl_ConcatObjCmd --
331
 *
332
 *      This object-based procedure is invoked to process the "concat" Tcl
333
 *      command. See the user documentation for details on what it does/
334
 *
335
 * Results:
336
 *      A standard Tcl object result.
337
 *
338
 * Side effects:
339
 *      See the user documentation.
340
 *
341
 *----------------------------------------------------------------------
342
 */
343
 
344
        /* ARGSUSED */
345
int
346
Tcl_ConcatObjCmd(dummy, interp, objc, objv)
347
    ClientData dummy;           /* Not used. */
348
    Tcl_Interp *interp;         /* Current interpreter. */
349
    int objc;                   /* Number of arguments. */
350
    Tcl_Obj *CONST objv[];      /* Argument objects. */
351
{
352
    if (objc >= 2) {
353
        Tcl_SetObjResult(interp, Tcl_ConcatObj(objc-1, objv+1));
354
    }
355
    return TCL_OK;
356
}
357
 
358
/*
359
 *----------------------------------------------------------------------
360
 *
361
 * Tcl_ContinueCmd -
362
 *
363
 *      This procedure is invoked to process the "continue" Tcl command.
364
 *      See the user documentation for details on what it does.
365
 *
366
 *      With the bytecode compiler, this procedure is only called when
367
 *      a command name is computed at runtime, and is "continue" or the name
368
 *      to which "continue" was renamed: e.g., "set z continue; $z"
369
 *
370
 * Results:
371
 *      A standard Tcl result.
372
 *
373
 * Side effects:
374
 *      See the user documentation.
375
 *
376
 *----------------------------------------------------------------------
377
 */
378
 
379
        /* ARGSUSED */
380
int
381
Tcl_ContinueCmd(dummy, interp, argc, argv)
382
    ClientData dummy;                   /* Not used. */
383
    Tcl_Interp *interp;                 /* Current interpreter. */
384
    int argc;                           /* Number of arguments. */
385
    char **argv;                        /* Argument strings. */
386
{
387
    if (argc != 1) {
388
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
389
                "\"", (char *) NULL);
390
        return TCL_ERROR;
391
    }
392
    return TCL_CONTINUE;
393
}
394
 
395
/*
396
 *----------------------------------------------------------------------
397
 *
398
 * Tcl_ErrorObjCmd --
399
 *
400
 *      This procedure is invoked to process the "error" Tcl command.
401
 *      See the user documentation for details on what it does.
402
 *
403
 * Results:
404
 *      A standard Tcl object result.
405
 *
406
 * Side effects:
407
 *      See the user documentation.
408
 *
409
 *----------------------------------------------------------------------
410
 */
411
 
412
        /* ARGSUSED */
413
int
414
Tcl_ErrorObjCmd(dummy, interp, objc, objv)
415
    ClientData dummy;           /* Not used. */
416
    Tcl_Interp *interp;         /* Current interpreter. */
417
    int objc;                   /* Number of arguments. */
418
    Tcl_Obj *CONST objv[];      /* Argument objects. */
419
{
420
    Interp *iPtr = (Interp *) interp;
421
    register Tcl_Obj *namePtr;
422
    char *info;
423
    int infoLen;
424
 
425
    if ((objc < 2) || (objc > 4)) {
426
        Tcl_WrongNumArgs(interp, 1, objv, "message ?errorInfo? ?errorCode?");
427
        return TCL_ERROR;
428
    }
429
 
430
    if (objc >= 3) {            /* process the optional info argument */
431
        info = Tcl_GetStringFromObj(objv[2], &infoLen);
432
        if (*info != 0) {
433
            Tcl_AddObjErrorInfo(interp, info, infoLen);
434
            iPtr->flags |= ERR_ALREADY_LOGGED;
435
        }
436
    }
437
 
438
    if (objc == 4) {
439
        namePtr = Tcl_NewStringObj("errorCode", -1);
440
        Tcl_ObjSetVar2(interp, namePtr, (Tcl_Obj *) NULL, objv[3],
441
                TCL_GLOBAL_ONLY);
442
        iPtr->flags |= ERROR_CODE_SET;
443
        Tcl_DecrRefCount(namePtr); /* we're done with name object */
444
    }
445
 
446
    Tcl_SetObjResult(interp, objv[1]);
447
    return TCL_ERROR;
448
}
449
 
450
/*
451
 *----------------------------------------------------------------------
452
 *
453
 * Tcl_EvalObjCmd --
454
 *
455
 *      This object-based procedure is invoked to process the "eval" Tcl
456
 *      command. See the user documentation for details on what it does.
457
 *
458
 * Results:
459
 *      A standard Tcl object result.
460
 *
461
 * Side effects:
462
 *      See the user documentation.
463
 *
464
 *----------------------------------------------------------------------
465
 */
466
 
467
        /* ARGSUSED */
468
int
469
Tcl_EvalObjCmd(dummy, interp, objc, objv)
470
    ClientData dummy;           /* Not used. */
471
    Tcl_Interp *interp;         /* Current interpreter. */
472
    int objc;                   /* Number of arguments. */
473
    Tcl_Obj *CONST objv[];      /* Argument objects. */
474
{
475
    int result;
476
    register Tcl_Obj *objPtr;
477
 
478
    if (objc < 2) {
479
        Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
480
        return TCL_ERROR;
481
    }
482
 
483
    if (objc == 2) {
484
        result = Tcl_EvalObj(interp, objv[1]);
485
    } else {
486
        /*
487
         * More than one argument: concatenate them together with spaces
488
         * between, then evaluate the result.
489
         */
490
 
491
        objPtr = Tcl_ConcatObj(objc-1, objv+1);
492
        result = Tcl_EvalObj(interp, objPtr);
493
        Tcl_DecrRefCount(objPtr);  /* we're done with the object */
494
    }
495
    if (result == TCL_ERROR) {
496
        char msg[60];
497
        sprintf(msg, "\n    (\"eval\" body line %d)", interp->errorLine);
498
        Tcl_AddObjErrorInfo(interp, msg, -1);
499
    }
500
    return result;
501
}
502
 
503
/*
504
 *----------------------------------------------------------------------
505
 *
506
 * Tcl_ExitObjCmd --
507
 *
508
 *      This procedure is invoked to process the "exit" Tcl command.
509
 *      See the user documentation for details on what it does.
510
 *
511
 * Results:
512
 *      A standard Tcl object result.
513
 *
514
 * Side effects:
515
 *      See the user documentation.
516
 *
517
 *----------------------------------------------------------------------
518
 */
519
 
520
        /* ARGSUSED */
521
int
522
Tcl_ExitObjCmd(dummy, interp, objc, objv)
523
    ClientData dummy;           /* Not used. */
524
    Tcl_Interp *interp;         /* Current interpreter. */
525
    int objc;                   /* Number of arguments. */
526
    Tcl_Obj *CONST objv[];      /* Argument objects. */
527
{
528
    int value;
529
 
530
    if ((objc != 1) && (objc != 2)) {
531
        Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?");
532
        return TCL_ERROR;
533
    }
534
 
535
    if (objc == 1) {
536
        value = 0;
537
    } else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) {
538
        return TCL_ERROR;
539
    }
540
    Tcl_Exit(value);
541
    /*NOTREACHED*/
542
    return TCL_OK;                      /* Better not ever reach this! */
543
}
544
 
545
/*
546
 *----------------------------------------------------------------------
547
 *
548
 * Tcl_ExprObjCmd --
549
 *
550
 *      This object-based procedure is invoked to process the "expr" Tcl
551
 *      command. See the user documentation for details on what it does.
552
 *
553
 *      With the bytecode compiler, this procedure is called in two
554
 *      circumstances: 1) to execute expr commands that are too complicated
555
 *      or too unsafe to try compiling directly into an inline sequence of
556
 *      instructions, and 2) to execute commands where the command name is
557
 *      computed at runtime and is "expr" or the name to which "expr" was
558
 *      renamed (e.g., "set z expr; $z 2+3")
559
 *
560
 * Results:
561
 *      A standard Tcl object result.
562
 *
563
 * Side effects:
564
 *      See the user documentation.
565
 *
566
 *----------------------------------------------------------------------
567
 */
568
 
569
        /* ARGSUSED */
570
int
571
Tcl_ExprObjCmd(dummy, interp, objc, objv)
572
    ClientData dummy;           /* Not used. */
573
    Tcl_Interp *interp;         /* Current interpreter. */
574
    int objc;                   /* Number of arguments. */
575
    Tcl_Obj *CONST objv[];      /* Argument objects. */
576
{
577
    register Tcl_Obj *objPtr;
578
    Tcl_Obj *resultPtr;
579
    register char *bytes;
580
    int length, i, result;
581
 
582
    if (objc < 2) {
583
        Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
584
        return TCL_ERROR;
585
    }
586
 
587
    if (objc == 2) {
588
        result = Tcl_ExprObj(interp, objv[1], &resultPtr);
589
        if (result == TCL_OK) {
590
            Tcl_SetObjResult(interp, resultPtr);
591
            Tcl_DecrRefCount(resultPtr);  /* done with the result object */
592
        }
593
        return result;
594
    }
595
 
596
    /*
597
     * Create a new object holding the concatenated argument strings.
598
     * THIS FAILS IF AN OBJECT'S STRING REP HAS A NULL BYTE.
599
     */
600
 
601
    bytes = Tcl_GetStringFromObj(objv[1], &length);
602
    objPtr = Tcl_NewStringObj(bytes, length);
603
    Tcl_IncrRefCount(objPtr);
604
    for (i = 2;  i < objc;  i++) {
605
        Tcl_AppendToObj(objPtr, " ", 1);
606
        bytes = Tcl_GetStringFromObj(objv[i], &length);
607
        Tcl_AppendToObj(objPtr, bytes, length);
608
    }
609
 
610
    /*
611
     * Evaluate the concatenated string object.
612
     */
613
 
614
    result = Tcl_ExprObj(interp, objPtr, &resultPtr);
615
    if (result == TCL_OK) {
616
        Tcl_SetObjResult(interp, resultPtr);
617
        Tcl_DecrRefCount(resultPtr);  /* done with the result object */
618
    }
619
 
620
    /*
621
     * Free allocated resources.
622
     */
623
 
624
    Tcl_DecrRefCount(objPtr);
625
    return result;
626
}
627
 
628
/*
629
 *----------------------------------------------------------------------
630
 *
631
 * Tcl_FileObjCmd --
632
 *
633
 *      This procedure is invoked to process the "file" Tcl command.
634
 *      See the user documentation for details on what it does.
635
 *      PLEASE NOTE THAT THIS FAILS WITH FILENAMES AND PATHS WITH
636
 *      EMBEDDED NULLS, WHICH COULD THEORETICALLY HAPPEN ON A MAC.
637
 *
638
 * Results:
639
 *      A standard Tcl result.
640
 *
641
 * Side effects:
642
 *      See the user documentation.
643
 *
644
 *----------------------------------------------------------------------
645
 */
646
 
647
        /* ARGSUSED */
648
int
649
Tcl_FileObjCmd(dummy, interp, objc, objv)
650
    ClientData dummy;           /* Not used. */
651
    Tcl_Interp *interp;         /* Current interpreter. */
652
    int objc;                   /* Number of arguments. */
653
    Tcl_Obj *CONST objv[];      /* Argument objects. */
654
{
655
    char *fileName, *extension, *errorString;
656
    int statOp = 0;              /* Init. to avoid compiler warning. */
657
    int length;
658
    int mode = 0;                        /* Initialized only to prevent
659
                                         * compiler warning message. */
660
    struct stat statBuf;
661
    Tcl_DString buffer;
662
    Tcl_Obj *resultPtr;
663
    int index, result;
664
 
665
/*
666
 * This list of constants should match the fileOption string array below.
667
 */
668
 
669
enum {FILE_ATIME, FILE_ATTRIBUTES, FILE_COPY, FILE_DELETE, FILE_DIRNAME,
670
        FILE_EXECUTABLE, FILE_EXISTS, FILE_EXTENSION, FILE_ISDIRECTORY,
671
        FILE_ISFILE, FILE_JOIN, FILE_LSTAT, FILE_MTIME, FILE_MKDIR,
672
        FILE_NATIVENAME, FILE_OWNED, FILE_PATHTYPE, FILE_READABLE,
673
        FILE_READLINK, FILE_RENAME, FILE_ROOTNAME, FILE_SIZE, FILE_SPLIT,
674
        FILE_STAT, FILE_TAIL, FILE_TYPE, FILE_VOLUMES, FILE_WRITABLE};
675
 
676
 
677
    static char *fileOptions[] = {"atime", "attributes", "copy", "delete",
678
            "dirname", "executable", "exists", "extension", "isdirectory",
679
            "isfile", "join", "lstat", "mtime", "mkdir", "nativename",
680
            "owned", "pathtype", "readable", "readlink", "rename",
681
            "rootname", "size", "split", "stat", "tail", "type", "volumes",
682
            "writable", (char *) NULL};
683
 
684
    if (objc < 2) {
685
        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
686
        return TCL_ERROR;
687
    }
688
 
689
    if (Tcl_GetIndexFromObj(interp, objv[1], fileOptions, "option", 0, &index)
690
            != TCL_OK) {
691
        return TCL_ERROR;
692
    }
693
 
694
    result = TCL_OK;
695
    /*
696
     * First, do the volumes command, since it is the only one that
697
     * has objc == 2.
698
     */
699
 
700
    if ( index == FILE_VOLUMES) {
701
        if ( objc != 2 ) {
702
            Tcl_WrongNumArgs(interp, 1, objv, "volumes");
703
            return TCL_ERROR;
704
        }
705
        result = TclpListVolumes(interp);
706
        return result;
707
    }
708
 
709
    if (objc < 3) {
710
        Tcl_WrongNumArgs(interp, 2, objv, "name ?arg ...?");
711
        return TCL_ERROR;
712
    }
713
 
714
    Tcl_DStringInit(&buffer);
715
    resultPtr = Tcl_GetObjResult(interp);
716
 
717
 
718
    /*
719
     * Handle operations on the file name.
720
     */
721
 
722
    switch (index) {
723
        case FILE_ATTRIBUTES:
724
            result = TclFileAttrsCmd(interp, objc - 2, objv + 2);
725
            goto done;
726
        case FILE_DIRNAME:      {
727
            int pargc;
728
            char **pargv;
729
 
730
            if (objc != 3) {
731
                errorString = "dirname name";
732
                goto not3Args;
733
            }
734
 
735
            fileName = Tcl_GetStringFromObj(objv[2], &length);
736
 
737
            /*
738
             * If there is only one element, and it starts with a tilde,
739
             * perform tilde substitution and resplit the path.
740
             */
741
 
742
            Tcl_SplitPath(fileName, &pargc, &pargv);
743
            if ((pargc == 1) && (*fileName == '~')) {
744
                ckfree((char*) pargv);
745
                fileName = Tcl_TranslateFileName(interp, fileName, &buffer);
746
                if (fileName == NULL) {
747
                    result = TCL_ERROR;
748
                    goto done;
749
                }
750
                Tcl_SplitPath(fileName, &pargc, &pargv);
751
                Tcl_DStringSetLength(&buffer, 0);
752
            }
753
 
754
            /*
755
             * Return all but the last component.  If there is only one
756
             * component, return it if the path was non-relative, otherwise
757
             * return the current directory.
758
             */
759
 
760
            if (pargc > 1) {
761
                Tcl_JoinPath(pargc-1, pargv, &buffer);
762
                Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&buffer),
763
                        buffer.length);
764
            } else if ((pargc == 0)
765
                    || (Tcl_GetPathType(pargv[0]) == TCL_PATH_RELATIVE)) {
766
                Tcl_SetStringObj(resultPtr, (tclPlatform == TCL_PLATFORM_MAC)
767
                        ? ":" : ".", 1);
768
            } else {
769
                Tcl_SetStringObj(resultPtr, pargv[0], -1);           }
770
            ckfree((char *)pargv);
771
            goto done;
772
        }
773
        case FILE_TAIL: {
774
            int pargc;
775
            char **pargv;
776
 
777
            if (objc != 3) {
778
                errorString = "tail name";
779
                goto not3Args;
780
            }
781
 
782
            fileName = Tcl_GetStringFromObj(objv[2], &length);
783
 
784
            /*
785
             * If there is only one element, and it starts with a tilde,
786
             * perform tilde substitution and resplit the path.
787
             */
788
 
789
            Tcl_SplitPath(fileName, &pargc, &pargv);
790
            if ((pargc == 1) && (*fileName == '~')) {
791
                ckfree((char*) pargv);
792
                fileName = Tcl_TranslateFileName(interp, fileName, &buffer);
793
                if (fileName == NULL) {
794
                    result = TCL_ERROR;
795
                    goto done;
796
                }
797
                Tcl_SplitPath(fileName, &pargc, &pargv);
798
                Tcl_DStringSetLength(&buffer, 0);
799
            }
800
 
801
            /*
802
             * Return the last component, unless it is the only component,
803
             * and it is the root of an absolute path.
804
             */
805
 
806
            if (pargc > 0) {
807
                if ((pargc > 1)
808
                        || (Tcl_GetPathType(pargv[0]) == TCL_PATH_RELATIVE)) {
809
                    Tcl_SetStringObj(resultPtr, pargv[pargc - 1], -1);
810
                }
811
            }
812
            ckfree((char *)pargv);
813
            goto done;
814
        }
815
        case FILE_ROOTNAME: {
816
            char *fileName;
817
 
818
            if (objc != 3) {
819
                errorString = "rootname name";
820
                goto not3Args;
821
            }
822
 
823
            fileName = Tcl_GetStringFromObj(objv[2], &length);
824
            extension = TclGetExtension(fileName);
825
            if (extension == NULL) {
826
                Tcl_SetObjResult(interp, objv[2]);
827
            } else {
828
                Tcl_SetStringObj(resultPtr, fileName,
829
                        (int) (length - strlen(extension)));
830
            }
831
            goto done;
832
        }
833
        case FILE_EXTENSION:
834
            if (objc != 3) {
835
                errorString = "extension name";
836
                goto not3Args;
837
            }
838
            extension = TclGetExtension(Tcl_GetStringFromObj(objv[2],&length));
839
 
840
            if (extension != NULL) {
841
                Tcl_SetStringObj(resultPtr, extension, (int)strlen(extension));
842
            }
843
            goto done;
844
        case FILE_PATHTYPE:
845
            if (objc != 3) {
846
                errorString = "pathtype name";
847
                goto not3Args;
848
            }
849
            switch (Tcl_GetPathType(Tcl_GetStringFromObj(objv[2], &length))) {
850
                case TCL_PATH_ABSOLUTE:
851
                    Tcl_SetStringObj(resultPtr, "absolute", -1);
852
                    break;
853
                case TCL_PATH_RELATIVE:
854
                    Tcl_SetStringObj(resultPtr, "relative", -1);
855
                    break;
856
                case TCL_PATH_VOLUME_RELATIVE:
857
                    Tcl_SetStringObj(resultPtr, "volumerelative", -1);
858
                    break;
859
            }
860
            goto done;
861
        case FILE_SPLIT: {
862
            int pargc, i;
863
            char **pargvList;
864
            Tcl_Obj *listObjPtr;
865
 
866
            if (objc != 3) {
867
                errorString = "split name";
868
                goto not3Args;
869
            }
870
 
871
            Tcl_SplitPath(Tcl_GetStringFromObj(objv[2], &length), &pargc,
872
                    &pargvList);
873
            listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
874
            for (i = 0; i < pargc; i++) {
875
                Tcl_ListObjAppendElement(interp, listObjPtr,
876
                        Tcl_NewStringObj(pargvList[i], -1));
877
            }
878
            ckfree((char *) pargvList);
879
            Tcl_SetObjResult(interp, listObjPtr);
880
            goto done;
881
        }
882
        case FILE_JOIN: {
883
            char **pargv = (char **) ckalloc((objc - 2) * sizeof(char *));
884
            int i;
885
 
886
            for (i = 2; i < objc; i++) {
887
                pargv[i - 2] = Tcl_GetStringFromObj(objv[i], &length);
888
            }
889
            Tcl_JoinPath(objc - 2, pargv, &buffer);
890
            Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&buffer),
891
                    buffer.length);
892
            ckfree((char *) pargv);
893
            Tcl_DStringFree(&buffer);
894
            goto done;
895
        }
896
        case FILE_RENAME: {
897
            char **pargv = (char **) ckalloc(objc * sizeof(char *));
898
            int i;
899
 
900
            for (i = 0; i < objc; i++) {
901
                pargv[i] = Tcl_GetStringFromObj(objv[i], &length);
902
            }
903
            result = TclFileRenameCmd(interp, objc, pargv);
904
            ckfree((char *) pargv);
905
            goto done;
906
        }
907
        case FILE_MKDIR: {
908
            char **pargv = (char **) ckalloc(objc * sizeof(char *));
909
            int i;
910
 
911
            for (i = 0; i < objc; i++) {
912
                pargv[i] = Tcl_GetStringFromObj(objv[i], &length);
913
            }
914
            result = TclFileMakeDirsCmd(interp, objc, pargv);
915
            ckfree((char *) pargv);
916
            goto done;
917
        }
918
        case FILE_DELETE: {
919
            char **pargv = (char **) ckalloc(objc * sizeof(char *));
920
            int i;
921
 
922
            for (i = 0; i < objc; i++) {
923
                pargv[i] = Tcl_GetStringFromObj(objv[i], &length);
924
            }
925
            result = TclFileDeleteCmd(interp, objc, pargv);
926
            ckfree((char *) pargv);
927
            goto done;
928
        }
929
        case FILE_COPY: {
930
            char **pargv = (char **) ckalloc(objc * sizeof(char *));
931
            int i;
932
 
933
            for (i = 0; i < objc; i++) {
934
                pargv[i] = Tcl_GetStringFromObj(objv[i], &length);
935
            }
936
            result = TclFileCopyCmd(interp, objc, pargv);
937
            ckfree((char *) pargv);
938
            goto done;
939
        }
940
        case FILE_NATIVENAME:
941
            fileName = Tcl_TranslateFileName(interp,
942
                    Tcl_GetStringFromObj(objv[2], &length), &buffer);
943
            if (fileName == NULL) {
944
                result = TCL_ERROR ;
945
            } else {
946
                Tcl_SetStringObj(resultPtr, fileName, -1);
947
            }
948
            goto done;
949
    }
950
 
951
    /*
952
     * Next, handle operations that can be satisfied with the "access"
953
     * kernel call.
954
     */
955
 
956
    fileName = Tcl_TranslateFileName(interp,
957
            Tcl_GetStringFromObj(objv[2], &length), &buffer);
958
 
959
    switch (index) {
960
        case FILE_READABLE:
961
            if (objc != 3) {
962
                errorString = "readable name";
963
                goto not3Args;
964
            }
965
            mode = R_OK;
966
checkAccess:
967
            /*
968
             * The result might have been set within Tcl_TranslateFileName
969
             * (like no such user "blah" for file exists ~blah)
970
             * but we don't want to flag an error in that case.
971
             */
972
            if (fileName == NULL) {
973
                Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
974
            } else {
975
                Tcl_SetBooleanObj(resultPtr, (TclAccess(fileName, mode) != -1));
976
            }
977
            goto done;
978
          case FILE_WRITABLE:
979
            if (objc != 3) {
980
                errorString = "writable name";
981
                goto not3Args;
982
            }
983
            mode = W_OK;
984
            goto checkAccess;
985
          case FILE_EXECUTABLE:
986
            if (objc != 3) {
987
                errorString = "executable name";
988
                goto not3Args;
989
            }
990
            mode = X_OK;
991
            goto checkAccess;
992
          case FILE_EXISTS:
993
            if (objc != 3) {
994
                errorString = "exists name";
995
                goto not3Args;
996
            }
997
            mode = F_OK;
998
            goto checkAccess;
999
    }
1000
 
1001
 
1002
    /*
1003
     * Lastly, check stuff that requires the file to be stat-ed.
1004
     */
1005
 
1006
    if (fileName == NULL) {
1007
        result = TCL_ERROR;
1008
        goto done;
1009
    }
1010
 
1011
    switch (index) {
1012
        case FILE_ATIME:
1013
            if (objc != 3) {
1014
                errorString = "atime name";
1015
                goto not3Args;
1016
            }
1017
 
1018
            if (TclStat(fileName, &statBuf) == -1) {
1019
                goto badStat;
1020
            }
1021
            Tcl_SetLongObj(resultPtr, (long) statBuf.st_atime);
1022
            goto done;
1023
        case FILE_ISDIRECTORY:
1024
            if (objc != 3) {
1025
                errorString = "isdirectory name";
1026
                goto not3Args;
1027
            }
1028
            statOp = 2;
1029
            break;
1030
        case FILE_ISFILE:
1031
            if (objc != 3) {
1032
                errorString = "isfile name";
1033
                goto not3Args;
1034
            }
1035
            statOp = 1;
1036
            break;
1037
        case FILE_LSTAT:
1038
            if (objc != 4) {
1039
                Tcl_WrongNumArgs(interp, 1, objv, "lstat name varName");
1040
                result = TCL_ERROR;
1041
                goto done;
1042
            }
1043
 
1044
            if (lstat(fileName, &statBuf) == -1) {
1045
                Tcl_AppendStringsToObj(resultPtr, "couldn't lstat \"",
1046
                        Tcl_GetStringFromObj(objv[2], &length), "\": ",
1047
                        Tcl_PosixError(interp), (char *) NULL);
1048
                result = TCL_ERROR;
1049
                goto done;
1050
            }
1051
            result = StoreStatData(interp, Tcl_GetStringFromObj(objv[3],
1052
                    &length), &statBuf);
1053
            goto done;
1054
        case FILE_MTIME:
1055
            if (objc != 3) {
1056
                errorString = "mtime name";
1057
                goto not3Args;
1058
            }
1059
            if (TclStat(fileName, &statBuf) == -1) {
1060
                goto badStat;
1061
            }
1062
            Tcl_SetLongObj(resultPtr, (long) statBuf.st_mtime);
1063
            goto done;
1064
        case FILE_OWNED:
1065
            if (objc != 3) {
1066
                errorString = "owned name";
1067
                goto not3Args;
1068
            }
1069
            statOp = 0;
1070
            break;
1071
        case FILE_READLINK: {
1072
            char linkValue[MAXPATHLEN + 1];
1073
            int linkLength;
1074
 
1075
            if (objc != 3) {
1076
                errorString = "readlink name";
1077
                goto not3Args;
1078
            }
1079
 
1080
            /*
1081
             * If S_IFLNK isn't defined it means that the machine doesn't
1082
             * support symbolic links, so the file can't possibly be a
1083
             * symbolic link.  Generate an EINVAL error, which is what
1084
             * happens on machines that do support symbolic links when
1085
             * you invoke readlink on a file that isn't a symbolic link.
1086
             */
1087
 
1088
#ifndef S_IFLNK
1089
            linkLength = -1;
1090
            errno = EINVAL;
1091
#else
1092
            linkLength = readlink(fileName, linkValue, sizeof(linkValue) - 1);
1093
#endif /* S_IFLNK */
1094
            if (linkLength == -1) {
1095
                Tcl_AppendStringsToObj(resultPtr, "couldn't readlink \"",
1096
                        Tcl_GetStringFromObj(objv[2], &length), "\": ",
1097
                        Tcl_PosixError(interp), (char *) NULL);
1098
                result = TCL_ERROR;
1099
                goto done;
1100
            }
1101
            linkValue[linkLength] = 0;
1102
            Tcl_SetStringObj(resultPtr, linkValue, linkLength);
1103
            goto done;
1104
        }
1105
        case FILE_SIZE:
1106
            if (objc != 3) {
1107
                errorString = "size name";
1108
                goto not3Args;
1109
            }
1110
            if (TclStat(fileName, &statBuf) == -1) {
1111
                goto badStat;
1112
            }
1113
            Tcl_SetLongObj(resultPtr, (long) statBuf.st_size);
1114
            goto done;
1115
        case FILE_STAT:
1116
            if (objc != 4) {
1117
                Tcl_WrongNumArgs(interp, 1, objv, "stat name varName");
1118
                result = TCL_ERROR;
1119
                goto done;
1120
            }
1121
 
1122
            if (TclStat(fileName, &statBuf) == -1) {
1123
badStat:
1124
                Tcl_AppendStringsToObj(resultPtr, "couldn't stat \"",
1125
                        Tcl_GetStringFromObj(objv[2], &length),
1126
                        "\": ", Tcl_PosixError(interp), (char *) NULL);
1127
                result = TCL_ERROR;
1128
                goto done;
1129
            }
1130
            result = StoreStatData(interp, Tcl_GetStringFromObj(objv[3],
1131
                    &length), &statBuf);
1132
            goto done;
1133
        case FILE_TYPE:
1134
            if (objc != 3) {
1135
                errorString = "type name";
1136
                goto not3Args;
1137
            }
1138
            if (lstat(fileName, &statBuf) == -1) {
1139
                goto badStat;
1140
            }
1141
            errorString = GetTypeFromMode((int) statBuf.st_mode);
1142
            Tcl_SetStringObj(resultPtr, errorString, -1);
1143
            goto done;
1144
    }
1145
 
1146
    if (TclStat(fileName, &statBuf) == -1) {
1147
        Tcl_SetBooleanObj(resultPtr, 0);
1148
        goto done;
1149
    }
1150
    switch (statOp) {
1151
        case 0:
1152
            /*
1153
             * For Windows and Macintosh, there are no user ids
1154
             * associated with a file, so we always return 1.
1155
             */
1156
 
1157
#if (defined(__WIN32__) || defined(MAC_TCL))
1158
            mode = 1;
1159
#else
1160
            mode = (geteuid() == statBuf.st_uid);
1161
#endif
1162
            break;
1163
        case 1:
1164
            mode = S_ISREG(statBuf.st_mode);
1165
            break;
1166
        case 2:
1167
            mode = S_ISDIR(statBuf.st_mode);
1168
            break;
1169
    }
1170
    Tcl_SetBooleanObj(resultPtr, mode);
1171
 
1172
done:
1173
    Tcl_DStringFree(&buffer);
1174
    return result;
1175
 
1176
not3Args:
1177
    Tcl_WrongNumArgs(interp, 1, objv, errorString);
1178
    result = TCL_ERROR;
1179
    goto done;
1180
}
1181
 
1182
/*
1183
 *----------------------------------------------------------------------
1184
 *
1185
 * StoreStatData --
1186
 *
1187
 *      This is a utility procedure that breaks out the fields of a
1188
 *      "stat" structure and stores them in textual form into the
1189
 *      elements of an associative array.
1190
 *
1191
 * Results:
1192
 *      Returns a standard Tcl return value.  If an error occurs then
1193
 *      a message is left in interp->result.
1194
 *
1195
 * Side effects:
1196
 *      Elements of the associative array given by "varName" are modified.
1197
 *
1198
 *----------------------------------------------------------------------
1199
 */
1200
 
1201
static int
1202
StoreStatData(interp, varName, statPtr)
1203
    Tcl_Interp *interp;                 /* Interpreter for error reports. */
1204
    char *varName;                      /* Name of associative array variable
1205
                                         * in which to store stat results. */
1206
    struct stat *statPtr;               /* Pointer to buffer containing
1207
                                         * stat data to store in varName. */
1208
{
1209
    char string[30];
1210
 
1211
    sprintf(string, "%ld", (long) statPtr->st_dev);
1212
    if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG)
1213
            == NULL) {
1214
        return TCL_ERROR;
1215
    }
1216
    sprintf(string, "%ld", (long) statPtr->st_ino);
1217
    if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG)
1218
            == NULL) {
1219
        return TCL_ERROR;
1220
    }
1221
    sprintf(string, "%ld", (long) statPtr->st_mode);
1222
    if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG)
1223
            == NULL) {
1224
        return TCL_ERROR;
1225
    }
1226
    sprintf(string, "%ld", (long) statPtr->st_nlink);
1227
    if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG)
1228
            == NULL) {
1229
        return TCL_ERROR;
1230
    }
1231
    sprintf(string, "%ld", (long) statPtr->st_uid);
1232
    if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG)
1233
            == NULL) {
1234
        return TCL_ERROR;
1235
    }
1236
    sprintf(string, "%ld", (long) statPtr->st_gid);
1237
    if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG)
1238
            == NULL) {
1239
        return TCL_ERROR;
1240
    }
1241
    sprintf(string, "%lu", (unsigned long) statPtr->st_size);
1242
    if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG)
1243
            == NULL) {
1244
        return TCL_ERROR;
1245
    }
1246
    sprintf(string, "%ld", (long) statPtr->st_atime);
1247
    if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG)
1248
            == NULL) {
1249
        return TCL_ERROR;
1250
    }
1251
    sprintf(string, "%ld", (long) statPtr->st_mtime);
1252
    if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG)
1253
            == NULL) {
1254
        return TCL_ERROR;
1255
    }
1256
    sprintf(string, "%ld", (long) statPtr->st_ctime);
1257
    if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG)
1258
            == NULL) {
1259
        return TCL_ERROR;
1260
    }
1261
    if (Tcl_SetVar2(interp, varName, "type",
1262
            GetTypeFromMode((int) statPtr->st_mode), TCL_LEAVE_ERR_MSG)
1263
            == NULL) {
1264
        return TCL_ERROR;
1265
    }
1266
    return TCL_OK;
1267
}
1268
 
1269
/*
1270
 *----------------------------------------------------------------------
1271
 *
1272
 * GetTypeFromMode --
1273
 *
1274
 *      Given a mode word, returns a string identifying the type of a
1275
 *      file.
1276
 *
1277
 * Results:
1278
 *      A static text string giving the file type from mode.
1279
 *
1280
 * Side effects:
1281
 *      None.
1282
 *
1283
 *----------------------------------------------------------------------
1284
 */
1285
 
1286
static char *
1287
GetTypeFromMode(mode)
1288
    int mode;
1289
{
1290
    if (S_ISREG(mode)) {
1291
        return "file";
1292
    } else if (S_ISDIR(mode)) {
1293
        return "directory";
1294
    } else if (S_ISCHR(mode)) {
1295
        return "characterSpecial";
1296
    } else if (S_ISBLK(mode)) {
1297
        return "blockSpecial";
1298
    } else if (S_ISFIFO(mode)) {
1299
        return "fifo";
1300
#ifdef S_ISLNK
1301
    } else if (S_ISLNK(mode)) {
1302
        return "link";
1303
#endif
1304
#ifdef S_ISSOCK
1305
    } else if (S_ISSOCK(mode)) {
1306
        return "socket";
1307
#endif
1308
    }
1309
    return "unknown";
1310
}
1311
 
1312
/*
1313
 *----------------------------------------------------------------------
1314
 *
1315
 * Tcl_ForCmd --
1316
 *
1317
 *      This procedure is invoked to process the "for" Tcl command.
1318
 *      See the user documentation for details on what it does.
1319
 *
1320
 *      With the bytecode compiler, this procedure is only called when
1321
 *      a command name is computed at runtime, and is "for" or the name
1322
 *      to which "for" was renamed: e.g.,
1323
 *      "set z for; $z {set i 0} {$i<100} {incr i} {puts $i}"
1324
 *
1325
 * Results:
1326
 *      A standard Tcl result.
1327
 *
1328
 * Side effects:
1329
 *      See the user documentation.
1330
 *
1331
 *----------------------------------------------------------------------
1332
 */
1333
 
1334
        /* ARGSUSED */
1335
int
1336
Tcl_ForCmd(dummy, interp, argc, argv)
1337
    ClientData dummy;                   /* Not used. */
1338
    Tcl_Interp *interp;                 /* Current interpreter. */
1339
    int argc;                           /* Number of arguments. */
1340
    char **argv;                        /* Argument strings. */
1341
{
1342
    int result, value;
1343
 
1344
    if (argc != 5) {
1345
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1346
                " start test next command\"", (char *) NULL);
1347
        return TCL_ERROR;
1348
    }
1349
 
1350
    result = Tcl_Eval(interp, argv[1]);
1351
    if (result != TCL_OK) {
1352
        if (result == TCL_ERROR) {
1353
            Tcl_AddErrorInfo(interp, "\n    (\"for\" initial command)");
1354
        }
1355
        return result;
1356
    }
1357
    while (1) {
1358
        result = Tcl_ExprBoolean(interp, argv[2], &value);
1359
        if (result != TCL_OK) {
1360
            return result;
1361
        }
1362
        if (!value) {
1363
            break;
1364
        }
1365
        result = Tcl_Eval(interp, argv[4]);
1366
        if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
1367
            if (result == TCL_ERROR) {
1368
                char msg[60];
1369
                sprintf(msg, "\n    (\"for\" body line %d)",interp->errorLine);
1370
                Tcl_AddErrorInfo(interp, msg);
1371
            }
1372
            break;
1373
        }
1374
        result = Tcl_Eval(interp, argv[3]);
1375
        if (result == TCL_BREAK) {
1376
            break;
1377
        } else if (result != TCL_OK) {
1378
            if (result == TCL_ERROR) {
1379
                Tcl_AddErrorInfo(interp, "\n    (\"for\" loop-end command)");
1380
            }
1381
            return result;
1382
        }
1383
    }
1384
    if (result == TCL_BREAK) {
1385
        result = TCL_OK;
1386
    }
1387
    if (result == TCL_OK) {
1388
        Tcl_ResetResult(interp);
1389
    }
1390
    return result;
1391
}
1392
 
1393
/*
1394
 *----------------------------------------------------------------------
1395
 *
1396
 * Tcl_ForeachObjCmd --
1397
 *
1398
 *      This object-based procedure is invoked to process the "foreach" Tcl
1399
 *      command.  See the user documentation for details on what it does.
1400
 *
1401
 * Results:
1402
 *      A standard Tcl object result.
1403
 *
1404
 * Side effects:
1405
 *      See the user documentation.
1406
 *
1407
 *----------------------------------------------------------------------
1408
 */
1409
 
1410
        /* ARGSUSED */
1411
int
1412
Tcl_ForeachObjCmd(dummy, interp, objc, objv)
1413
    ClientData dummy;           /* Not used. */
1414
    Tcl_Interp *interp;         /* Current interpreter. */
1415
    int objc;                   /* Number of arguments. */
1416
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1417
{
1418
    int result = TCL_OK;
1419
    int i;                      /* i selects a value list */
1420
    int j, maxj;                /* Number of loop iterations */
1421
    int v;                      /* v selects a loop variable */
1422
    int numLists;               /* Count of value lists */
1423
    Tcl_Obj *bodyPtr;
1424
 
1425
    /*
1426
     * We copy the argument object pointers into a local array to avoid
1427
     * the problem that "objv" might become invalid. It is a pointer into
1428
     * the evaluation stack and that stack might be grown and reallocated
1429
     * if the loop body requires a large amount of stack space.
1430
     */
1431
 
1432
#define NUM_ARGS 9
1433
    Tcl_Obj *(argObjStorage[NUM_ARGS]);
1434
    Tcl_Obj **argObjv = argObjStorage;
1435
 
1436
#define STATIC_LIST_SIZE 4
1437
    int indexArray[STATIC_LIST_SIZE];     /* Array of value list indices */
1438
    int varcListArray[STATIC_LIST_SIZE];  /* # loop variables per list */
1439
    Tcl_Obj **varvListArray[STATIC_LIST_SIZE]; /* Array of var name lists */
1440
    int argcListArray[STATIC_LIST_SIZE];  /* Array of value list sizes */
1441
    Tcl_Obj **argvListArray[STATIC_LIST_SIZE]; /* Array of value lists */
1442
 
1443
    int *index = indexArray;
1444
    int *varcList = varcListArray;
1445
    Tcl_Obj ***varvList = varvListArray;
1446
    int *argcList = argcListArray;
1447
    Tcl_Obj ***argvList = argvListArray;
1448
 
1449
    if (objc < 4 || (objc%2 != 0)) {
1450
        Tcl_WrongNumArgs(interp, 1, objv,
1451
                "varList list ?varList list ...? command");
1452
        return TCL_ERROR;
1453
    }
1454
 
1455
    /*
1456
     * Create the object argument array "argObjv". Make sure argObjv is
1457
     * large enough to hold the objc arguments.
1458
     */
1459
 
1460
    if (objc > NUM_ARGS) {
1461
        argObjv = (Tcl_Obj **) ckalloc(objc * sizeof(Tcl_Obj *));
1462
    }
1463
    for (i = 0;  i < objc;  i++) {
1464
        argObjv[i] = objv[i];
1465
    }
1466
 
1467
    /*
1468
     * Manage numList parallel value lists.
1469
     * argvList[i] is a value list counted by argcList[i]
1470
     * varvList[i] is the list of variables associated with the value list
1471
     * varcList[i] is the number of variables associated with the value list
1472
     * index[i] is the current pointer into the value list argvList[i]
1473
     */
1474
 
1475
    numLists = (objc-2)/2;
1476
    if (numLists > STATIC_LIST_SIZE) {
1477
        index = (int *) ckalloc(numLists * sizeof(int));
1478
        varcList = (int *) ckalloc(numLists * sizeof(int));
1479
        varvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
1480
        argcList = (int *) ckalloc(numLists * sizeof(int));
1481
        argvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
1482
    }
1483
    for (i = 0;  i < numLists;  i++) {
1484
        index[i] = 0;
1485
        varcList[i] = 0;
1486
        varvList[i] = (Tcl_Obj **) NULL;
1487
        argcList[i] = 0;
1488
        argvList[i] = (Tcl_Obj **) NULL;
1489
    }
1490
 
1491
    /*
1492
     * Break up the value lists and variable lists into elements
1493
     * THIS FAILS IF THE OBJECT'S STRING REP HAS A NULL BYTE.
1494
     */
1495
 
1496
    maxj = 0;
1497
    for (i = 0;  i < numLists;  i++) {
1498
        result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
1499
                &varcList[i], &varvList[i]);
1500
        if (result != TCL_OK) {
1501
            goto done;
1502
        }
1503
        if (varcList[i] < 1) {
1504
            Tcl_AppendToObj(Tcl_GetObjResult(interp),
1505
                    "foreach varlist is empty", -1);
1506
            result = TCL_ERROR;
1507
            goto done;
1508
        }
1509
 
1510
        result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
1511
                &argcList[i], &argvList[i]);
1512
        if (result != TCL_OK) {
1513
            goto done;
1514
        }
1515
 
1516
        j = argcList[i] / varcList[i];
1517
        if ((argcList[i] % varcList[i]) != 0) {
1518
            j++;
1519
        }
1520
        if (j > maxj) {
1521
            maxj = j;
1522
        }
1523
    }
1524
 
1525
    /*
1526
     * Iterate maxj times through the lists in parallel
1527
     * If some value lists run out of values, set loop vars to ""
1528
     */
1529
 
1530
    bodyPtr = argObjv[objc-1];
1531
    for (j = 0;  j < maxj;  j++) {
1532
        for (i = 0;  i < numLists;  i++) {
1533
            /*
1534
             * If a variable or value list object has been converted to
1535
             * another kind of Tcl object, convert it back to a list object
1536
             * and refetch the pointer to its element array.
1537
             */
1538
 
1539
            if (argObjv[1+i*2]->typePtr != &tclListType) {
1540
                result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
1541
                        &varcList[i], &varvList[i]);
1542
                if (result != TCL_OK) {
1543
                    panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i);
1544
                }
1545
            }
1546
            if (argObjv[2+i*2]->typePtr != &tclListType) {
1547
                result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
1548
                        &argcList[i], &argvList[i]);
1549
                if (result != TCL_OK) {
1550
                    panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i);
1551
                }
1552
            }
1553
 
1554
            for (v = 0;  v < varcList[i];  v++) {
1555
                int k = index[i]++;
1556
                Tcl_Obj *valuePtr, *varValuePtr;
1557
                int isEmptyObj = 0;
1558
 
1559
                if (k < argcList[i]) {
1560
                    valuePtr = argvList[i][k];
1561
                } else {
1562
                    valuePtr = Tcl_NewObj(); /* empty string */
1563
                    isEmptyObj = 1;
1564
                }
1565
                varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v], NULL,
1566
                        valuePtr, TCL_PARSE_PART1);
1567
                if (varValuePtr == NULL) {
1568
                    if (isEmptyObj) {
1569
                        Tcl_DecrRefCount(valuePtr);
1570
                    }
1571
                    Tcl_ResetResult(interp);
1572
                    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1573
                        "couldn't set loop variable: \"",
1574
                        Tcl_GetStringFromObj(varvList[i][v], (int *) NULL),
1575
                        "\"", (char *) NULL);
1576
                    result = TCL_ERROR;
1577
                    goto done;
1578
                }
1579
 
1580
            }
1581
        }
1582
 
1583
        result = Tcl_EvalObj(interp, bodyPtr);
1584
        if (result != TCL_OK) {
1585
            if (result == TCL_CONTINUE) {
1586
                result = TCL_OK;
1587
            } else if (result == TCL_BREAK) {
1588
                result = TCL_OK;
1589
                break;
1590
            } else if (result == TCL_ERROR) {
1591
                char msg[100];
1592
                sprintf(msg, "\n    (\"foreach\" body line %d)",
1593
                        interp->errorLine);
1594
                Tcl_AddObjErrorInfo(interp, msg, -1);
1595
                break;
1596
            } else {
1597
                break;
1598
            }
1599
        }
1600
    }
1601
    if (result == TCL_OK) {
1602
        Tcl_ResetResult(interp);
1603
    }
1604
 
1605
    done:
1606
    if (numLists > STATIC_LIST_SIZE) {
1607
        ckfree((char *) index);
1608
        ckfree((char *) varcList);
1609
        ckfree((char *) argcList);
1610
        ckfree((char *) varvList);
1611
        ckfree((char *) argvList);
1612
    }
1613
    if (argObjv != argObjStorage) {
1614
        ckfree((char *) argObjv);
1615
    }
1616
    return result;
1617
#undef STATIC_LIST_SIZE
1618
#undef NUM_ARGS
1619
}
1620
 
1621
/*
1622
 *----------------------------------------------------------------------
1623
 *
1624
 * Tcl_FormatObjCmd --
1625
 *
1626
 *      This procedure is invoked to process the "format" Tcl command.
1627
 *      See the user documentation for details on what it does.
1628
 *
1629
 * Results:
1630
 *      A standard Tcl result.
1631
 *
1632
 * Side effects:
1633
 *      See the user documentation.
1634
 *
1635
 *----------------------------------------------------------------------
1636
 */
1637
 
1638
        /* ARGSUSED */
1639
int
1640
Tcl_FormatObjCmd(dummy, interp, objc, objv)
1641
    ClientData dummy;           /* Not used. */
1642
    Tcl_Interp *interp;         /* Current interpreter. */
1643
    int objc;                   /* Number of arguments. */
1644
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1645
{
1646
    register char *format;      /* Used to read characters from the format
1647
                                 * string. */
1648
    int formatLen;              /* The length of the format string */
1649
    char *endPtr;               /* Points to the last char in format array */
1650
    char newFormat[40];         /* A new format specifier is generated here. */
1651
    int width;                  /* Field width from field specifier, or 0 if
1652
                                 * no width given. */
1653
    int precision;              /* Field precision from field specifier, or 0
1654
                                 * if no precision given. */
1655
    int size;                   /* Number of bytes needed for result of
1656
                                 * conversion, based on type of conversion
1657
                                 * ("e", "s", etc.), width, and precision. */
1658
    int intValue;               /* Used to hold value to pass to sprintf, if
1659
                                 * it's a one-word integer or char value */
1660
    char *ptrValue = NULL;      /* Used to hold value to pass to sprintf, if
1661
                                 * it's a one-word value. */
1662
    double doubleValue;         /* Used to hold value to pass to sprintf if
1663
                                 * it's a double value. */
1664
    int whichValue;             /* Indicates which of intValue, ptrValue,
1665
                                 * or doubleValue has the value to pass to
1666
                                 * sprintf, according to the following
1667
                                 * definitions: */
1668
#   define INT_VALUE 0
1669
#   define PTR_VALUE 1
1670
#   define DOUBLE_VALUE 2
1671
#   define MAX_FLOAT_SIZE 320
1672
 
1673
    Tcl_Obj *resultPtr;         /* Where result is stored finally. */
1674
    char staticBuf[MAX_FLOAT_SIZE + 1];
1675
                                /* A static buffer to copy the format results
1676
                                 * into */
1677
    char *dst = staticBuf;      /* The buffer that sprintf writes into each
1678
                                 * time the format processes a specifier */
1679
    int dstSize = MAX_FLOAT_SIZE;
1680
                                /* The size of the dst buffer */
1681
    int noPercent;              /* Special case for speed:  indicates there's
1682
                                 * no field specifier, just a string to copy.*/
1683
    int objIndex;               /* Index of argument to substitute next. */
1684
    int gotXpg = 0;              /* Non-zero means that an XPG3 %n$-style
1685
                                 * specifier has been seen. */
1686
    int gotSequential = 0;       /* Non-zero means that a regular sequential
1687
                                 * (non-XPG3) conversion specifier has been
1688
                                 * seen. */
1689
    int useShort;               /* Value to be printed is short (half word). */
1690
    char *end;                  /* Used to locate end of numerical fields. */
1691
 
1692
    /*
1693
     * This procedure is a bit nasty.  The goal is to use sprintf to
1694
     * do most of the dirty work.  There are several problems:
1695
     * 1. this procedure can't trust its arguments.
1696
     * 2. we must be able to provide a large enough result area to hold
1697
     *    whatever's generated.  This is hard to estimate.
1698
     * 2. there's no way to move the arguments from objv to the call
1699
     *    to sprintf in a reasonable way.  This is particularly nasty
1700
     *    because some of the arguments may be two-word values (doubles).
1701
     * So, what happens here is to scan the format string one % group
1702
     * at a time, making many individual calls to sprintf.
1703
     */
1704
 
1705
    if (objc < 2) {
1706
        Tcl_WrongNumArgs(interp, 1, objv,
1707
                "formatString ?arg arg ...?");
1708
        return TCL_ERROR;
1709
    }
1710
 
1711
    format = Tcl_GetStringFromObj(objv[1], &formatLen);
1712
    endPtr = format + formatLen;
1713
    resultPtr = Tcl_NewObj();
1714
    objIndex = 2;
1715
 
1716
    while (format < endPtr) {
1717
        register char *newPtr = newFormat;
1718
 
1719
        width = precision = noPercent = useShort = 0;
1720
        whichValue = PTR_VALUE;
1721
 
1722
        /*
1723
         * Get rid of any characters before the next field specifier.
1724
         */
1725
        if (*format != '%') {
1726
            ptrValue = format;
1727
            while ((*format != '%') && (format < endPtr)) {
1728
                format++;
1729
            }
1730
            size = format - ptrValue;
1731
            noPercent = 1;
1732
            goto doField;
1733
        }
1734
 
1735
        if (format[1] == '%') {
1736
            ptrValue = format;
1737
            size = 1;
1738
            noPercent = 1;
1739
            format += 2;
1740
            goto doField;
1741
        }
1742
 
1743
        /*
1744
         * Parse off a field specifier, compute how many characters
1745
         * will be needed to store the result, and substitute for
1746
         * "*" size specifiers.
1747
         */
1748
        *newPtr = '%';
1749
        newPtr++;
1750
        format++;
1751
        if (isdigit(UCHAR(*format))) {
1752
            int tmp;
1753
 
1754
            /*
1755
             * Check for an XPG3-style %n$ specification.  Note: there
1756
             * must not be a mixture of XPG3 specs and non-XPG3 specs
1757
             * in the same format string.
1758
             */
1759
 
1760
            tmp = strtoul(format, &end, 10);
1761
            if (*end != '$') {
1762
                goto notXpg;
1763
            }
1764
            format = end+1;
1765
            gotXpg = 1;
1766
            if (gotSequential) {
1767
                goto mixedXPG;
1768
            }
1769
            objIndex = tmp+1;
1770
            if ((objIndex < 2) || (objIndex >= objc)) {
1771
                goto badIndex;
1772
            }
1773
            goto xpgCheckDone;
1774
        }
1775
 
1776
        notXpg:
1777
        gotSequential = 1;
1778
        if (gotXpg) {
1779
            goto mixedXPG;
1780
        }
1781
 
1782
        xpgCheckDone:
1783
        while ((*format == '-') || (*format == '#') || (*format == '0')
1784
                || (*format == ' ') || (*format == '+')) {
1785
            *newPtr = *format;
1786
            newPtr++;
1787
            format++;
1788
        }
1789
        if (isdigit(UCHAR(*format))) {
1790
            width = strtoul(format, &end, 10);
1791
            format = end;
1792
        } else if (*format == '*') {
1793
            if (objIndex >= objc) {
1794
                goto badIndex;
1795
            }
1796
            if (Tcl_GetIntFromObj(interp, objv[objIndex],
1797
                    &width) != TCL_OK) {
1798
                goto fmtError;
1799
            }
1800
            objIndex++;
1801
            format++;
1802
        }
1803
        if (width > 100000) {
1804
            /*
1805
             * Don't allow arbitrarily large widths:  could cause core
1806
             * dump when we try to allocate a zillion bytes of memory
1807
             * below.
1808
             */
1809
 
1810
            width = 100000;
1811
        } else if (width < 0) {
1812
            width = 0;
1813
        }
1814
        if (width != 0) {
1815
            TclFormatInt(newPtr, width);
1816
            while (*newPtr != 0) {
1817
                newPtr++;
1818
            }
1819
        }
1820
        if (*format == '.') {
1821
            *newPtr = '.';
1822
            newPtr++;
1823
            format++;
1824
        }
1825
        if (isdigit(UCHAR(*format))) {
1826
            precision = strtoul(format, &end, 10);
1827
            format = end;
1828
        } else if (*format == '*') {
1829
            if (objIndex >= objc) {
1830
                goto badIndex;
1831
            }
1832
            if (Tcl_GetIntFromObj(interp, objv[objIndex],
1833
                    &precision) != TCL_OK) {
1834
                goto fmtError;
1835
            }
1836
            objIndex++;
1837
            format++;
1838
        }
1839
        if (precision != 0) {
1840
            TclFormatInt(newPtr, precision);
1841
            while (*newPtr != 0) {
1842
                newPtr++;
1843
            }
1844
        }
1845
        if (*format == 'l') {
1846
            format++;
1847
        } else if (*format == 'h') {
1848
            useShort = 1;
1849
            *newPtr = 'h';
1850
            newPtr++;
1851
            format++;
1852
        }
1853
        *newPtr = *format;
1854
        newPtr++;
1855
        *newPtr = 0;
1856
        if (objIndex >= objc) {
1857
            goto badIndex;
1858
        }
1859
        switch (*format) {
1860
            case 'i':
1861
                newPtr[-1] = 'd';
1862
            case 'd':
1863
            case 'o':
1864
            case 'u':
1865
            case 'x':
1866
            case 'X':
1867
                if (Tcl_GetIntFromObj(interp, objv[objIndex],
1868
                        (int *) &intValue) != TCL_OK) {
1869
                    goto fmtError;
1870
                }
1871
                whichValue = INT_VALUE;
1872
                size = 40 + precision;
1873
                break;
1874
            case 's':
1875
                ptrValue = Tcl_GetStringFromObj(objv[objIndex], &size);
1876
                break;
1877
            case 'c':
1878
                if (Tcl_GetIntFromObj(interp, objv[objIndex],
1879
                        (int *) &intValue) != TCL_OK) {
1880
                    goto fmtError;
1881
                }
1882
                whichValue = INT_VALUE;
1883
                size = 1;
1884
                break;
1885
            case 'e':
1886
            case 'E':
1887
            case 'f':
1888
            case 'g':
1889
            case 'G':
1890
                if (Tcl_GetDoubleFromObj(interp, objv[objIndex],
1891
                        &doubleValue) != TCL_OK) {
1892
                    goto fmtError;
1893
                }
1894
                whichValue = DOUBLE_VALUE;
1895
                size = MAX_FLOAT_SIZE;
1896
                if (precision > 10) {
1897
                    size += precision;
1898
                }
1899
                break;
1900
            case 0:
1901
                Tcl_SetResult(interp,
1902
                        "format string ended in middle of field specifier",
1903
                        TCL_STATIC);
1904
                goto fmtError;
1905
            default:
1906
                {
1907
                    char buf[40];
1908
                    sprintf(buf, "bad field specifier \"%c\"", *format);
1909
                    Tcl_SetResult(interp, buf, TCL_VOLATILE);
1910
                    goto fmtError;
1911
                }
1912
        }
1913
        objIndex++;
1914
        format++;
1915
 
1916
        /*
1917
         * Make sure that there's enough space to hold the formatted
1918
         * result, then format it.
1919
         */
1920
 
1921
        doField:
1922
        if (width > size) {
1923
            size = width;
1924
        }
1925
        if (noPercent) {
1926
            Tcl_AppendToObj(resultPtr, ptrValue, size);
1927
        } else {
1928
            if (size > dstSize) {
1929
                if (dst != staticBuf) {
1930
                    ckfree(dst);
1931
                }
1932
                dst = (char *) ckalloc((unsigned) (size + 1));
1933
                dstSize = size;
1934
            }
1935
 
1936
            if (whichValue == DOUBLE_VALUE) {
1937
                sprintf(dst, newFormat, doubleValue);
1938
            } else if (whichValue == INT_VALUE) {
1939
                if (useShort) {
1940
                    sprintf(dst, newFormat, (short) intValue);
1941
                } else {
1942
                    sprintf(dst, newFormat, intValue);
1943
                }
1944
            } else {
1945
                sprintf(dst, newFormat, ptrValue);
1946
            }
1947
            Tcl_AppendToObj(resultPtr, dst, -1);
1948
        }
1949
    }
1950
 
1951
    Tcl_SetObjResult(interp, resultPtr);
1952
    if(dst != staticBuf) {
1953
        ckfree(dst);
1954
    }
1955
    return TCL_OK;
1956
 
1957
    mixedXPG:
1958
    Tcl_SetResult(interp,
1959
            "cannot mix \"%\" and \"%n$\" conversion specifiers", TCL_STATIC);
1960
    goto fmtError;
1961
 
1962
    badIndex:
1963
    if (gotXpg) {
1964
        Tcl_SetResult(interp,
1965
                "\"%n$\" argument index out of range", TCL_STATIC);
1966
    } else {
1967
        Tcl_SetResult(interp,
1968
                "not enough arguments for all format specifiers", TCL_STATIC);
1969
    }
1970
 
1971
    fmtError:
1972
    if(dst != staticBuf) {
1973
        ckfree(dst);
1974
    }
1975
    Tcl_DecrRefCount(resultPtr);
1976
    return TCL_ERROR;
1977
}

powered by: WebSVN 2.1.0

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