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

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tcl/] [generic/] [tclCmdIL.c] - Blame information for rev 579

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

Line No. Rev Author Line
1 578 markom
/*
2
 * tclCmdIL.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
 *      I through L.  It contains only commands in the generic core
7
 *      (i.e. 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) 1993-1997 Lucent Technologies.
11
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12
 * Copyright (c) 1998 by Scriptics Corporation.
13
 *
14
 * See the file "license.terms" for information on usage and redistribution
15
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16
 *
17
 * RCS: @(#) $Id: tclCmdIL.c,v 1.1.1.1 2002-01-16 10:25:25 markom Exp $
18
 */
19
 
20
#include "tclInt.h"
21
#include "tclPort.h"
22
#include "tclCompile.h"
23
 
24
/*
25
 * During execution of the "lsort" command, structures of the following
26
 * type are used to arrange the objects being sorted into a collection
27
 * of linked lists.
28
 */
29
 
30
typedef struct SortElement {
31
    Tcl_Obj *objPtr;                    /* Object being sorted. */
32
    struct SortElement *nextPtr;        /* Next element in the list, or
33
                                         * NULL for end of list. */
34
} SortElement;
35
 
36
/*
37
 * The "lsort" command needs to pass certain information down to the
38
 * function that compares two list elements, and the comparison function
39
 * needs to pass success or failure information back up to the top-level
40
 * "lsort" command.  The following structure is used to pass this
41
 * information.
42
 */
43
 
44
typedef struct SortInfo {
45
    int isIncreasing;           /* Nonzero means sort in increasing order. */
46
    int sortMode;               /* The sort mode.  One of SORTMODE_*
47
                                 * values defined below */
48
    Tcl_DString compareCmd;     /* The Tcl comparison command when sortMode
49
                                 * is SORTMODE_COMMAND.  Pre-initialized to
50
                                 * hold base of command.*/
51
    int index;                  /* If the -index option was specified, this
52
                                 * holds the index of the list element
53
                                 * to extract for comparison.  If -index
54
                                 * wasn't specified, this is -1. */
55
    Tcl_Interp *interp;         /* The interpreter in which the sortis
56
                                 * being done. */
57
    int resultCode;             /* Completion code for the lsort command.
58
                                 * If an error occurs during the sort this
59
                                 * is changed from TCL_OK to  TCL_ERROR. */
60
} SortInfo;
61
 
62
/*
63
 * The "sortMode" field of the SortInfo structure can take on any of the
64
 * following values.
65
 */
66
 
67
#define SORTMODE_ASCII      0
68
#define SORTMODE_INTEGER    1
69
#define SORTMODE_REAL       2
70
#define SORTMODE_COMMAND    3
71
#define SORTMODE_DICTIONARY 4
72
 
73
/*
74
 * Forward declarations for procedures defined in this file:
75
 */
76
 
77
static void             AppendLocals _ANSI_ARGS_((Tcl_Interp *interp,
78
                            Tcl_Obj *listPtr, char *pattern,
79
                            int includeLinks));
80
static int              DictionaryCompare _ANSI_ARGS_((char *left,
81
                            char *right));
82
static int              InfoArgsCmd _ANSI_ARGS_((ClientData dummy,
83
                            Tcl_Interp *interp, int objc,
84
                            Tcl_Obj *CONST objv[]));
85
static int              InfoBodyCmd _ANSI_ARGS_((ClientData dummy,
86
                            Tcl_Interp *interp, int objc,
87
                            Tcl_Obj *CONST objv[]));
88
static int              InfoCmdCountCmd _ANSI_ARGS_((ClientData dummy,
89
                            Tcl_Interp *interp, int objc,
90
                            Tcl_Obj *CONST objv[]));
91
static int              InfoCommandsCmd _ANSI_ARGS_((ClientData dummy,
92
                            Tcl_Interp *interp, int objc,
93
                            Tcl_Obj *CONST objv[]));
94
static int              InfoCompleteCmd _ANSI_ARGS_((ClientData dummy,
95
                            Tcl_Interp *interp, int objc,
96
                            Tcl_Obj *CONST objv[]));
97
static int              InfoDefaultCmd _ANSI_ARGS_((ClientData dummy,
98
                            Tcl_Interp *interp, int objc,
99
                            Tcl_Obj *CONST objv[]));
100
static int              InfoExistsCmd _ANSI_ARGS_((ClientData dummy,
101
                            Tcl_Interp *interp, int objc,
102
                            Tcl_Obj *CONST objv[]));
103
static int              InfoGlobalsCmd _ANSI_ARGS_((ClientData dummy,
104
                            Tcl_Interp *interp, int objc,
105
                            Tcl_Obj *CONST objv[]));
106
static int              InfoHostnameCmd _ANSI_ARGS_((ClientData dummy,
107
                            Tcl_Interp *interp, int objc,
108
                            Tcl_Obj *CONST objv[]));
109
static int              InfoLevelCmd _ANSI_ARGS_((ClientData dummy,
110
                            Tcl_Interp *interp, int objc,
111
                            Tcl_Obj *CONST objv[]));
112
static int              InfoLibraryCmd _ANSI_ARGS_((ClientData dummy,
113
                            Tcl_Interp *interp, int objc,
114
                            Tcl_Obj *CONST objv[]));
115
static int              InfoLoadedCmd _ANSI_ARGS_((ClientData dummy,
116
                            Tcl_Interp *interp, int objc,
117
                            Tcl_Obj *CONST objv[]));
118
static int              InfoLocalsCmd _ANSI_ARGS_((ClientData dummy,
119
                            Tcl_Interp *interp, int objc,
120
                            Tcl_Obj *CONST objv[]));
121
static int              InfoNameOfExecutableCmd _ANSI_ARGS_((
122
                            ClientData dummy, Tcl_Interp *interp, int objc,
123
                            Tcl_Obj *CONST objv[]));
124
static int              InfoPatchLevelCmd _ANSI_ARGS_((ClientData dummy,
125
                            Tcl_Interp *interp, int objc,
126
                            Tcl_Obj *CONST objv[]));
127
static int              InfoProcsCmd _ANSI_ARGS_((ClientData dummy,
128
                            Tcl_Interp *interp, int objc,
129
                            Tcl_Obj *CONST objv[]));
130
static int              InfoScriptCmd _ANSI_ARGS_((ClientData dummy,
131
                            Tcl_Interp *interp, int objc,
132
                            Tcl_Obj *CONST objv[]));
133
static int              InfoSharedlibCmd _ANSI_ARGS_((ClientData dummy,
134
                            Tcl_Interp *interp, int objc,
135
                            Tcl_Obj *CONST objv[]));
136
static int              InfoTclVersionCmd _ANSI_ARGS_((ClientData dummy,
137
                            Tcl_Interp *interp, int objc,
138
                            Tcl_Obj *CONST objv[]));
139
static int              InfoVarsCmd _ANSI_ARGS_((ClientData dummy,
140
                            Tcl_Interp *interp, int objc,
141
                            Tcl_Obj *CONST objv[]));
142
static SortElement *    MergeSort _ANSI_ARGS_((SortElement *headPt,
143
                            SortInfo *infoPtr));
144
static SortElement *    MergeLists _ANSI_ARGS_((SortElement *leftPtr,
145
                            SortElement *rightPtr, SortInfo *infoPtr));
146
static int              SortCompare _ANSI_ARGS_((Tcl_Obj *firstPtr,
147
                            Tcl_Obj *second, SortInfo *infoPtr));
148
 
149
/*
150
 *----------------------------------------------------------------------
151
 *
152
 * Tcl_IfCmd --
153
 *
154
 *      This procedure is invoked to process the "if" Tcl command.
155
 *      See the user documentation for details on what it does.
156
 *
157
 *      With the bytecode compiler, this procedure is only called when
158
 *      a command name is computed at runtime, and is "if" or the name
159
 *      to which "if" was renamed: e.g., "set z if; $z 1 {puts foo}"
160
 *
161
 * Results:
162
 *      A standard Tcl result.
163
 *
164
 * Side effects:
165
 *      See the user documentation.
166
 *
167
 *----------------------------------------------------------------------
168
 */
169
 
170
        /* ARGSUSED */
171
int
172
Tcl_IfCmd(dummy, interp, argc, argv)
173
    ClientData dummy;                   /* Not used. */
174
    Tcl_Interp *interp;                 /* Current interpreter. */
175
    int argc;                           /* Number of arguments. */
176
    char **argv;                        /* Argument strings. */
177
{
178
    int i, result, value;
179
 
180
    i = 1;
181
    while (1) {
182
        /*
183
         * At this point in the loop, argv and argc refer to an expression
184
         * to test, either for the main expression or an expression
185
         * following an "elseif".  The arguments after the expression must
186
         * be "then" (optional) and a script to execute if the expression is
187
         * true.
188
         */
189
 
190
        if (i >= argc) {
191
            Tcl_AppendResult(interp, "wrong # args: no expression after \"",
192
                    argv[i-1], "\" argument", (char *) NULL);
193
            return TCL_ERROR;
194
        }
195
        result = Tcl_ExprBoolean(interp, argv[i], &value);
196
        if (result != TCL_OK) {
197
            return result;
198
        }
199
        i++;
200
        if ((i < argc) && (strcmp(argv[i], "then") == 0)) {
201
            i++;
202
        }
203
        if (i >= argc) {
204
            Tcl_AppendResult(interp, "wrong # args: no script following \"",
205
                    argv[i-1], "\" argument", (char *) NULL);
206
            return TCL_ERROR;
207
        }
208
        if (value) {
209
            return Tcl_Eval(interp, argv[i]);
210
        }
211
 
212
        /*
213
         * The expression evaluated to false.  Skip the command, then
214
         * see if there is an "else" or "elseif" clause.
215
         */
216
 
217
        i++;
218
        if (i >= argc) {
219
            return TCL_OK;
220
        }
221
        if ((argv[i][0] == 'e') && (strcmp(argv[i], "elseif") == 0)) {
222
            i++;
223
            continue;
224
        }
225
        break;
226
    }
227
 
228
    /*
229
     * Couldn't find a "then" or "elseif" clause to execute.  Check now
230
     * for an "else" clause.  We know that there's at least one more
231
     * argument when we get here.
232
     */
233
 
234
    if (strcmp(argv[i], "else") == 0) {
235
        i++;
236
        if (i >= argc) {
237
            Tcl_AppendResult(interp,
238
                    "wrong # args: no script following \"else\" argument",
239
                    (char *) NULL);
240
            return TCL_ERROR;
241
        }
242
    }
243
    return Tcl_Eval(interp, argv[i]);
244
}
245
 
246
/*
247
 *----------------------------------------------------------------------
248
 *
249
 * Tcl_IncrCmd --
250
 *
251
 *      This procedure is invoked to process the "incr" Tcl command.
252
 *      See the user documentation for details on what it does.
253
 *
254
 *      With the bytecode compiler, this procedure is only called when
255
 *      a command name is computed at runtime, and is "incr" or the name
256
 *      to which "incr" was renamed: e.g., "set z incr; $z i -1"
257
 *
258
 * Results:
259
 *      A standard Tcl result.
260
 *
261
 * Side effects:
262
 *      See the user documentation.
263
 *
264
 *----------------------------------------------------------------------
265
 */
266
 
267
    /* ARGSUSED */
268
int
269
Tcl_IncrCmd(dummy, interp, argc, argv)
270
    ClientData dummy;                   /* Not used. */
271
    Tcl_Interp *interp;                 /* Current interpreter. */
272
    int argc;                           /* Number of arguments. */
273
    char **argv;                        /* Argument strings. */
274
{
275
    int value;
276
    char *oldString, *result;
277
    char newString[30];
278
 
279
    if ((argc != 2) && (argc != 3)) {
280
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
281
                " varName ?increment?\"", (char *) NULL);
282
        return TCL_ERROR;
283
    }
284
 
285
    oldString = Tcl_GetVar(interp, argv[1], TCL_LEAVE_ERR_MSG);
286
    if (oldString == NULL) {
287
        return TCL_ERROR;
288
    }
289
    if (Tcl_GetInt(interp, oldString, &value) != TCL_OK) {
290
        Tcl_AddErrorInfo(interp,
291
                "\n    (reading value of variable to increment)");
292
        return TCL_ERROR;
293
    }
294
    if (argc == 2) {
295
        value += 1;
296
    } else {
297
        int increment;
298
 
299
        if (Tcl_GetInt(interp, argv[2], &increment) != TCL_OK) {
300
            Tcl_AddErrorInfo(interp,
301
                    "\n    (reading increment)");
302
            return TCL_ERROR;
303
        }
304
        value += increment;
305
    }
306
    TclFormatInt(newString, value);
307
    result = Tcl_SetVar(interp, argv[1], newString, TCL_LEAVE_ERR_MSG);
308
    if (result == NULL) {
309
        return TCL_ERROR;
310
    }
311
 
312
    /*
313
     * Copy the result since the variable's value might change.
314
     */
315
 
316
    Tcl_SetResult(interp, result, TCL_VOLATILE);
317
    return TCL_OK;
318
}
319
 
320
/*
321
 *----------------------------------------------------------------------
322
 *
323
 * Tcl_InfoObjCmd --
324
 *
325
 *      This procedure is invoked to process the "info" Tcl command.
326
 *      See the user documentation for details on what it does.
327
 *
328
 * Results:
329
 *      A standard Tcl result.
330
 *
331
 * Side effects:
332
 *      See the user documentation.
333
 *
334
 *----------------------------------------------------------------------
335
 */
336
 
337
        /* ARGSUSED */
338
int
339
Tcl_InfoObjCmd(clientData, interp, objc, objv)
340
    ClientData clientData;      /* Arbitrary value passed to the command. */
341
    Tcl_Interp *interp;         /* Current interpreter. */
342
    int objc;                   /* Number of arguments. */
343
    Tcl_Obj *CONST objv[];      /* Argument objects. */
344
{
345
    static char *subCmds[] = {
346
            "args", "body", "cmdcount", "commands",
347
             "complete", "default", "exists", "globals",
348
             "hostname", "level", "library", "loaded",
349
             "locals", "nameofexecutable", "patchlevel", "procs",
350
             "script", "sharedlibextension", "tclversion", "vars",
351
             (char *) NULL};
352
    enum ISubCmdIdx {
353
            IArgsIdx, IBodyIdx, ICmdCountIdx, ICommandsIdx,
354
            ICompleteIdx, IDefaultIdx, IExistsIdx, IGlobalsIdx,
355
            IHostnameIdx, ILevelIdx, ILibraryIdx, ILoadedIdx,
356
            ILocalsIdx, INameOfExecutableIdx, IPatchLevelIdx, IProcsIdx,
357
            IScriptIdx, ISharedLibExtensionIdx, ITclVersionIdx, IVarsIdx
358
    } index;
359
    int result;
360
 
361
    if (objc < 2) {
362
        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
363
        return TCL_ERROR;
364
    }
365
 
366
    result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", 0,
367
            (int *) &index);
368
    if (result != TCL_OK) {
369
        return result;
370
    }
371
 
372
    switch (index) {
373
        case IArgsIdx:
374
            result = InfoArgsCmd(clientData, interp, objc, objv);
375
            break;
376
        case IBodyIdx:
377
            result = InfoBodyCmd(clientData, interp, objc, objv);
378
            break;
379
        case ICmdCountIdx:
380
            result = InfoCmdCountCmd(clientData, interp, objc, objv);
381
            break;
382
        case ICommandsIdx:
383
            result = InfoCommandsCmd(clientData, interp, objc, objv);
384
            break;
385
        case ICompleteIdx:
386
            result = InfoCompleteCmd(clientData, interp, objc, objv);
387
            break;
388
        case IDefaultIdx:
389
            result = InfoDefaultCmd(clientData, interp, objc, objv);
390
            break;
391
        case IExistsIdx:
392
            result = InfoExistsCmd(clientData, interp, objc, objv);
393
            break;
394
        case IGlobalsIdx:
395
            result = InfoGlobalsCmd(clientData, interp, objc, objv);
396
            break;
397
        case IHostnameIdx:
398
            result = InfoHostnameCmd(clientData, interp, objc, objv);
399
            break;
400
        case ILevelIdx:
401
            result = InfoLevelCmd(clientData, interp, objc, objv);
402
            break;
403
        case ILibraryIdx:
404
            result = InfoLibraryCmd(clientData, interp, objc, objv);
405
            break;
406
        case ILoadedIdx:
407
            result = InfoLoadedCmd(clientData, interp, objc, objv);
408
            break;
409
        case ILocalsIdx:
410
            result = InfoLocalsCmd(clientData, interp, objc, objv);
411
            break;
412
        case INameOfExecutableIdx:
413
            result = InfoNameOfExecutableCmd(clientData, interp, objc, objv);
414
            break;
415
        case IPatchLevelIdx:
416
            result = InfoPatchLevelCmd(clientData, interp, objc, objv);
417
            break;
418
        case IProcsIdx:
419
            result = InfoProcsCmd(clientData, interp, objc, objv);
420
            break;
421
        case IScriptIdx:
422
            result = InfoScriptCmd(clientData, interp, objc, objv);
423
            break;
424
        case ISharedLibExtensionIdx:
425
            result = InfoSharedlibCmd(clientData, interp, objc, objv);
426
            break;
427
        case ITclVersionIdx:
428
            result = InfoTclVersionCmd(clientData, interp, objc, objv);
429
            break;
430
        case IVarsIdx:
431
            result = InfoVarsCmd(clientData, interp, objc, objv);
432
            break;
433
    }
434
    return result;
435
}
436
 
437
/*
438
 *----------------------------------------------------------------------
439
 *
440
 * InfoArgsCmd --
441
 *
442
 *      Called to implement the "info args" command that returns the
443
 *      argument list for a procedure. Handles the following syntax:
444
 *
445
 *          info args procName
446
 *
447
 * Results:
448
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
449
 *
450
 * Side effects:
451
 *      Returns a result in the interpreter's result object. If there is
452
 *      an error, the result is an error message.
453
 *
454
 *----------------------------------------------------------------------
455
 */
456
 
457
static int
458
InfoArgsCmd(dummy, interp, objc, objv)
459
    ClientData dummy;           /* Not used. */
460
    Tcl_Interp *interp;         /* Current interpreter. */
461
    int objc;                   /* Number of arguments. */
462
    Tcl_Obj *CONST objv[];      /* Argument objects. */
463
{
464
    register Interp *iPtr = (Interp *) interp;
465
    char *name;
466
    Proc *procPtr;
467
    CompiledLocal *localPtr;
468
    Tcl_Obj *listObjPtr;
469
 
470
    if (objc != 3) {
471
        Tcl_WrongNumArgs(interp, 2, objv, "procname");
472
        return TCL_ERROR;
473
    }
474
 
475
    name = Tcl_GetStringFromObj(objv[2], (int *) NULL);
476
    procPtr = TclFindProc(iPtr, name);
477
    if (procPtr == NULL) {
478
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
479
                "\"", name, "\" isn't a procedure", (char *) NULL);
480
        return TCL_ERROR;
481
    }
482
 
483
    /*
484
     * Build a return list containing the arguments.
485
     */
486
 
487
    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
488
    for (localPtr = procPtr->firstLocalPtr;  localPtr != NULL;
489
            localPtr = localPtr->nextPtr) {
490
        if (TclIsVarArgument(localPtr)) {
491
            Tcl_ListObjAppendElement(interp, listObjPtr,
492
                    Tcl_NewStringObj(localPtr->name, -1));
493
        }
494
    }
495
    Tcl_SetObjResult(interp, listObjPtr);
496
    return TCL_OK;
497
}
498
 
499
/*
500
 *----------------------------------------------------------------------
501
 *
502
 * InfoBodyCmd --
503
 *
504
 *      Called to implement the "info body" command that returns the body
505
 *      for a procedure. Handles the following syntax:
506
 *
507
 *          info body procName
508
 *
509
 * Results:
510
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
511
 *
512
 * Side effects:
513
 *      Returns a result in the interpreter's result object. If there is
514
 *      an error, the result is an error message.
515
 *
516
 *----------------------------------------------------------------------
517
 */
518
 
519
static int
520
InfoBodyCmd(dummy, interp, objc, objv)
521
    ClientData dummy;           /* Not used. */
522
    Tcl_Interp *interp;         /* Current interpreter. */
523
    int objc;                   /* Number of arguments. */
524
    Tcl_Obj *CONST objv[];      /* Argument objects. */
525
{
526
    register Interp *iPtr = (Interp *) interp;
527
    char *name;
528
    Proc *procPtr;
529
    Tcl_Obj *bodyPtr, *resultPtr;
530
 
531
    if (objc != 3) {
532
        Tcl_WrongNumArgs(interp, 2, objv, "procname");
533
        return TCL_ERROR;
534
    }
535
 
536
    name = Tcl_GetStringFromObj(objv[2], (int *) NULL);
537
    procPtr = TclFindProc(iPtr, name);
538
    if (procPtr == NULL) {
539
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
540
                "\"", name, "\" isn't a procedure", (char *) NULL);
541
        return TCL_ERROR;
542
    }
543
 
544
    /*
545
     * we need to check if the body from this procedure had been generated
546
     * from a precompiled body. If that is the case, then the bodyPtr's
547
     * string representation is bogus, since sources are not available.
548
     * In order to make sure that later manipulations of the object do not
549
     * invalidate the internal representation, we make a copy of the string
550
     * representation and return that one, instead.
551
     */
552
 
553
    bodyPtr = procPtr->bodyPtr;
554
    resultPtr = bodyPtr;
555
    if (bodyPtr->typePtr == &tclByteCodeType) {
556
        ByteCode *codePtr = (ByteCode *) bodyPtr->internalRep.otherValuePtr;
557
 
558
        if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) {
559
            resultPtr = Tcl_NewStringObj(bodyPtr->bytes, bodyPtr->length);
560
        }
561
    }
562
 
563
    Tcl_SetObjResult(interp, resultPtr);
564
    return TCL_OK;
565
}
566
 
567
/*
568
 *----------------------------------------------------------------------
569
 *
570
 * InfoCmdCountCmd --
571
 *
572
 *      Called to implement the "info cmdcount" command that returns the
573
 *      number of commands that have been executed. Handles the following
574
 *      syntax:
575
 *
576
 *          info cmdcount
577
 *
578
 * Results:
579
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
580
 *
581
 * Side effects:
582
 *      Returns a result in the interpreter's result object. If there is
583
 *      an error, the result is an error message.
584
 *
585
 *----------------------------------------------------------------------
586
 */
587
 
588
static int
589
InfoCmdCountCmd(dummy, interp, objc, objv)
590
    ClientData dummy;           /* Not used. */
591
    Tcl_Interp *interp;         /* Current interpreter. */
592
    int objc;                   /* Number of arguments. */
593
    Tcl_Obj *CONST objv[];      /* Argument objects. */
594
{
595
    Interp *iPtr = (Interp *) interp;
596
 
597
    if (objc != 2) {
598
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
599
        return TCL_ERROR;
600
    }
601
 
602
    Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->cmdCount);
603
    return TCL_OK;
604
}
605
 
606
/*
607
 *----------------------------------------------------------------------
608
 *
609
 * InfoCommandsCmd --
610
 *
611
 *      Called to implement the "info commands" command that returns the
612
 *      list of commands in the interpreter that match an optional pattern.
613
 *      The pattern, if any, consists of an optional sequence of namespace
614
 *      names separated by "::" qualifiers, which is followed by a
615
 *      glob-style pattern that restricts which commands are returned.
616
 *      Handles the following syntax:
617
 *
618
 *          info commands ?pattern?
619
 *
620
 * Results:
621
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
622
 *
623
 * Side effects:
624
 *      Returns a result in the interpreter's result object. If there is
625
 *      an error, the result is an error message.
626
 *
627
 *----------------------------------------------------------------------
628
 */
629
 
630
static int
631
InfoCommandsCmd(dummy, interp, objc, objv)
632
    ClientData dummy;           /* Not used. */
633
    Tcl_Interp *interp;         /* Current interpreter. */
634
    int objc;                   /* Number of arguments. */
635
    Tcl_Obj *CONST objv[];      /* Argument objects. */
636
{
637
    char *cmdName, *pattern, *simplePattern;
638
    register Tcl_HashEntry *entryPtr;
639
    Tcl_HashSearch search;
640
    Namespace *nsPtr;
641
    Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
642
    Namespace *currNsPtr   = (Namespace *) Tcl_GetCurrentNamespace(interp);
643
    Tcl_Obj *listPtr, *elemObjPtr;
644
    int specificNsInPattern = 0;  /* Init. to avoid compiler warning. */
645
    Tcl_Command cmd;
646
    int result;
647
 
648
    /*
649
     * Get the pattern and find the "effective namespace" in which to
650
     * list commands.
651
     */
652
 
653
    if (objc == 2) {
654
        simplePattern = NULL;
655
        nsPtr = currNsPtr;
656
        specificNsInPattern = 0;
657
    } else if (objc == 3) {
658
        /*
659
         * From the pattern, get the effective namespace and the simple
660
         * pattern (no namespace qualifiers or ::'s) at the end. If an
661
         * error was found while parsing the pattern, return it. Otherwise,
662
         * if the namespace wasn't found, just leave nsPtr NULL: we will
663
         * return an empty list since no commands there can be found.
664
         */
665
 
666
        Namespace *dummy1NsPtr, *dummy2NsPtr;
667
 
668
        pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
669
        result = TclGetNamespaceForQualName(interp, pattern,
670
                (Namespace *) NULL, /*flags*/ TCL_LEAVE_ERR_MSG,
671
                &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern);
672
        if (result != TCL_OK) {
673
            return TCL_ERROR;
674
        }
675
        if (nsPtr != NULL) {    /* we successfully found the pattern's ns */
676
            specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
677
        }
678
    } else {
679
        Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
680
        return TCL_ERROR;
681
    }
682
 
683
    /*
684
     * Scan through the effective namespace's command table and create a
685
     * list with all commands that match the pattern. If a specific
686
     * namespace was requested in the pattern, qualify the command names
687
     * with the namespace name.
688
     */
689
 
690
    listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
691
 
692
    if (nsPtr != NULL) {
693
        entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
694
        while (entryPtr != NULL) {
695
            cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
696
            if ((simplePattern == NULL)
697
                    || Tcl_StringMatch(cmdName, simplePattern)) {
698
                if (specificNsInPattern) {
699
                    cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr);
700
                    elemObjPtr = Tcl_NewObj();
701
                    Tcl_GetCommandFullName(interp, cmd, elemObjPtr);
702
                } else {
703
                    elemObjPtr = Tcl_NewStringObj(cmdName, -1);
704
                }
705
                Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
706
            }
707
            entryPtr = Tcl_NextHashEntry(&search);
708
        }
709
 
710
        /*
711
         * If the effective namespace isn't the global :: namespace, and a
712
         * specific namespace wasn't requested in the pattern, then add in
713
         * all global :: commands that match the simple pattern. Of course,
714
         * we add in only those commands that aren't hidden by a command in
715
         * the effective namespace.
716
         */
717
 
718
        if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
719
            entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
720
            while (entryPtr != NULL) {
721
                cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
722
                if ((simplePattern == NULL)
723
                        || Tcl_StringMatch(cmdName, simplePattern)) {
724
                    if (Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName) == NULL) {
725
                        Tcl_ListObjAppendElement(interp, listPtr,
726
                                Tcl_NewStringObj(cmdName, -1));
727
                    }
728
                }
729
                entryPtr = Tcl_NextHashEntry(&search);
730
            }
731
        }
732
    }
733
 
734
    Tcl_SetObjResult(interp, listPtr);
735
    return TCL_OK;
736
}
737
 
738
/*
739
 *----------------------------------------------------------------------
740
 *
741
 * InfoCompleteCmd --
742
 *
743
 *      Called to implement the "info complete" command that determines
744
 *      whether a string is a complete Tcl command. Handles the following
745
 *      syntax:
746
 *
747
 *          info complete command
748
 *
749
 * Results:
750
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
751
 *
752
 * Side effects:
753
 *      Returns a result in the interpreter's result object. If there is
754
 *      an error, the result is an error message.
755
 *
756
 *----------------------------------------------------------------------
757
 */
758
 
759
static int
760
InfoCompleteCmd(dummy, interp, objc, objv)
761
    ClientData dummy;           /* Not used. */
762
    Tcl_Interp *interp;         /* Current interpreter. */
763
    int objc;                   /* Number of arguments. */
764
    Tcl_Obj *CONST objv[];      /* Argument objects. */
765
{
766
    if (objc != 3) {
767
        Tcl_WrongNumArgs(interp, 2, objv, "command");
768
        return TCL_ERROR;
769
    }
770
 
771
    if (TclObjCommandComplete(objv[2])) {
772
        Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
773
    } else {
774
        Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
775
    }
776
 
777
    return TCL_OK;
778
}
779
 
780
/*
781
 *----------------------------------------------------------------------
782
 *
783
 * InfoDefaultCmd --
784
 *
785
 *      Called to implement the "info default" command that returns the
786
 *      default value for a procedure argument. Handles the following
787
 *      syntax:
788
 *
789
 *          info default procName arg varName
790
 *
791
 * Results:
792
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
793
 *
794
 * Side effects:
795
 *      Returns a result in the interpreter's result object. If there is
796
 *      an error, the result is an error message.
797
 *
798
 *----------------------------------------------------------------------
799
 */
800
 
801
static int
802
InfoDefaultCmd(dummy, interp, objc, objv)
803
    ClientData dummy;           /* Not used. */
804
    Tcl_Interp *interp;         /* Current interpreter. */
805
    int objc;                   /* Number of arguments. */
806
    Tcl_Obj *CONST objv[];      /* Argument objects. */
807
{
808
    Interp *iPtr = (Interp *) interp;
809
    char *procName, *argName, *varName;
810
    Proc *procPtr;
811
    CompiledLocal *localPtr;
812
    Tcl_Obj *valueObjPtr;
813
 
814
    if (objc != 5) {
815
        Tcl_WrongNumArgs(interp, 2, objv, "procname arg varname");
816
        return TCL_ERROR;
817
    }
818
 
819
    procName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
820
    argName = Tcl_GetStringFromObj(objv[3], (int *) NULL);
821
 
822
    procPtr = TclFindProc(iPtr, procName);
823
    if (procPtr == NULL) {
824
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
825
                "\"", procName, "\" isn't a procedure", (char *) NULL);
826
        return TCL_ERROR;
827
    }
828
 
829
    for (localPtr = procPtr->firstLocalPtr;  localPtr != NULL;
830
            localPtr = localPtr->nextPtr) {
831
        if (TclIsVarArgument(localPtr)
832
                && (strcmp(argName, localPtr->name) == 0)) {
833
            if (localPtr->defValuePtr != NULL) {
834
                valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
835
                        localPtr->defValuePtr, 0);
836
                if (valueObjPtr == NULL) {
837
                    defStoreError:
838
                    varName = Tcl_GetStringFromObj(objv[4], (int *) NULL);
839
                    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
840
                            "couldn't store default value in variable \"",
841
                            varName, "\"", (char *) NULL);
842
                    return TCL_ERROR;
843
                }
844
                Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
845
            } else {
846
                Tcl_Obj *nullObjPtr = Tcl_NewObj();
847
                valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
848
                    nullObjPtr, 0);
849
                if (valueObjPtr == NULL) {
850
                    Tcl_DecrRefCount(nullObjPtr); /* free unneeded obj */
851
                    goto defStoreError;
852
                }
853
                Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
854
            }
855
            return TCL_OK;
856
        }
857
    }
858
 
859
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
860
            "procedure \"", procName, "\" doesn't have an argument \"",
861
            argName, "\"", (char *) NULL);
862
    return TCL_ERROR;
863
}
864
 
865
/*
866
 *----------------------------------------------------------------------
867
 *
868
 * InfoExistsCmd --
869
 *
870
 *      Called to implement the "info exists" command that determines
871
 *      whether a variable exists. Handles the following syntax:
872
 *
873
 *          info exists varName
874
 *
875
 * Results:
876
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
877
 *
878
 * Side effects:
879
 *      Returns a result in the interpreter's result object. If there is
880
 *      an error, the result is an error message.
881
 *
882
 *----------------------------------------------------------------------
883
 */
884
 
885
static int
886
InfoExistsCmd(dummy, interp, objc, objv)
887
    ClientData dummy;           /* Not used. */
888
    Tcl_Interp *interp;         /* Current interpreter. */
889
    int objc;                   /* Number of arguments. */
890
    Tcl_Obj *CONST objv[];      /* Argument objects. */
891
{
892
    char *varName;
893
    Var *varPtr, *arrayPtr;
894
 
895
    if (objc != 3) {
896
        Tcl_WrongNumArgs(interp, 2, objv, "varName");
897
        return TCL_ERROR;
898
    }
899
 
900
    varName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
901
    varPtr = TclLookupVar(interp, varName, (char *) NULL,
902
            TCL_PARSE_PART1, "access",
903
            /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
904
    if ((varPtr != NULL) && !TclIsVarUndefined(varPtr)) {
905
        Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
906
    } else {
907
        Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
908
    }
909
    return TCL_OK;
910
}
911
 
912
/*
913
 *----------------------------------------------------------------------
914
 *
915
 * InfoGlobalsCmd --
916
 *
917
 *      Called to implement the "info globals" command that returns the list
918
 *      of global variables matching an optional pattern. Handles the
919
 *      following syntax:
920
 *
921
 *          info globals ?pattern?
922
 *
923
 * Results:
924
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
925
 *
926
 * Side effects:
927
 *      Returns a result in the interpreter's result object. If there is
928
 *      an error, the result is an error message.
929
 *
930
 *----------------------------------------------------------------------
931
 */
932
 
933
static int
934
InfoGlobalsCmd(dummy, interp, objc, objv)
935
    ClientData dummy;           /* Not used. */
936
    Tcl_Interp *interp;         /* Current interpreter. */
937
    int objc;                   /* Number of arguments. */
938
    Tcl_Obj *CONST objv[];      /* Argument objects. */
939
{
940
    char *varName, *pattern;
941
    Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
942
    register Tcl_HashEntry *entryPtr;
943
    Tcl_HashSearch search;
944
    Var *varPtr;
945
    Tcl_Obj *listPtr;
946
 
947
    if (objc == 2) {
948
        pattern = NULL;
949
    } else if (objc == 3) {
950
        pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
951
    } else {
952
        Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
953
        return TCL_ERROR;
954
    }
955
 
956
    /*
957
     * Scan through the global :: namespace's variable table and create a
958
     * list of all global variables that match the pattern.
959
     */
960
 
961
    listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
962
    for (entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
963
            entryPtr != NULL;
964
            entryPtr = Tcl_NextHashEntry(&search)) {
965
        varPtr = (Var *) Tcl_GetHashValue(entryPtr);
966
        if (TclIsVarUndefined(varPtr)) {
967
            continue;
968
        }
969
        varName = Tcl_GetHashKey(&globalNsPtr->varTable, entryPtr);
970
        if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
971
            Tcl_ListObjAppendElement(interp, listPtr,
972
                    Tcl_NewStringObj(varName, -1));
973
        }
974
    }
975
    Tcl_SetObjResult(interp, listPtr);
976
    return TCL_OK;
977
}
978
 
979
/*
980
 *----------------------------------------------------------------------
981
 *
982
 * InfoHostnameCmd --
983
 *
984
 *      Called to implement the "info hostname" command that returns the
985
 *      host name. Handles the following syntax:
986
 *
987
 *          info hostname
988
 *
989
 * Results:
990
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
991
 *
992
 * Side effects:
993
 *      Returns a result in the interpreter's result object. If there is
994
 *      an error, the result is an error message.
995
 *
996
 *----------------------------------------------------------------------
997
 */
998
 
999
static int
1000
InfoHostnameCmd(dummy, interp, objc, objv)
1001
    ClientData dummy;           /* Not used. */
1002
    Tcl_Interp *interp;         /* Current interpreter. */
1003
    int objc;                   /* Number of arguments. */
1004
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1005
{
1006
    char *name;
1007
    if (objc != 2) {
1008
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
1009
        return TCL_ERROR;
1010
    }
1011
 
1012
    name = Tcl_GetHostName();
1013
    if (name) {
1014
        Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1);
1015
        return TCL_OK;
1016
    } else {
1017
        Tcl_SetStringObj(Tcl_GetObjResult(interp),
1018
                "unable to determine name of host", -1);
1019
        return TCL_ERROR;
1020
    }
1021
}
1022
 
1023
/*
1024
 *----------------------------------------------------------------------
1025
 *
1026
 * InfoLevelCmd --
1027
 *
1028
 *      Called to implement the "info level" command that returns
1029
 *      information about the call stack. Handles the following syntax:
1030
 *
1031
 *          info level ?number?
1032
 *
1033
 * Results:
1034
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1035
 *
1036
 * Side effects:
1037
 *      Returns a result in the interpreter's result object. If there is
1038
 *      an error, the result is an error message.
1039
 *
1040
 *----------------------------------------------------------------------
1041
 */
1042
 
1043
static int
1044
InfoLevelCmd(dummy, interp, objc, objv)
1045
    ClientData dummy;           /* Not used. */
1046
    Tcl_Interp *interp;         /* Current interpreter. */
1047
    int objc;                   /* Number of arguments. */
1048
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1049
{
1050
    Interp *iPtr = (Interp *) interp;
1051
    int level;
1052
    CallFrame *framePtr;
1053
    Tcl_Obj *listPtr;
1054
 
1055
    if (objc == 2) {            /* just "info level" */
1056
        if (iPtr->varFramePtr == NULL) {
1057
            Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
1058
        } else {
1059
            Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->varFramePtr->level);
1060
        }
1061
        return TCL_OK;
1062
    } else if (objc == 3) {
1063
        if (Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK) {
1064
            return TCL_ERROR;
1065
        }
1066
        if (level <= 0) {
1067
            if (iPtr->varFramePtr == NULL) {
1068
                levelError:
1069
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1070
                        "bad level \"",
1071
                        Tcl_GetStringFromObj(objv[2], (int *) NULL),
1072
                        "\"", (char *) NULL);
1073
                return TCL_ERROR;
1074
            }
1075
            level += iPtr->varFramePtr->level;
1076
        }
1077
        for (framePtr = iPtr->varFramePtr;  framePtr != NULL;
1078
                framePtr = framePtr->callerVarPtr) {
1079
            if (framePtr->level == level) {
1080
                break;
1081
            }
1082
        }
1083
        if (framePtr == NULL) {
1084
            goto levelError;
1085
        }
1086
 
1087
        listPtr = Tcl_NewListObj(framePtr->objc, framePtr->objv);
1088
        Tcl_SetObjResult(interp, listPtr);
1089
        return TCL_OK;
1090
    }
1091
 
1092
    Tcl_WrongNumArgs(interp, 2, objv, "?number?");
1093
    return TCL_ERROR;
1094
}
1095
 
1096
/*
1097
 *----------------------------------------------------------------------
1098
 *
1099
 * InfoLibraryCmd --
1100
 *
1101
 *      Called to implement the "info library" command that returns the
1102
 *      library directory for the Tcl installation. Handles the following
1103
 *      syntax:
1104
 *
1105
 *          info library
1106
 *
1107
 * Results:
1108
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1109
 *
1110
 * Side effects:
1111
 *      Returns a result in the interpreter's result object. If there is
1112
 *      an error, the result is an error message.
1113
 *
1114
 *----------------------------------------------------------------------
1115
 */
1116
 
1117
static int
1118
InfoLibraryCmd(dummy, interp, objc, objv)
1119
    ClientData dummy;           /* Not used. */
1120
    Tcl_Interp *interp;         /* Current interpreter. */
1121
    int objc;                   /* Number of arguments. */
1122
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1123
{
1124
    char *libDirName;
1125
 
1126
    if (objc != 2) {
1127
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
1128
        return TCL_ERROR;
1129
    }
1130
 
1131
    libDirName = Tcl_GetVar(interp, "tcl_library", TCL_GLOBAL_ONLY);
1132
    if (libDirName != NULL) {
1133
        Tcl_SetStringObj(Tcl_GetObjResult(interp), libDirName, -1);
1134
        return TCL_OK;
1135
    }
1136
    Tcl_SetStringObj(Tcl_GetObjResult(interp),
1137
            "no library has been specified for Tcl", -1);
1138
    return TCL_ERROR;
1139
}
1140
 
1141
/*
1142
 *----------------------------------------------------------------------
1143
 *
1144
 * InfoLoadedCmd --
1145
 *
1146
 *      Called to implement the "info loaded" command that returns the
1147
 *      packages that have been loaded into an interpreter. Handles the
1148
 *      following syntax:
1149
 *
1150
 *          info loaded ?interp?
1151
 *
1152
 * Results:
1153
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1154
 *
1155
 * Side effects:
1156
 *      Returns a result in the interpreter's result object. If there is
1157
 *      an error, the result is an error message.
1158
 *
1159
 *----------------------------------------------------------------------
1160
 */
1161
 
1162
static int
1163
InfoLoadedCmd(dummy, interp, objc, objv)
1164
    ClientData dummy;           /* Not used. */
1165
    Tcl_Interp *interp;         /* Current interpreter. */
1166
    int objc;                   /* Number of arguments. */
1167
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1168
{
1169
    char *interpName;
1170
    int result;
1171
 
1172
    if ((objc != 2) && (objc != 3)) {
1173
        Tcl_WrongNumArgs(interp, 2, objv, "?interp?");
1174
        return TCL_ERROR;
1175
    }
1176
 
1177
    if (objc == 2) {            /* get loaded pkgs in all interpreters */
1178
        interpName = NULL;
1179
    } else {                    /* get pkgs just in specified interp */
1180
        interpName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
1181
    }
1182
    result = TclGetLoadedPackages(interp, interpName);
1183
    return result;
1184
}
1185
 
1186
/*
1187
 *----------------------------------------------------------------------
1188
 *
1189
 * InfoLocalsCmd --
1190
 *
1191
 *      Called to implement the "info locals" command to return a list of
1192
 *      local variables that match an optional pattern. Handles the
1193
 *      following syntax:
1194
 *
1195
 *          info locals ?pattern?
1196
 *
1197
 * Results:
1198
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1199
 *
1200
 * Side effects:
1201
 *      Returns a result in the interpreter's result object. If there is
1202
 *      an error, the result is an error message.
1203
 *
1204
 *----------------------------------------------------------------------
1205
 */
1206
 
1207
static int
1208
InfoLocalsCmd(dummy, interp, objc, objv)
1209
    ClientData dummy;           /* Not used. */
1210
    Tcl_Interp *interp;         /* Current interpreter. */
1211
    int objc;                   /* Number of arguments. */
1212
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1213
{
1214
    Interp *iPtr = (Interp *) interp;
1215
    char *pattern;
1216
    Tcl_Obj *listPtr;
1217
 
1218
    if (objc == 2) {
1219
        pattern = NULL;
1220
    } else if (objc == 3) {
1221
        pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
1222
    } else {
1223
        Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
1224
        return TCL_ERROR;
1225
    }
1226
 
1227
    if (iPtr->varFramePtr == NULL || !iPtr->varFramePtr->isProcCallFrame) {
1228
        return TCL_OK;
1229
    }
1230
 
1231
    /*
1232
     * Return a list containing names of first the compiled locals (i.e. the
1233
     * ones stored in the call frame), then the variables in the local hash
1234
     * table (if one exists).
1235
     */
1236
 
1237
    listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1238
    AppendLocals(interp, listPtr, pattern, 0);
1239
    Tcl_SetObjResult(interp, listPtr);
1240
    return TCL_OK;
1241
}
1242
 
1243
/*
1244
 *----------------------------------------------------------------------
1245
 *
1246
 * AppendLocals --
1247
 *
1248
 *      Append the local variables for the current frame to the
1249
 *      specified list object.
1250
 *
1251
 * Results:
1252
 *      None.
1253
 *
1254
 * Side effects:
1255
 *      None.
1256
 *
1257
 *----------------------------------------------------------------------
1258
 */
1259
 
1260
static void
1261
AppendLocals(interp, listPtr, pattern, includeLinks)
1262
    Tcl_Interp *interp;         /* Current interpreter. */
1263
    Tcl_Obj *listPtr;           /* List object to append names to. */
1264
    char *pattern;              /* Pattern to match against. */
1265
    int includeLinks;           /* 1 if upvars should be included, else 0. */
1266
{
1267
    Interp *iPtr = (Interp *) interp;
1268
    CompiledLocal *localPtr;
1269
    Var *varPtr;
1270
    int i, localVarCt;
1271
    char *varName;
1272
    Tcl_HashTable *localVarTablePtr;
1273
    register Tcl_HashEntry *entryPtr;
1274
    Tcl_HashSearch search;
1275
 
1276
    localPtr = iPtr->varFramePtr->procPtr->firstLocalPtr;
1277
    localVarCt = iPtr->varFramePtr->numCompiledLocals;
1278
    varPtr = iPtr->varFramePtr->compiledLocals;
1279
    localVarTablePtr = iPtr->varFramePtr->varTablePtr;
1280
 
1281
    for (i = 0; i < localVarCt; i++) {
1282
        /*
1283
         * Skip nameless (temporary) variables and undefined variables
1284
         */
1285
 
1286
        if (!TclIsVarTemporary(localPtr) && !TclIsVarUndefined(varPtr)) {
1287
            varName = varPtr->name;
1288
            if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
1289
                Tcl_ListObjAppendElement(interp, listPtr,
1290
                        Tcl_NewStringObj(varName, -1));
1291
            }
1292
        }
1293
        varPtr++;
1294
        localPtr = localPtr->nextPtr;
1295
    }
1296
 
1297
    if (localVarTablePtr != NULL) {
1298
        for (entryPtr = Tcl_FirstHashEntry(localVarTablePtr, &search);
1299
                entryPtr != NULL;
1300
                entryPtr = Tcl_NextHashEntry(&search)) {
1301
            varPtr = (Var *) Tcl_GetHashValue(entryPtr);
1302
            if (!TclIsVarUndefined(varPtr)
1303
                    && (includeLinks || !TclIsVarLink(varPtr))) {
1304
                varName = Tcl_GetHashKey(localVarTablePtr, entryPtr);
1305
                if ((pattern == NULL)
1306
                        || Tcl_StringMatch(varName, pattern)) {
1307
                    Tcl_ListObjAppendElement(interp, listPtr,
1308
                            Tcl_NewStringObj(varName, -1));
1309
                }
1310
            }
1311
        }
1312
    }
1313
}
1314
 
1315
/*
1316
 *----------------------------------------------------------------------
1317
 *
1318
 * InfoNameOfExecutableCmd --
1319
 *
1320
 *      Called to implement the "info nameofexecutable" command that returns
1321
 *      the name of the binary file running this application. Handles the
1322
 *      following syntax:
1323
 *
1324
 *          info nameofexecutable
1325
 *
1326
 * Results:
1327
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1328
 *
1329
 * Side effects:
1330
 *      Returns a result in the interpreter's result object. If there is
1331
 *      an error, the result is an error message.
1332
 *
1333
 *----------------------------------------------------------------------
1334
 */
1335
 
1336
static int
1337
InfoNameOfExecutableCmd(dummy, interp, objc, objv)
1338
    ClientData dummy;           /* Not used. */
1339
    Tcl_Interp *interp;         /* Current interpreter. */
1340
    int objc;                   /* Number of arguments. */
1341
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1342
{
1343
    CONST char *nameOfExecutable;
1344
 
1345
    if (objc != 2) {
1346
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
1347
        return TCL_ERROR;
1348
    }
1349
 
1350
    nameOfExecutable = Tcl_GetNameOfExecutable();
1351
 
1352
    if (nameOfExecutable != NULL) {
1353
        Tcl_SetStringObj(Tcl_GetObjResult(interp), (char *)nameOfExecutable, -1);
1354
    }
1355
    return TCL_OK;
1356
}
1357
 
1358
/*
1359
 *----------------------------------------------------------------------
1360
 *
1361
 * InfoPatchLevelCmd --
1362
 *
1363
 *      Called to implement the "info patchlevel" command that returns the
1364
 *      default value for an argument to a procedure. Handles the following
1365
 *      syntax:
1366
 *
1367
 *          info patchlevel
1368
 *
1369
 * Results:
1370
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1371
 *
1372
 * Side effects:
1373
 *      Returns a result in the interpreter's result object. If there is
1374
 *      an error, the result is an error message.
1375
 *
1376
 *----------------------------------------------------------------------
1377
 */
1378
 
1379
static int
1380
InfoPatchLevelCmd(dummy, interp, objc, objv)
1381
    ClientData dummy;           /* Not used. */
1382
    Tcl_Interp *interp;         /* Current interpreter. */
1383
    int objc;                   /* Number of arguments. */
1384
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1385
{
1386
    char *patchlevel;
1387
 
1388
    if (objc != 2) {
1389
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
1390
        return TCL_ERROR;
1391
    }
1392
 
1393
    patchlevel = Tcl_GetVar(interp, "tcl_patchLevel",
1394
            (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
1395
    if (patchlevel != NULL) {
1396
        Tcl_SetStringObj(Tcl_GetObjResult(interp), patchlevel, -1);
1397
        return TCL_OK;
1398
    }
1399
    return TCL_ERROR;
1400
}
1401
 
1402
/*
1403
 *----------------------------------------------------------------------
1404
 *
1405
 * InfoProcsCmd --
1406
 *
1407
 *      Called to implement the "info procs" command that returns the
1408
 *      procedures in the current namespace that match an optional pattern.
1409
 *      Handles the following syntax:
1410
 *
1411
 *          info procs ?pattern?
1412
 *
1413
 * Results:
1414
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1415
 *
1416
 * Side effects:
1417
 *      Returns a result in the interpreter's result object. If there is
1418
 *      an error, the result is an error message.
1419
 *
1420
 *----------------------------------------------------------------------
1421
 */
1422
 
1423
static int
1424
InfoProcsCmd(dummy, interp, objc, objv)
1425
    ClientData dummy;           /* Not used. */
1426
    Tcl_Interp *interp;         /* Current interpreter. */
1427
    int objc;                   /* Number of arguments. */
1428
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1429
{
1430
    char *cmdName, *pattern;
1431
    Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
1432
    register Tcl_HashEntry *entryPtr;
1433
    Tcl_HashSearch search;
1434
    Command *cmdPtr;
1435
    Tcl_Obj *listPtr;
1436
 
1437
    if (objc == 2) {
1438
        pattern = NULL;
1439
    } else if (objc == 3) {
1440
        pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
1441
    } else {
1442
        Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
1443
        return TCL_ERROR;
1444
    }
1445
 
1446
    /*
1447
     * Scan through the current namespace's command table and return a list
1448
     * of all procs that match the pattern.
1449
     */
1450
 
1451
    listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1452
    for (entryPtr = Tcl_FirstHashEntry(&currNsPtr->cmdTable, &search);
1453
            entryPtr != NULL;
1454
            entryPtr = Tcl_NextHashEntry(&search)) {
1455
        cmdName = Tcl_GetHashKey(&currNsPtr->cmdTable, entryPtr);
1456
        cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
1457
        if (TclIsProc(cmdPtr)) {
1458
            if ((pattern == NULL) || Tcl_StringMatch(cmdName, pattern)) {
1459
                Tcl_ListObjAppendElement(interp, listPtr,
1460
                        Tcl_NewStringObj(cmdName, -1));
1461
            }
1462
        }
1463
    }
1464
    Tcl_SetObjResult(interp, listPtr);
1465
    return TCL_OK;
1466
}
1467
 
1468
/*
1469
 *----------------------------------------------------------------------
1470
 *
1471
 * InfoScriptCmd --
1472
 *
1473
 *      Called to implement the "info script" command that returns the
1474
 *      script file that is currently being evaluated. Handles the
1475
 *      following syntax:
1476
 *
1477
 *          info script
1478
 *
1479
 * Results:
1480
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1481
 *
1482
 * Side effects:
1483
 *      Returns a result in the interpreter's result object. If there is
1484
 *      an error, the result is an error message.
1485
 *
1486
 *----------------------------------------------------------------------
1487
 */
1488
 
1489
static int
1490
InfoScriptCmd(dummy, interp, objc, objv)
1491
    ClientData dummy;           /* Not used. */
1492
    Tcl_Interp *interp;         /* Current interpreter. */
1493
    int objc;                   /* Number of arguments. */
1494
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1495
{
1496
    Interp *iPtr = (Interp *) interp;
1497
    if (objc != 2) {
1498
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
1499
        return TCL_ERROR;
1500
    }
1501
 
1502
    if (iPtr->scriptFile != NULL) {
1503
        Tcl_SetStringObj(Tcl_GetObjResult(interp), iPtr->scriptFile, -1);
1504
    }
1505
    return TCL_OK;
1506
}
1507
 
1508
/*
1509
 *----------------------------------------------------------------------
1510
 *
1511
 * InfoSharedlibCmd --
1512
 *
1513
 *      Called to implement the "info sharedlibextension" command that
1514
 *      returns the file extension used for shared libraries. Handles the
1515
 *      following syntax:
1516
 *
1517
 *          info sharedlibextension
1518
 *
1519
 * Results:
1520
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1521
 *
1522
 * Side effects:
1523
 *      Returns a result in the interpreter's result object. If there is
1524
 *      an error, the result is an error message.
1525
 *
1526
 *----------------------------------------------------------------------
1527
 */
1528
 
1529
static int
1530
InfoSharedlibCmd(dummy, interp, objc, objv)
1531
    ClientData dummy;           /* Not used. */
1532
    Tcl_Interp *interp;         /* Current interpreter. */
1533
    int objc;                   /* Number of arguments. */
1534
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1535
{
1536
    if (objc != 2) {
1537
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
1538
        return TCL_ERROR;
1539
    }
1540
 
1541
#ifdef TCL_SHLIB_EXT
1542
    Tcl_SetStringObj(Tcl_GetObjResult(interp), TCL_SHLIB_EXT, -1);
1543
#endif
1544
    return TCL_OK;
1545
}
1546
 
1547
/*
1548
 *----------------------------------------------------------------------
1549
 *
1550
 * InfoTclVersionCmd --
1551
 *
1552
 *      Called to implement the "info tclversion" command that returns the
1553
 *      version number for this Tcl library. Handles the following syntax:
1554
 *
1555
 *          info tclversion
1556
 *
1557
 * Results:
1558
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1559
 *
1560
 * Side effects:
1561
 *      Returns a result in the interpreter's result object. If there is
1562
 *      an error, the result is an error message.
1563
 *
1564
 *----------------------------------------------------------------------
1565
 */
1566
 
1567
static int
1568
InfoTclVersionCmd(dummy, interp, objc, objv)
1569
    ClientData dummy;           /* Not used. */
1570
    Tcl_Interp *interp;         /* Current interpreter. */
1571
    int objc;                   /* Number of arguments. */
1572
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1573
{
1574
    char *version;
1575
 
1576
    if (objc != 2) {
1577
        Tcl_WrongNumArgs(interp, 2, objv, NULL);
1578
        return TCL_ERROR;
1579
    }
1580
 
1581
    version = Tcl_GetVar(interp, "tcl_version",
1582
        (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
1583
    if (version != NULL) {
1584
        Tcl_SetStringObj(Tcl_GetObjResult(interp), version, -1);
1585
        return TCL_OK;
1586
    }
1587
    return TCL_ERROR;
1588
}
1589
 
1590
/*
1591
 *----------------------------------------------------------------------
1592
 *
1593
 * InfoVarsCmd --
1594
 *
1595
 *      Called to implement the "info vars" command that returns the
1596
 *      list of variables in the interpreter that match an optional pattern.
1597
 *      The pattern, if any, consists of an optional sequence of namespace
1598
 *      names separated by "::" qualifiers, which is followed by a
1599
 *      glob-style pattern that restricts which variables are returned.
1600
 *      Handles the following syntax:
1601
 *
1602
 *          info vars ?pattern?
1603
 *
1604
 * Results:
1605
 *      Returns TCL_OK is successful and TCL_ERROR is there is an error.
1606
 *
1607
 * Side effects:
1608
 *      Returns a result in the interpreter's result object. If there is
1609
 *      an error, the result is an error message.
1610
 *
1611
 *----------------------------------------------------------------------
1612
 */
1613
 
1614
static int
1615
InfoVarsCmd(dummy, interp, objc, objv)
1616
    ClientData dummy;           /* Not used. */
1617
    Tcl_Interp *interp;         /* Current interpreter. */
1618
    int objc;                   /* Number of arguments. */
1619
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1620
{
1621
    Interp *iPtr = (Interp *) interp;
1622
    char *varName, *pattern, *simplePattern;
1623
    register Tcl_HashEntry *entryPtr;
1624
    Tcl_HashSearch search;
1625
    Var *varPtr;
1626
    Namespace *nsPtr;
1627
    Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
1628
    Namespace *currNsPtr   = (Namespace *) Tcl_GetCurrentNamespace(interp);
1629
    Tcl_Obj *listPtr, *elemObjPtr;
1630
    int specificNsInPattern = 0;  /* Init. to avoid compiler warning. */
1631
    int result;
1632
 
1633
    /*
1634
     * Get the pattern and find the "effective namespace" in which to
1635
     * list variables. We only use this effective namespace if there's
1636
     * no active Tcl procedure frame.
1637
     */
1638
 
1639
    if (objc == 2) {
1640
        simplePattern = NULL;
1641
        nsPtr = currNsPtr;
1642
        specificNsInPattern = 0;
1643
    } else if (objc == 3) {
1644
        /*
1645
         * From the pattern, get the effective namespace and the simple
1646
         * pattern (no namespace qualifiers or ::'s) at the end. If an
1647
         * error was found while parsing the pattern, return it. Otherwise,
1648
         * if the namespace wasn't found, just leave nsPtr NULL: we will
1649
         * return an empty list since no variables there can be found.
1650
         */
1651
 
1652
        Namespace *dummy1NsPtr, *dummy2NsPtr;
1653
 
1654
        pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
1655
        result = TclGetNamespaceForQualName(interp, pattern,
1656
                (Namespace *) NULL, /*flags*/ TCL_LEAVE_ERR_MSG,
1657
                &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern);
1658
        if (result != TCL_OK) {
1659
            return TCL_ERROR;
1660
        }
1661
        if (nsPtr != NULL) {    /* we successfully found the pattern's ns */
1662
            specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
1663
        }
1664
    } else {
1665
        Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
1666
        return TCL_ERROR;
1667
    }
1668
 
1669
    /*
1670
     * If the namespace specified in the pattern wasn't found, just return.
1671
     */
1672
 
1673
    if (nsPtr == NULL) {
1674
        return TCL_OK;
1675
    }
1676
 
1677
    listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1678
 
1679
    if ((iPtr->varFramePtr == NULL)
1680
            || !iPtr->varFramePtr->isProcCallFrame
1681
            || specificNsInPattern) {
1682
        /*
1683
         * There is no frame pointer, the frame pointer was pushed only
1684
         * to activate a namespace, or we are in a procedure call frame
1685
         * but a specific namespace was specified. Create a list containing
1686
         * only the variables in the effective namespace's variable table.
1687
         */
1688
 
1689
        entryPtr = Tcl_FirstHashEntry(&nsPtr->varTable, &search);
1690
        while (entryPtr != NULL) {
1691
            varPtr = (Var *) Tcl_GetHashValue(entryPtr);
1692
            if (!TclIsVarUndefined(varPtr)
1693
                    || (varPtr->flags & VAR_NAMESPACE_VAR)) {
1694
                varName = Tcl_GetHashKey(&nsPtr->varTable, entryPtr);
1695
                if ((simplePattern == NULL)
1696
                        || Tcl_StringMatch(varName, simplePattern)) {
1697
                    if (specificNsInPattern) {
1698
                        elemObjPtr = Tcl_NewObj();
1699
                        Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr,
1700
                                elemObjPtr);
1701
                    } else {
1702
                        elemObjPtr = Tcl_NewStringObj(varName, -1);
1703
                    }
1704
                    Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
1705
                }
1706
            }
1707
            entryPtr = Tcl_NextHashEntry(&search);
1708
        }
1709
 
1710
        /*
1711
         * If the effective namespace isn't the global :: namespace, and a
1712
         * specific namespace wasn't requested in the pattern (i.e., the
1713
         * pattern only specifies variable names), then add in all global ::
1714
         * variables that match the simple pattern. Of course, add in only
1715
         * those variables that aren't hidden by a variable in the effective
1716
         * namespace.
1717
         */
1718
 
1719
        if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
1720
            entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
1721
            while (entryPtr != NULL) {
1722
                varPtr = (Var *) Tcl_GetHashValue(entryPtr);
1723
                if (!TclIsVarUndefined(varPtr)
1724
                        || (varPtr->flags & VAR_NAMESPACE_VAR)) {
1725
                    varName = Tcl_GetHashKey(&globalNsPtr->varTable,
1726
                            entryPtr);
1727
                    if ((simplePattern == NULL)
1728
                            || Tcl_StringMatch(varName, simplePattern)) {
1729
                        if (Tcl_FindHashEntry(&nsPtr->varTable, varName) == NULL) {
1730
                            Tcl_ListObjAppendElement(interp, listPtr,
1731
                                    Tcl_NewStringObj(varName, -1));
1732
                        }
1733
                    }
1734
                }
1735
                entryPtr = Tcl_NextHashEntry(&search);
1736
            }
1737
        }
1738
    } else {
1739
        AppendLocals(interp, listPtr, simplePattern, 1);
1740
    }
1741
 
1742
    Tcl_SetObjResult(interp, listPtr);
1743
    return TCL_OK;
1744
}
1745
 
1746
/*
1747
 *----------------------------------------------------------------------
1748
 *
1749
 * Tcl_JoinObjCmd --
1750
 *
1751
 *      This procedure is invoked to process the "join" Tcl command.
1752
 *      See the user documentation for details on what it does.
1753
 *
1754
 * Results:
1755
 *      A standard Tcl object result.
1756
 *
1757
 * Side effects:
1758
 *      See the user documentation.
1759
 *
1760
 *----------------------------------------------------------------------
1761
 */
1762
 
1763
        /* ARGSUSED */
1764
int
1765
Tcl_JoinObjCmd(dummy, interp, objc, objv)
1766
    ClientData dummy;           /* Not used. */
1767
    Tcl_Interp *interp;         /* Current interpreter. */
1768
    int objc;                   /* Number of arguments. */
1769
    Tcl_Obj *CONST objv[];      /* The argument objects. */
1770
{
1771
    char *joinString, *bytes;
1772
    int joinLength, listLen, length, i, result;
1773
    Tcl_Obj **elemPtrs;
1774
    Tcl_Obj *resObjPtr;
1775
 
1776
    if (objc == 2) {
1777
        joinString = " ";
1778
        joinLength = 1;
1779
    } else if (objc == 3) {
1780
        joinString = Tcl_GetStringFromObj(objv[2], &joinLength);
1781
    } else {
1782
        Tcl_WrongNumArgs(interp, 1, objv, "list ?joinString?");
1783
        return TCL_ERROR;
1784
    }
1785
 
1786
    /*
1787
     * Make sure the list argument is a list object and get its length and
1788
     * a pointer to its array of element pointers.
1789
     */
1790
 
1791
    result = Tcl_ListObjGetElements(interp, objv[1], &listLen, &elemPtrs);
1792
    if (result != TCL_OK) {
1793
        return result;
1794
    }
1795
 
1796
    /*
1797
     * Now concatenate strings to form the "joined" result. We append
1798
     * directly into the interpreter's result object.
1799
     */
1800
 
1801
    resObjPtr = Tcl_GetObjResult(interp);
1802
 
1803
    for (i = 0;  i < listLen;  i++) {
1804
        bytes = Tcl_GetStringFromObj(elemPtrs[i], &length);
1805
        if (i > 0) {
1806
            Tcl_AppendToObj(resObjPtr, joinString, joinLength);
1807
        }
1808
        Tcl_AppendToObj(resObjPtr, bytes, length);
1809
    }
1810
    return TCL_OK;
1811
}
1812
 
1813
/*
1814
 *----------------------------------------------------------------------
1815
 *
1816
 * Tcl_LindexObjCmd --
1817
 *
1818
 *      This object-based procedure is invoked to process the "lindex" Tcl
1819
 *      command. See the user documentation for details on what it does.
1820
 *
1821
 * Results:
1822
 *      A standard Tcl object result.
1823
 *
1824
 * Side effects:
1825
 *      See the user documentation.
1826
 *
1827
 *----------------------------------------------------------------------
1828
 */
1829
 
1830
    /* ARGSUSED */
1831
int
1832
Tcl_LindexObjCmd(dummy, interp, objc, objv)
1833
    ClientData dummy;           /* Not used. */
1834
    Tcl_Interp *interp;         /* Current interpreter. */
1835
    int objc;                   /* Number of arguments. */
1836
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1837
{
1838
    Tcl_Obj *listPtr;
1839
    Tcl_Obj **elemPtrs;
1840
    int listLen, index, result;
1841
 
1842
    if (objc != 3) {
1843
        Tcl_WrongNumArgs(interp, 1, objv, "list index");
1844
        return TCL_ERROR;
1845
    }
1846
 
1847
    /*
1848
     * Convert the first argument to a list if necessary.
1849
     */
1850
 
1851
    listPtr = objv[1];
1852
    result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
1853
    if (result != TCL_OK) {
1854
        return result;
1855
    }
1856
 
1857
    /*
1858
     * Get the index from objv[2].
1859
     */
1860
 
1861
    result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
1862
            &index);
1863
    if (result != TCL_OK) {
1864
        return result;
1865
    }
1866
    if ((index < 0) || (index >= listLen)) {
1867
        /*
1868
         * The index is out of range: the result is an empty string object.
1869
         */
1870
 
1871
        return TCL_OK;
1872
    }
1873
 
1874
    /*
1875
     * Make sure listPtr still refers to a list object. It might have been
1876
     * converted to an int above if the argument objects were shared.
1877
     */
1878
 
1879
    if (listPtr->typePtr != &tclListType) {
1880
        result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
1881
                &elemPtrs);
1882
        if (result != TCL_OK) {
1883
            return result;
1884
        }
1885
    }
1886
 
1887
    /*
1888
     * Set the interpreter's object result to the index-th list element.
1889
     */
1890
 
1891
    Tcl_SetObjResult(interp, elemPtrs[index]);
1892
    return TCL_OK;
1893
}
1894
 
1895
/*
1896
 *----------------------------------------------------------------------
1897
 *
1898
 * Tcl_LinsertObjCmd --
1899
 *
1900
 *      This object-based procedure is invoked to process the "linsert" Tcl
1901
 *      command. See the user documentation for details on what it does.
1902
 *
1903
 * Results:
1904
 *      A new Tcl list object formed by inserting zero or more elements
1905
 *      into a list.
1906
 *
1907
 * Side effects:
1908
 *      See the user documentation.
1909
 *
1910
 *----------------------------------------------------------------------
1911
 */
1912
 
1913
        /* ARGSUSED */
1914
int
1915
Tcl_LinsertObjCmd(dummy, interp, objc, objv)
1916
    ClientData dummy;           /* Not used. */
1917
    Tcl_Interp *interp;         /* Current interpreter. */
1918
    register int objc;          /* Number of arguments. */
1919
    Tcl_Obj *CONST objv[];      /* Argument objects. */
1920
{
1921
    Tcl_Obj *listPtr, *resultPtr;
1922
    Tcl_ObjType *typePtr;
1923
    int index, isDuplicate, len, result;
1924
 
1925
    if (objc < 4) {
1926
        Tcl_WrongNumArgs(interp, 1, objv, "list index element ?element ...?");
1927
        return TCL_ERROR;
1928
    }
1929
 
1930
    /*
1931
     * Get the index first since, if a conversion to int is needed, it
1932
     * will invalidate the list's internal representation.
1933
     */
1934
 
1935
    result = TclGetIntForIndex(interp, objv[2], /*endValue*/ INT_MAX,
1936
            &index);
1937
    if (result != TCL_OK) {
1938
        return result;
1939
    }
1940
 
1941
    /*
1942
     * If the list object is unshared we can modify it directly. Otherwise
1943
     * we create a copy to modify: this is "copy on write". We create the
1944
     * duplicate directly in the interpreter's object result.
1945
     */
1946
 
1947
    listPtr = objv[1];
1948
    isDuplicate = 0;
1949
    if (Tcl_IsShared(listPtr)) {
1950
        /*
1951
         * The following code must reflect the logic in Tcl_DuplicateObj()
1952
         * except that it must duplicate the list object directly into the
1953
         * interpreter's result.
1954
         */
1955
 
1956
        Tcl_ResetResult(interp);
1957
        resultPtr = Tcl_GetObjResult(interp);
1958
        typePtr = listPtr->typePtr;
1959
        if (listPtr->bytes == NULL) {
1960
            resultPtr->bytes = NULL;
1961
        } else if (listPtr->bytes != tclEmptyStringRep) {
1962
            len = listPtr->length;
1963
            TclInitStringRep(resultPtr, listPtr->bytes, len);
1964
        }
1965
        if (typePtr != NULL) {
1966
            if (typePtr->dupIntRepProc == NULL) {
1967
                resultPtr->internalRep = listPtr->internalRep;
1968
                resultPtr->typePtr = typePtr;
1969
            } else {
1970
                (*typePtr->dupIntRepProc)(listPtr, resultPtr);
1971
            }
1972
        }
1973
        listPtr = resultPtr;
1974
        isDuplicate = 1;
1975
    }
1976
 
1977
    if ((objc == 4) && (index == INT_MAX)) {
1978
        /*
1979
         * Special case: insert one element at the end of the list.
1980
         */
1981
 
1982
        result = Tcl_ListObjAppendElement(interp, listPtr, objv[3]);
1983
    } else if (objc > 3) {
1984
        result = Tcl_ListObjReplace(interp, listPtr, index, 0,
1985
                                    (objc-3), &(objv[3]));
1986
    }
1987
    if (result != TCL_OK) {
1988
        return result;
1989
    }
1990
 
1991
    /*
1992
     * Set the interpreter's object result.
1993
     */
1994
 
1995
    if (!isDuplicate) {
1996
        Tcl_SetObjResult(interp, listPtr);
1997
    }
1998
    return TCL_OK;
1999
}
2000
 
2001
/*
2002
 *----------------------------------------------------------------------
2003
 *
2004
 * Tcl_ListObjCmd --
2005
 *
2006
 *      This procedure is invoked to process the "list" Tcl command.
2007
 *      See the user documentation for details on what it does.
2008
 *
2009
 * Results:
2010
 *      A standard Tcl object result.
2011
 *
2012
 * Side effects:
2013
 *      See the user documentation.
2014
 *
2015
 *----------------------------------------------------------------------
2016
 */
2017
 
2018
        /* ARGSUSED */
2019
int
2020
Tcl_ListObjCmd(dummy, interp, objc, objv)
2021
    ClientData dummy;                   /* Not used. */
2022
    Tcl_Interp *interp;                 /* Current interpreter. */
2023
    register int objc;                  /* Number of arguments. */
2024
    register Tcl_Obj *CONST objv[];     /* The argument objects. */
2025
{
2026
    /*
2027
     * If there are no list elements, the result is an empty object.
2028
     * Otherwise modify the interpreter's result object to be a list object.
2029
     */
2030
 
2031
    if (objc > 1) {
2032
        Tcl_SetListObj(Tcl_GetObjResult(interp), (objc-1), &(objv[1]));
2033
    }
2034
    return TCL_OK;
2035
}
2036
 
2037
/*
2038
 *----------------------------------------------------------------------
2039
 *
2040
 * Tcl_LlengthObjCmd --
2041
 *
2042
 *      This object-based procedure is invoked to process the "llength" Tcl
2043
 *      command.  See the user documentation for details on what it does.
2044
 *
2045
 * Results:
2046
 *      A standard Tcl object result.
2047
 *
2048
 * Side effects:
2049
 *      See the user documentation.
2050
 *
2051
 *----------------------------------------------------------------------
2052
 */
2053
 
2054
        /* ARGSUSED */
2055
int
2056
Tcl_LlengthObjCmd(dummy, interp, objc, objv)
2057
    ClientData dummy;                   /* Not used. */
2058
    Tcl_Interp *interp;                 /* Current interpreter. */
2059
    int objc;                           /* Number of arguments. */
2060
    register Tcl_Obj *CONST objv[];     /* Argument objects. */
2061
{
2062
    int listLen, result;
2063
 
2064
    if (objc != 2) {
2065
        Tcl_WrongNumArgs(interp, 1, objv, "list");
2066
        return TCL_ERROR;
2067
    }
2068
 
2069
    result = Tcl_ListObjLength(interp, objv[1], &listLen);
2070
    if (result != TCL_OK) {
2071
        return result;
2072
    }
2073
 
2074
    /*
2075
     * Set the interpreter's object result to an integer object holding the
2076
     * length.
2077
     */
2078
 
2079
    Tcl_SetIntObj(Tcl_GetObjResult(interp), listLen);
2080
    return TCL_OK;
2081
}
2082
 
2083
/*
2084
 *----------------------------------------------------------------------
2085
 *
2086
 * Tcl_LrangeObjCmd --
2087
 *
2088
 *      This procedure is invoked to process the "lrange" Tcl command.
2089
 *      See the user documentation for details on what it does.
2090
 *
2091
 * Results:
2092
 *      A standard Tcl object result.
2093
 *
2094
 * Side effects:
2095
 *      See the user documentation.
2096
 *
2097
 *----------------------------------------------------------------------
2098
 */
2099
 
2100
        /* ARGSUSED */
2101
int
2102
Tcl_LrangeObjCmd(notUsed, interp, objc, objv)
2103
    ClientData notUsed;                 /* Not used. */
2104
    Tcl_Interp *interp;                 /* Current interpreter. */
2105
    int objc;                           /* Number of arguments. */
2106
    register Tcl_Obj *CONST objv[];     /* Argument objects. */
2107
{
2108
    Tcl_Obj *listPtr;
2109
    Tcl_Obj **elemPtrs;
2110
    int listLen, first, last, numElems, result;
2111
 
2112
    if (objc != 4) {
2113
        Tcl_WrongNumArgs(interp, 1, objv, "list first last");
2114
        return TCL_ERROR;
2115
    }
2116
 
2117
    /*
2118
     * Make sure the list argument is a list object and get its length and
2119
     * a pointer to its array of element pointers.
2120
     */
2121
 
2122
    listPtr = objv[1];
2123
    result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
2124
    if (result != TCL_OK) {
2125
        return result;
2126
    }
2127
 
2128
    /*
2129
     * Get the first and last indexes.
2130
     */
2131
 
2132
    result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
2133
            &first);
2134
    if (result != TCL_OK) {
2135
        return result;
2136
    }
2137
    if (first < 0) {
2138
        first = 0;
2139
    }
2140
 
2141
    result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
2142
            &last);
2143
    if (result != TCL_OK) {
2144
        return result;
2145
    }
2146
    if (last >= listLen) {
2147
        last = (listLen - 1);
2148
    }
2149
 
2150
    if (first > last) {
2151
        return TCL_OK;          /* the result is an empty object */
2152
    }
2153
 
2154
    /*
2155
     * Make sure listPtr still refers to a list object. It might have been
2156
     * converted to an int above if the argument objects were shared.
2157
     */
2158
 
2159
    if (listPtr->typePtr != &tclListType) {
2160
        result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
2161
                &elemPtrs);
2162
        if (result != TCL_OK) {
2163
            return result;
2164
        }
2165
    }
2166
 
2167
    /*
2168
     * Extract a range of fields. We modify the interpreter's result object
2169
     * to be a list object containing the specified elements.
2170
     */
2171
 
2172
    numElems = (last - first + 1);
2173
    Tcl_SetListObj(Tcl_GetObjResult(interp), numElems, &(elemPtrs[first]));
2174
    return TCL_OK;
2175
}
2176
 
2177
/*
2178
 *----------------------------------------------------------------------
2179
 *
2180
 * Tcl_LreplaceObjCmd --
2181
 *
2182
 *      This object-based procedure is invoked to process the "lreplace"
2183
 *      Tcl command. See the user documentation for details on what it does.
2184
 *
2185
 * Results:
2186
 *      A new Tcl list object formed by replacing zero or more elements of
2187
 *      a list.
2188
 *
2189
 * Side effects:
2190
 *      See the user documentation.
2191
 *
2192
 *----------------------------------------------------------------------
2193
 */
2194
 
2195
        /* ARGSUSED */
2196
int
2197
Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
2198
    ClientData dummy;           /* Not used. */
2199
    Tcl_Interp *interp;         /* Current interpreter. */
2200
    int objc;                   /* Number of arguments. */
2201
    Tcl_Obj *CONST objv[];      /* Argument objects. */
2202
{
2203
    register Tcl_Obj *listPtr;
2204
    int createdNewObj, first, last, listLen, numToDelete;
2205
    int firstArgLen, result;
2206
    char *firstArg;
2207
 
2208
    if (objc < 4) {
2209
        Tcl_WrongNumArgs(interp, 1, objv,
2210
                "list first last ?element element ...?");
2211
        return TCL_ERROR;
2212
    }
2213
 
2214
    /*
2215
     * If the list object is unshared we can modify it directly, otherwise
2216
     * we create a copy to modify: this is "copy on write".
2217
     */
2218
 
2219
    listPtr = objv[1];
2220
    createdNewObj = 0;
2221
    if (Tcl_IsShared(listPtr)) {
2222
        listPtr = Tcl_DuplicateObj(listPtr);
2223
        createdNewObj = 1;
2224
    }
2225
    result = Tcl_ListObjLength(interp, listPtr, &listLen);
2226
    if (result != TCL_OK) {
2227
        errorReturn:
2228
        if (createdNewObj) {
2229
            Tcl_DecrRefCount(listPtr); /* free unneeded obj */
2230
        }
2231
        return result;
2232
    }
2233
 
2234
    /*
2235
     * Get the first and last indexes.
2236
     */
2237
 
2238
    result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
2239
            &first);
2240
    if (result != TCL_OK) {
2241
        goto errorReturn;
2242
    }
2243
    firstArg = Tcl_GetStringFromObj(objv[2], &firstArgLen);
2244
 
2245
    result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
2246
            &last);
2247
    if (result != TCL_OK) {
2248
        goto errorReturn;
2249
    }
2250
 
2251
    if (first < 0)  {
2252
        first = 0;
2253
    }
2254
    if ((first >= listLen) && (listLen > 0)
2255
            && (strncmp(firstArg, "end", (unsigned) firstArgLen) != 0)) {
2256
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2257
                "list doesn't contain element ",
2258
                Tcl_GetStringFromObj(objv[2], (int *) NULL), (int *) NULL);
2259
        result = TCL_ERROR;
2260
        goto errorReturn;
2261
    }
2262
    if (last >= listLen) {
2263
        last = (listLen - 1);
2264
    }
2265
    if (first <= last) {
2266
        numToDelete = (last - first + 1);
2267
    } else {
2268
        numToDelete = 0;
2269
    }
2270
 
2271
    if (objc > 4) {
2272
        result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
2273
                (objc-4), &(objv[4]));
2274
    } else {
2275
        result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
2276
                0, NULL);
2277
    }
2278
    if (result != TCL_OK) {
2279
        goto errorReturn;
2280
    }
2281
 
2282
    /*
2283
     * Set the interpreter's object result.
2284
     */
2285
 
2286
    Tcl_SetObjResult(interp, listPtr);
2287
    return TCL_OK;
2288
}
2289
 
2290
/*
2291
 *----------------------------------------------------------------------
2292
 *
2293
 * Tcl_LsearchObjCmd --
2294
 *
2295
 *      This procedure is invoked to process the "lsearch" Tcl command.
2296
 *      See the user documentation for details on what it does.
2297
 *
2298
 * Results:
2299
 *      A standard Tcl result.
2300
 *
2301
 * Side effects:
2302
 *      See the user documentation.
2303
 *
2304
 *----------------------------------------------------------------------
2305
 */
2306
 
2307
int
2308
Tcl_LsearchObjCmd(clientData, interp, objc, objv)
2309
    ClientData clientData;      /* Not used. */
2310
    Tcl_Interp *interp;         /* Current interpreter. */
2311
    int objc;                   /* Number of arguments. */
2312
    Tcl_Obj *CONST objv[];      /* Argument values. */
2313
{
2314
#define EXACT   0
2315
#define GLOB    1
2316
#define REGEXP  2
2317
#define DICTIONARY 3
2318
#define NOCASE  4
2319
    char *bytes, *patternBytes;
2320
    int i, match, mode, index, result, listLen, length, elemLen;
2321
    Tcl_Obj **elemPtrs;
2322
    static char *switches[] =
2323
            {"-exact", "-glob", "-regexp", "-dictionary", "-nocase", (char *) NULL};
2324
 
2325
    mode = GLOB;
2326
    if (objc == 4) {
2327
        if (Tcl_GetIndexFromObj(interp, objv[1], switches,
2328
                "search mode", 0, &mode) != TCL_OK) {
2329
            return TCL_ERROR;
2330
        }
2331
    } else if (objc != 3) {
2332
        Tcl_WrongNumArgs(interp, 1, objv, "?mode? list pattern");
2333
        return TCL_ERROR;
2334
    }
2335
 
2336
    /*
2337
     * Make sure the list argument is a list object and get its length and
2338
     * a pointer to its array of element pointers.
2339
     */
2340
 
2341
    result = Tcl_ListObjGetElements(interp, objv[objc-2], &listLen, &elemPtrs);
2342
    if (result != TCL_OK) {
2343
        return result;
2344
    }
2345
 
2346
    patternBytes = Tcl_GetStringFromObj(objv[objc-1], &length);
2347
 
2348
    index = -1;
2349
    for (i = 0; i < listLen; i++) {
2350
        match = 0;
2351
        bytes = Tcl_GetStringFromObj(elemPtrs[i], &elemLen);
2352
        switch (mode) {
2353
            case EXACT:
2354
                if (length == elemLen) {
2355
                    match = (memcmp(bytes, patternBytes,
2356
                            (size_t) length) == 0);
2357
                }
2358
                break;
2359
            case GLOB:
2360
                /*
2361
                 * WARNING: will not work with data containing NULLs.
2362
                 */
2363
                match = Tcl_StringMatch(bytes, patternBytes);
2364
                break;
2365
            case REGEXP:
2366
                /*
2367
                 * WARNING: will not work with data containing NULLs.
2368
                 */
2369
                match = Tcl_RegExpMatch(interp, bytes, patternBytes);
2370
                if (match < 0) {
2371
                    return TCL_ERROR;
2372
                }
2373
                break;
2374
             case DICTIONARY:
2375
             case NOCASE:
2376
#if defined(__MSVC__) || defined(_MSC_VER)
2377
                match = strnicmp (bytes, patternBytes, length) == 0;
2378
#else
2379
                match = strncasecmp (bytes, patternBytes, length) == 0;
2380
#endif
2381
                break;
2382
        }
2383
        if (match) {
2384
            index = i;
2385
            break;
2386
        }
2387
    }
2388
 
2389
    Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
2390
    return TCL_OK;
2391
}
2392
 
2393
/*
2394
 *----------------------------------------------------------------------
2395
 *
2396
 * Tcl_LsortObjCmd --
2397
 *
2398
 *      This procedure is invoked to process the "lsort" Tcl command.
2399
 *      See the user documentation for details on what it does.
2400
 *
2401
 * Results:
2402
 *      A standard Tcl result.
2403
 *
2404
 * Side effects:
2405
 *      See the user documentation.
2406
 *
2407
 *----------------------------------------------------------------------
2408
 */
2409
 
2410
int
2411
Tcl_LsortObjCmd(clientData, interp, objc, objv)
2412
    ClientData clientData;      /* Not used. */
2413
    Tcl_Interp *interp;         /* Current interpreter. */
2414
    int objc;                   /* Number of arguments. */
2415
    Tcl_Obj *CONST objv[];      /* Argument values. */
2416
{
2417
    int i, index, dummy;
2418
    Tcl_Obj *resultPtr;
2419
    int length;
2420
    Tcl_Obj *cmdPtr, **listObjPtrs;
2421
    SortElement *elementArray;
2422
    SortElement *elementPtr;
2423
    SortInfo sortInfo;                  /* Information about this sort that
2424
                                         * needs to be passed to the
2425
                                         * comparison function */
2426
    static char *switches[] =
2427
            {"-ascii", "-command", "-decreasing", "-dictionary",
2428
            "-increasing", "-index", "-integer", "-real", (char *) NULL};
2429
 
2430
    resultPtr = Tcl_GetObjResult(interp);
2431
    if (objc < 2) {
2432
        Tcl_WrongNumArgs(interp, 1, objv, "?options? list");
2433
        return TCL_ERROR;
2434
    }
2435
 
2436
    /*
2437
     * Parse arguments to set up the mode for the sort.
2438
     */
2439
 
2440
    sortInfo.isIncreasing = 1;
2441
    sortInfo.sortMode = SORTMODE_ASCII;
2442
    sortInfo.index = -1;
2443
    sortInfo.interp = interp;
2444
    sortInfo.resultCode = TCL_OK;
2445
    cmdPtr = NULL;
2446
    for (i = 1; i < objc-1; i++) {
2447
        if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0, &index)
2448
                != TCL_OK) {
2449
            return TCL_ERROR;
2450
        }
2451
        switch (index) {
2452
            case 0:                      /* -ascii */
2453
                sortInfo.sortMode = SORTMODE_ASCII;
2454
                break;
2455
            case 1:                     /* -command */
2456
                if (i == (objc-2)) {
2457
                    Tcl_AppendToObj(resultPtr,
2458
                            "\"-command\" option must be followed by comparison command",
2459
                            -1);
2460
                    return TCL_ERROR;
2461
                }
2462
                sortInfo.sortMode = SORTMODE_COMMAND;
2463
                cmdPtr = objv[i+1];
2464
                i++;
2465
                break;
2466
            case 2:                     /* -decreasing */
2467
                sortInfo.isIncreasing = 0;
2468
                break;
2469
            case 3:                     /* -dictionary */
2470
                sortInfo.sortMode = SORTMODE_DICTIONARY;
2471
                break;
2472
            case 4:                     /* -increasing */
2473
                sortInfo.isIncreasing = 1;
2474
                break;
2475
            case 5:                     /* -index */
2476
                if (i == (objc-2)) {
2477
                    Tcl_AppendToObj(resultPtr,
2478
                            "\"-index\" option must be followed by list index",
2479
                            -1);
2480
                    return TCL_ERROR;
2481
                }
2482
                if (TclGetIntForIndex(interp, objv[i+1], -2, &sortInfo.index)
2483
                        != TCL_OK) {
2484
                    return TCL_ERROR;
2485
                }
2486
                cmdPtr = objv[i+1];
2487
                i++;
2488
                break;
2489
            case 6:                     /* -integer */
2490
                sortInfo.sortMode = SORTMODE_INTEGER;
2491
                break;
2492
            case 7:                     /* -real */
2493
                sortInfo.sortMode = SORTMODE_REAL;
2494
                break;
2495
        }
2496
    }
2497
    if (sortInfo.sortMode == SORTMODE_COMMAND) {
2498
        Tcl_DStringInit(&sortInfo.compareCmd);
2499
        Tcl_DStringAppend(&sortInfo.compareCmd,
2500
                Tcl_GetStringFromObj(cmdPtr, &dummy), -1);
2501
    }
2502
 
2503
    sortInfo.resultCode = Tcl_ListObjGetElements(interp, objv[objc-1],
2504
            &length, &listObjPtrs);
2505
    if (sortInfo.resultCode != TCL_OK) {
2506
        goto done;
2507
    }
2508
    if (length <= 0) {
2509
        return TCL_OK;
2510
    }
2511
    elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
2512
    for (i=0; i < length; i++){
2513
        elementArray[i].objPtr = listObjPtrs[i];
2514
        elementArray[i].nextPtr = &elementArray[i+1];
2515
    }
2516
    elementArray[length-1].nextPtr = NULL;
2517
    elementPtr = MergeSort(elementArray, &sortInfo);
2518
    if (sortInfo.resultCode == TCL_OK) {
2519
        /*
2520
         * Note: must clear the interpreter's result object: it could
2521
         * have been set by the -command script.
2522
         */
2523
 
2524
        Tcl_ResetResult(interp);
2525
        resultPtr = Tcl_GetObjResult(interp);
2526
        for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
2527
            Tcl_ListObjAppendElement(interp, resultPtr, elementPtr->objPtr);
2528
        }
2529
    }
2530
    ckfree((char*) elementArray);
2531
 
2532
    done:
2533
    if (sortInfo.sortMode == SORTMODE_COMMAND) {
2534
        Tcl_DStringFree(&sortInfo.compareCmd);
2535
    }
2536
    return sortInfo.resultCode;
2537
}
2538
 
2539
/*
2540
 *----------------------------------------------------------------------
2541
 *
2542
 * MergeSort -
2543
 *
2544
 *      This procedure sorts a linked list of SortElement structures
2545
 *      use the merge-sort algorithm.
2546
 *
2547
 * Results:
2548
 *      A pointer to the head of the list after sorting is returned.
2549
 *
2550
 * Side effects:
2551
 *      None, unless a user-defined comparison command does something
2552
 *      weird.
2553
 *
2554
 *----------------------------------------------------------------------
2555
 */
2556
 
2557
static SortElement *
2558
MergeSort(headPtr, infoPtr)
2559
    SortElement *headPtr;               /* First element on the list */
2560
    SortInfo *infoPtr;                  /* Information needed by the
2561
                                         * comparison operator */
2562
{
2563
    /*
2564
     * The subList array below holds pointers to temporary lists built
2565
     * during the merge sort.  Element i of the array holds a list of
2566
     * length 2**i.
2567
     */
2568
 
2569
#   define NUM_LISTS 30
2570
    SortElement *subList[NUM_LISTS];
2571
    SortElement *elementPtr;
2572
    int i;
2573
 
2574
    for(i = 0; i < NUM_LISTS; i++){
2575
        subList[i] = NULL;
2576
    }
2577
    while (headPtr != NULL) {
2578
        elementPtr = headPtr;
2579
        headPtr = headPtr->nextPtr;
2580
        elementPtr->nextPtr = 0;
2581
        for (i = 0; (i < NUM_LISTS) && (subList[i] != NULL); i++){
2582
            elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
2583
            subList[i] = NULL;
2584
        }
2585
        if (i >= NUM_LISTS) {
2586
            i = NUM_LISTS-1;
2587
        }
2588
        subList[i] = elementPtr;
2589
    }
2590
    elementPtr = NULL;
2591
    for (i = 0; i < NUM_LISTS; i++){
2592
        elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
2593
    }
2594
    return elementPtr;
2595
}
2596
 
2597
/*
2598
 *----------------------------------------------------------------------
2599
 *
2600
 * MergeLists -
2601
 *
2602
 *      This procedure combines two sorted lists of SortElement structures
2603
 *      into a single sorted list.
2604
 *
2605
 * Results:
2606
 *      The unified list of SortElement structures.
2607
 *
2608
 * Side effects:
2609
 *      None, unless a user-defined comparison command does something
2610
 *      weird.
2611
 *
2612
 *----------------------------------------------------------------------
2613
 */
2614
 
2615
static SortElement *
2616
MergeLists(leftPtr, rightPtr, infoPtr)
2617
    SortElement *leftPtr;               /* First list to be merged; may be
2618
                                         * NULL. */
2619
    SortElement *rightPtr;              /* Second list to be merged; may be
2620
                                         * NULL. */
2621
    SortInfo *infoPtr;                  /* Information needed by the
2622
                                         * comparison operator. */
2623
{
2624
    SortElement *headPtr;
2625
    SortElement *tailPtr;
2626
 
2627
    if (leftPtr == NULL) {
2628
        return rightPtr;
2629
    }
2630
    if (rightPtr == NULL) {
2631
        return leftPtr;
2632
    }
2633
    if (SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr) > 0) {
2634
        tailPtr = rightPtr;
2635
        rightPtr = rightPtr->nextPtr;
2636
    } else {
2637
        tailPtr = leftPtr;
2638
        leftPtr = leftPtr->nextPtr;
2639
    }
2640
    headPtr = tailPtr;
2641
    while ((leftPtr != NULL) && (rightPtr != NULL)) {
2642
        if (SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr) > 0) {
2643
            tailPtr->nextPtr = rightPtr;
2644
            tailPtr = rightPtr;
2645
            rightPtr = rightPtr->nextPtr;
2646
        } else {
2647
            tailPtr->nextPtr = leftPtr;
2648
            tailPtr = leftPtr;
2649
            leftPtr = leftPtr->nextPtr;
2650
        }
2651
    }
2652
    if (leftPtr != NULL) {
2653
       tailPtr->nextPtr = leftPtr;
2654
    } else {
2655
       tailPtr->nextPtr = rightPtr;
2656
    }
2657
    return headPtr;
2658
}
2659
 
2660
/*
2661
 *----------------------------------------------------------------------
2662
 *
2663
 * SortCompare --
2664
 *
2665
 *      This procedure is invoked by MergeLists to determine the proper
2666
 *      ordering between two elements.
2667
 *
2668
 * Results:
2669
 *      A negative results means the the first element comes before the
2670
 *      second, and a positive results means that the second element
2671
 *      should come first.  A result of zero means the two elements
2672
 *      are equal and it doesn't matter which comes first.
2673
 *
2674
 * Side effects:
2675
 *      None, unless a user-defined comparison command does something
2676
 *      weird.
2677
 *
2678
 *----------------------------------------------------------------------
2679
 */
2680
 
2681
static int
2682
SortCompare(objPtr1, objPtr2, infoPtr)
2683
    Tcl_Obj *objPtr1, *objPtr2;         /* Values to be compared. */
2684
    SortInfo *infoPtr;                  /* Information passed from the
2685
                                         * top-level "lsort" command */
2686
{
2687
    int order, dummy, listLen, index;
2688
    Tcl_Obj *objPtr;
2689
    char buffer[30];
2690
 
2691
    order = 0;
2692
    if (infoPtr->resultCode != TCL_OK) {
2693
        /*
2694
         * Once an error has occurred, skip any future comparisons
2695
         * so as to preserve the error message in sortInterp->result.
2696
         */
2697
 
2698
        return order;
2699
    }
2700
    if (infoPtr->index != -1) {
2701
        /*
2702
         * The "-index" option was specified.  Treat each object as a
2703
         * list, extract the requested element from each list, and
2704
         * compare the elements, not the lists.  The special index "end"
2705
         * is signaled here with a large negative index.
2706
         */
2707
 
2708
        if (Tcl_ListObjLength(infoPtr->interp, objPtr1, &listLen) != TCL_OK) {
2709
            infoPtr->resultCode = TCL_ERROR;
2710
            return order;
2711
        }
2712
        if (infoPtr->index < -1) {
2713
            index = listLen - 1;
2714
        } else {
2715
            index = infoPtr->index;
2716
        }
2717
 
2718
        if (Tcl_ListObjIndex(infoPtr->interp, objPtr1, index, &objPtr)
2719
                != TCL_OK) {
2720
            infoPtr->resultCode = TCL_ERROR;
2721
            return order;
2722
        }
2723
        if (objPtr == NULL) {
2724
            objPtr = objPtr1;
2725
            missingElement:
2726
            sprintf(buffer, "%d", infoPtr->index);
2727
            Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->interp),
2728
                        "element ", buffer, " missing from sublist \"",
2729
                        Tcl_GetStringFromObj(objPtr, (int *) NULL),
2730
                        "\"", (char *) NULL);
2731
            infoPtr->resultCode = TCL_ERROR;
2732
            return order;
2733
        }
2734
        objPtr1 = objPtr;
2735
 
2736
        if (Tcl_ListObjLength(infoPtr->interp, objPtr2, &listLen) != TCL_OK) {
2737
            infoPtr->resultCode = TCL_ERROR;
2738
            return order;
2739
        }
2740
        if (infoPtr->index < -1) {
2741
            index = listLen - 1;
2742
        } else {
2743
            index = infoPtr->index;
2744
        }
2745
 
2746
        if (Tcl_ListObjIndex(infoPtr->interp, objPtr2, index, &objPtr)
2747
                != TCL_OK) {
2748
            infoPtr->resultCode = TCL_ERROR;
2749
            return order;
2750
        }
2751
        if (objPtr == NULL) {
2752
            objPtr = objPtr2;
2753
            goto missingElement;
2754
        }
2755
        objPtr2 = objPtr;
2756
    }
2757
    if (infoPtr->sortMode == SORTMODE_ASCII) {
2758
        order = strcmp(Tcl_GetStringFromObj(objPtr1, &dummy),
2759
                Tcl_GetStringFromObj(objPtr2, &dummy));
2760
    } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) {
2761
        order = DictionaryCompare(
2762
                Tcl_GetStringFromObj(objPtr1, &dummy),
2763
                Tcl_GetStringFromObj(objPtr2, &dummy));
2764
    } else if (infoPtr->sortMode == SORTMODE_INTEGER) {
2765
        int a, b;
2766
 
2767
        if ((Tcl_GetIntFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
2768
                || (Tcl_GetIntFromObj(infoPtr->interp, objPtr2, &b)
2769
                != TCL_OK)) {
2770
            infoPtr->resultCode = TCL_ERROR;
2771
            return order;
2772
        }
2773
        if (a > b) {
2774
            order = 1;
2775
        } else if (b > a) {
2776
            order = -1;
2777
        }
2778
    } else if (infoPtr->sortMode == SORTMODE_REAL) {
2779
        double a, b;
2780
 
2781
        if ((Tcl_GetDoubleFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
2782
              || (Tcl_GetDoubleFromObj(infoPtr->interp, objPtr2, &b)
2783
                      != TCL_OK)) {
2784
            infoPtr->resultCode = TCL_ERROR;
2785
            return order;
2786
        }
2787
        if (a > b) {
2788
            order = 1;
2789
        } else if (b > a) {
2790
            order = -1;
2791
        }
2792
    } else {
2793
        int oldLength;
2794
 
2795
        /*
2796
         * Generate and evaluate a command to determine which string comes
2797
         * first.
2798
         */
2799
 
2800
        oldLength = Tcl_DStringLength(&infoPtr->compareCmd);
2801
        Tcl_DStringAppendElement(&infoPtr->compareCmd,
2802
                Tcl_GetStringFromObj(objPtr1, &dummy));
2803
        Tcl_DStringAppendElement(&infoPtr->compareCmd,
2804
                Tcl_GetStringFromObj(objPtr2, &dummy));
2805
        infoPtr->resultCode = Tcl_Eval(infoPtr->interp,
2806
                Tcl_DStringValue(&infoPtr->compareCmd));
2807
        Tcl_DStringTrunc(&infoPtr->compareCmd, oldLength);
2808
        if (infoPtr->resultCode != TCL_OK) {
2809
            Tcl_AddErrorInfo(infoPtr->interp,
2810
                    "\n    (-compare command)");
2811
            return order;
2812
        }
2813
 
2814
        /*
2815
         * Parse the result of the command.
2816
         */
2817
 
2818
        if (Tcl_GetIntFromObj(infoPtr->interp,
2819
                Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
2820
            Tcl_ResetResult(infoPtr->interp);
2821
            Tcl_AppendToObj(Tcl_GetObjResult(infoPtr->interp),
2822
                    "-compare command returned non-numeric result", -1);
2823
            infoPtr->resultCode = TCL_ERROR;
2824
            return order;
2825
        }
2826
    }
2827
    if (!infoPtr->isIncreasing) {
2828
        order = -order;
2829
    }
2830
    return order;
2831
}
2832
 
2833
/*
2834
 *----------------------------------------------------------------------
2835
 *
2836
 * DictionaryCompare
2837
 *
2838
 *      This function compares two strings as if they were being used in
2839
 *      an index or card catalog.  The case of alphabetic characters is
2840
 *      ignored, except to break ties.  Thus "B" comes before "b" but
2841
 *      after "a".  Also, integers embedded in the strings compare in
2842
 *      numerical order.  In other words, "x10y" comes after "x9y", not
2843
 *      before it as it would when using strcmp().
2844
 *
2845
 * Results:
2846
 *      A negative result means that the first element comes before the
2847
 *      second, and a positive result means that the second element
2848
 *      should come first.  A result of zero means the two elements
2849
 *      are equal and it doesn't matter which comes first.
2850
 *
2851
 * Side effects:
2852
 *      None.
2853
 *
2854
 *----------------------------------------------------------------------
2855
 */
2856
 
2857
static int
2858
DictionaryCompare(left, right)
2859
    char *left, *right;          /* The strings to compare */
2860
{
2861
    int diff, zeros;
2862
    int secondaryDiff = 0;
2863
 
2864
    while (1) {
2865
        if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) {
2866
            /*
2867
             * There are decimal numbers embedded in the two
2868
             * strings.  Compare them as numbers, rather than
2869
             * strings.  If one number has more leading zeros than
2870
             * the other, the number with more leading zeros sorts
2871
             * later, but only as a secondary choice.
2872
             */
2873
 
2874
            zeros = 0;
2875
            while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
2876
                right++;
2877
                zeros--;
2878
            }
2879
            while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
2880
                left++;
2881
                zeros++;
2882
            }
2883
            if (secondaryDiff == 0) {
2884
                secondaryDiff = zeros;
2885
            }
2886
 
2887
            /*
2888
             * The code below compares the numbers in the two
2889
             * strings without ever converting them to integers.  It
2890
             * does this by first comparing the lengths of the
2891
             * numbers and then comparing the digit values.
2892
             */
2893
 
2894
            diff = 0;
2895
            while (1) {
2896
                if (diff == 0) {
2897
                    diff = UCHAR(*left) - UCHAR(*right);
2898
                }
2899
                right++;
2900
                left++;
2901
                if (!isdigit(UCHAR(*right))) {
2902
                    if (isdigit(UCHAR(*left))) {
2903
                        return 1;
2904
                    } else {
2905
                        /*
2906
                         * The two numbers have the same length. See
2907
                         * if their values are different.
2908
                         */
2909
 
2910
                        if (diff != 0) {
2911
                            return diff;
2912
                        }
2913
                        break;
2914
                    }
2915
                } else if (!isdigit(UCHAR(*left))) {
2916
                    return -1;
2917
                }
2918
            }
2919
            continue;
2920
        }
2921
        diff = UCHAR(*left) - UCHAR(*right);
2922
        if (diff) {
2923
            if (isupper(UCHAR(*left)) && islower(UCHAR(*right))) {
2924
                diff = UCHAR(tolower(*left)) - UCHAR(*right);
2925
                if (diff) {
2926
                    return diff;
2927
                } else if (secondaryDiff == 0) {
2928
                    secondaryDiff = -1;
2929
                }
2930
            } else if (isupper(UCHAR(*right)) && islower(UCHAR(*left))) {
2931
                diff = UCHAR(*left) - UCHAR(tolower(UCHAR(*right)));
2932
                if (diff) {
2933
                    return diff;
2934
                } else if (secondaryDiff == 0) {
2935
                    secondaryDiff = 1;
2936
                }
2937
            } else {
2938
                return diff;
2939
            }
2940
        }
2941
        if (*left == 0) {
2942
            break;
2943
        }
2944
        left++;
2945
        right++;
2946
    }
2947
    if (diff == 0) {
2948
        diff = secondaryDiff;
2949
    }
2950
    return diff;
2951
}

powered by: WebSVN 2.1.0

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