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