Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclCmdIL.c
1810 views
1
/*
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) 1994-1995 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: @(#) tclCmdIL.c 1.120 96/07/10 17:16:03
16
*/
17
18
#include "tclInt.h"
19
#include "tclPort.h"
20
21
/*
22
* The following variable holds the full path name of the binary
23
* from which this application was executed, or NULL if it isn't
24
* know. The value of the variable is set by the procedure
25
* Tcl_FindExecutable. The storage space is dynamically allocated.
26
*/
27
28
char *tclExecutableName = NULL;
29
30
/*
31
* The variables below are used to implement the "lsort" command.
32
* Unfortunately, this use of static variables prevents "lsort"
33
* from being thread-safe, but there's no alternative given the
34
* current implementation of qsort. In a threaded environment
35
* these variables should be made thread-local if possible, or else
36
* "lsort" needs internal mutual exclusion.
37
*/
38
39
static Tcl_Interp *sortInterp = NULL; /* Interpreter for "lsort" command.
40
* NULL means no lsort is active. */
41
static enum {ASCII, INTEGER, REAL, COMMAND} sortMode;
42
/* Mode for sorting: compare as strings,
43
* compare as numbers, or call
44
* user-defined command for
45
* comparison. */
46
static Tcl_DString sortCmd; /* Holds command if mode is COMMAND.
47
* pre-initialized to hold base of
48
* command. */
49
static int sortIncreasing; /* 0 means sort in decreasing order,
50
* 1 means increasing order. */
51
static int sortCode; /* Anything other than TCL_OK means a
52
* problem occurred while sorting; this
53
* executing a comparison command, so
54
* the sort was aborted. */
55
56
/*
57
* Forward declarations for procedures defined in this file:
58
*/
59
60
static int SortCompareProc _ANSI_ARGS_((CONST VOID *first,
61
CONST VOID *second));
62
63
/*
64
*----------------------------------------------------------------------
65
*
66
* Tcl_IfCmd --
67
*
68
* This procedure is invoked to process the "if" Tcl command.
69
* See the user documentation for details on what it does.
70
*
71
* Results:
72
* A standard Tcl result.
73
*
74
* Side effects:
75
* See the user documentation.
76
*
77
*----------------------------------------------------------------------
78
*/
79
80
/* ARGSUSED */
81
int
82
Tcl_IfCmd(dummy, interp, argc, argv)
83
ClientData dummy; /* Not used. */
84
Tcl_Interp *interp; /* Current interpreter. */
85
int argc; /* Number of arguments. */
86
char **argv; /* Argument strings. */
87
{
88
int i, result, value;
89
90
i = 1;
91
while (1) {
92
/*
93
* At this point in the loop, argv and argc refer to an expression
94
* to test, either for the main expression or an expression
95
* following an "elseif". The arguments after the expression must
96
* be "then" (optional) and a script to execute if the expression is
97
* true.
98
*/
99
100
if (i >= argc) {
101
Tcl_AppendResult(interp, "wrong # args: no expression after \"",
102
argv[i-1], "\" argument", (char *) NULL);
103
return TCL_ERROR;
104
}
105
result = Tcl_ExprBoolean(interp, argv[i], &value);
106
if (result != TCL_OK) {
107
return result;
108
}
109
i++;
110
if ((i < argc) && (strcmp(argv[i], "then") == 0)) {
111
i++;
112
}
113
if (i >= argc) {
114
Tcl_AppendResult(interp, "wrong # args: no script following \"",
115
argv[i-1], "\" argument", (char *) NULL);
116
return TCL_ERROR;
117
}
118
if (value) {
119
return Tcl_Eval(interp, argv[i]);
120
}
121
122
/*
123
* The expression evaluated to false. Skip the command, then
124
* see if there is an "else" or "elseif" clause.
125
*/
126
127
i++;
128
if (i >= argc) {
129
return TCL_OK;
130
}
131
if ((argv[i][0] == 'e') && (strcmp(argv[i], "elseif") == 0)) {
132
i++;
133
continue;
134
}
135
break;
136
}
137
138
/*
139
* Couldn't find a "then" or "elseif" clause to execute. Check now
140
* for an "else" clause. We know that there's at least one more
141
* argument when we get here.
142
*/
143
144
if (strcmp(argv[i], "else") == 0) {
145
i++;
146
if (i >= argc) {
147
Tcl_AppendResult(interp,
148
"wrong # args: no script following \"else\" argument",
149
(char *) NULL);
150
return TCL_ERROR;
151
}
152
}
153
return Tcl_Eval(interp, argv[i]);
154
}
155
156
/*
157
*----------------------------------------------------------------------
158
*
159
* Tcl_IncrCmd --
160
*
161
* This procedure is invoked to process the "incr" Tcl command.
162
* See the user documentation for details on what it does.
163
*
164
* Results:
165
* A standard Tcl result.
166
*
167
* Side effects:
168
* See the user documentation.
169
*
170
*----------------------------------------------------------------------
171
*/
172
173
/* ARGSUSED */
174
int
175
Tcl_IncrCmd(dummy, interp, argc, argv)
176
ClientData dummy; /* Not used. */
177
Tcl_Interp *interp; /* Current interpreter. */
178
int argc; /* Number of arguments. */
179
char **argv; /* Argument strings. */
180
{
181
int value;
182
char *oldString, *result;
183
char newString[30];
184
185
if ((argc != 2) && (argc != 3)) {
186
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
187
" varName ?increment?\"", (char *) NULL);
188
return TCL_ERROR;
189
}
190
191
oldString = Tcl_GetVar(interp, argv[1], TCL_LEAVE_ERR_MSG);
192
if (oldString == NULL) {
193
return TCL_ERROR;
194
}
195
if (Tcl_GetInt(interp, oldString, &value) != TCL_OK) {
196
Tcl_AddErrorInfo(interp,
197
"\n (reading value of variable to increment)");
198
return TCL_ERROR;
199
}
200
if (argc == 2) {
201
value += 1;
202
} else {
203
int increment;
204
205
if (Tcl_GetInt(interp, argv[2], &increment) != TCL_OK) {
206
Tcl_AddErrorInfo(interp,
207
"\n (reading increment)");
208
return TCL_ERROR;
209
}
210
value += increment;
211
}
212
sprintf(newString, "%d", value);
213
result = Tcl_SetVar(interp, argv[1], newString, TCL_LEAVE_ERR_MSG);
214
if (result == NULL) {
215
return TCL_ERROR;
216
}
217
interp->result = result;
218
return TCL_OK;
219
}
220
221
/*
222
*----------------------------------------------------------------------
223
*
224
* Tcl_JoinCmd --
225
*
226
* This procedure is invoked to process the "join" Tcl command.
227
* See the user documentation for details on what it does.
228
*
229
* Results:
230
* A standard Tcl result.
231
*
232
* Side effects:
233
* See the user documentation.
234
*
235
*----------------------------------------------------------------------
236
*/
237
238
/* ARGSUSED */
239
int
240
Tcl_JoinCmd(dummy, interp, argc, argv)
241
ClientData dummy; /* Not used. */
242
Tcl_Interp *interp; /* Current interpreter. */
243
int argc; /* Number of arguments. */
244
char **argv; /* Argument strings. */
245
{
246
char *joinString;
247
char **listArgv;
248
int listArgc, i;
249
250
if (argc == 2) {
251
joinString = " ";
252
} else if (argc == 3) {
253
joinString = argv[2];
254
} else {
255
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
256
" list ?joinString?\"", (char *) NULL);
257
return TCL_ERROR;
258
}
259
260
if (Tcl_SplitList(interp, argv[1], &listArgc, &listArgv) != TCL_OK) {
261
return TCL_ERROR;
262
}
263
for (i = 0; i < listArgc; i++) {
264
if (i == 0) {
265
Tcl_AppendResult(interp, listArgv[0], (char *) NULL);
266
} else {
267
Tcl_AppendResult(interp, joinString, listArgv[i], (char *) NULL);
268
}
269
}
270
ckfree((char *) listArgv);
271
return TCL_OK;
272
}
273
274
/*
275
*----------------------------------------------------------------------
276
*
277
* Tcl_LindexCmd --
278
*
279
* This procedure is invoked to process the "lindex" Tcl command.
280
* See the user documentation for details on what it does.
281
*
282
* Results:
283
* A standard Tcl result.
284
*
285
* Side effects:
286
* See the user documentation.
287
*
288
*----------------------------------------------------------------------
289
*/
290
291
/* ARGSUSED */
292
int
293
Tcl_LindexCmd(dummy, interp, argc, argv)
294
ClientData dummy; /* Not used. */
295
Tcl_Interp *interp; /* Current interpreter. */
296
int argc; /* Number of arguments. */
297
char **argv; /* Argument strings. */
298
{
299
char *p, *element, *next;
300
int index, size, parenthesized, result, returnLast;
301
302
if (argc != 3) {
303
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
304
" list index\"", (char *) NULL);
305
return TCL_ERROR;
306
}
307
if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
308
returnLast = 1;
309
index = INT_MAX;
310
} else {
311
returnLast = 0;
312
if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
313
return TCL_ERROR;
314
}
315
}
316
if (index < 0) {
317
return TCL_OK;
318
}
319
for (p = argv[1] ; index >= 0; index--) {
320
result = TclFindElement(interp, p, &element, &next, &size,
321
&parenthesized);
322
if (result != TCL_OK) {
323
return result;
324
}
325
if ((*next == 0) && returnLast) {
326
break;
327
}
328
p = next;
329
}
330
if (size == 0) {
331
return TCL_OK;
332
}
333
if (size >= TCL_RESULT_SIZE) {
334
interp->result = (char *) ckalloc((unsigned) size+1);
335
interp->freeProc = TCL_DYNAMIC;
336
}
337
if (parenthesized) {
338
memcpy((VOID *) interp->result, (VOID *) element, (size_t) size);
339
interp->result[size] = 0;
340
} else {
341
TclCopyAndCollapse(size, element, interp->result);
342
}
343
return TCL_OK;
344
}
345
346
/*
347
*----------------------------------------------------------------------
348
*
349
* Tcl_LinsertCmd --
350
*
351
* This procedure is invoked to process the "linsert" Tcl command.
352
* See the user documentation for details on what it does.
353
*
354
* Results:
355
* A standard Tcl result.
356
*
357
* Side effects:
358
* See the user documentation.
359
*
360
*----------------------------------------------------------------------
361
*/
362
363
/* ARGSUSED */
364
int
365
Tcl_LinsertCmd(dummy, interp, argc, argv)
366
ClientData dummy; /* Not used. */
367
Tcl_Interp *interp; /* Current interpreter. */
368
int argc; /* Number of arguments. */
369
char **argv; /* Argument strings. */
370
{
371
char *p, *element, savedChar;
372
int i, index, count, result, size;
373
374
if (argc < 4) {
375
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
376
" list index element ?element ...?\"", (char *) NULL);
377
return TCL_ERROR;
378
}
379
if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
380
index = INT_MAX;
381
} else if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
382
return TCL_ERROR;
383
}
384
385
/*
386
* Skip over the first "index" elements of the list, then add
387
* all of those elements to the result.
388
*/
389
390
size = 0;
391
element = argv[1];
392
for (count = 0, p = argv[1]; (count < index) && (*p != 0); count++) {
393
result = TclFindElement(interp, p, &element, &p, &size, (int *) NULL);
394
if (result != TCL_OK) {
395
return result;
396
}
397
}
398
if (*p == 0) {
399
Tcl_AppendResult(interp, argv[1], (char *) NULL);
400
} else {
401
char *end;
402
403
end = element+size;
404
if (element != argv[1]) {
405
while ((*end != 0) && !isspace(UCHAR(*end))) {
406
end++;
407
}
408
}
409
savedChar = *end;
410
*end = 0;
411
Tcl_AppendResult(interp, argv[1], (char *) NULL);
412
*end = savedChar;
413
}
414
415
/*
416
* Add the new list elements.
417
*/
418
419
for (i = 3; i < argc; i++) {
420
Tcl_AppendElement(interp, argv[i]);
421
}
422
423
/*
424
* Append the remainder of the original list.
425
*/
426
427
if (*p != 0) {
428
Tcl_AppendResult(interp, " ", p, (char *) NULL);
429
}
430
return TCL_OK;
431
}
432
433
/*
434
*----------------------------------------------------------------------
435
*
436
* Tcl_ListCmd --
437
*
438
* This procedure is invoked to process the "list" Tcl command.
439
* See the user documentation for details on what it does.
440
*
441
* Results:
442
* A standard Tcl result.
443
*
444
* Side effects:
445
* See the user documentation.
446
*
447
*----------------------------------------------------------------------
448
*/
449
450
/* ARGSUSED */
451
int
452
Tcl_ListCmd(dummy, interp, argc, argv)
453
ClientData dummy; /* Not used. */
454
Tcl_Interp *interp; /* Current interpreter. */
455
int argc; /* Number of arguments. */
456
char **argv; /* Argument strings. */
457
{
458
if (argc >= 2) {
459
interp->result = Tcl_Merge(argc-1, argv+1);
460
interp->freeProc = TCL_DYNAMIC;
461
}
462
return TCL_OK;
463
}
464
465
/*
466
*----------------------------------------------------------------------
467
*
468
* Tcl_LlengthCmd --
469
*
470
* This procedure is invoked to process the "llength" Tcl command.
471
* See the user documentation for details on what it does.
472
*
473
* Results:
474
* A standard Tcl result.
475
*
476
* Side effects:
477
* See the user documentation.
478
*
479
*----------------------------------------------------------------------
480
*/
481
482
/* ARGSUSED */
483
int
484
Tcl_LlengthCmd(dummy, interp, argc, argv)
485
ClientData dummy; /* Not used. */
486
Tcl_Interp *interp; /* Current interpreter. */
487
int argc; /* Number of arguments. */
488
char **argv; /* Argument strings. */
489
{
490
int count, result;
491
char *element, *p;
492
493
if (argc != 2) {
494
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
495
" list\"", (char *) NULL);
496
return TCL_ERROR;
497
}
498
for (count = 0, p = argv[1]; *p != 0 ; count++) {
499
result = TclFindElement(interp, p, &element, &p, (int *) NULL,
500
(int *) NULL);
501
if (result != TCL_OK) {
502
return result;
503
}
504
if (*element == 0) {
505
break;
506
}
507
}
508
sprintf(interp->result, "%d", count);
509
return TCL_OK;
510
}
511
512
/*
513
*----------------------------------------------------------------------
514
*
515
* Tcl_LrangeCmd --
516
*
517
* This procedure is invoked to process the "lrange" Tcl command.
518
* See the user documentation for details on what it does.
519
*
520
* Results:
521
* A standard Tcl result.
522
*
523
* Side effects:
524
* See the user documentation.
525
*
526
*----------------------------------------------------------------------
527
*/
528
529
/* ARGSUSED */
530
int
531
Tcl_LrangeCmd(notUsed, interp, argc, argv)
532
ClientData notUsed; /* Not used. */
533
Tcl_Interp *interp; /* Current interpreter. */
534
int argc; /* Number of arguments. */
535
char **argv; /* Argument strings. */
536
{
537
int first, last, result;
538
char *begin, *end, c, *dummy, *next;
539
int count, firstIsEnd;
540
541
if (argc != 4) {
542
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
543
" list first last\"", (char *) NULL);
544
return TCL_ERROR;
545
}
546
if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
547
firstIsEnd = 1;
548
first = INT_MAX;
549
} else {
550
firstIsEnd = 0;
551
if (Tcl_GetInt(interp, argv[2], &first) != TCL_OK) {
552
return TCL_ERROR;
553
}
554
}
555
if (first < 0) {
556
first = 0;
557
}
558
if ((*argv[3] == 'e') && (strncmp(argv[3], "end", strlen(argv[3])) == 0)) {
559
last = INT_MAX;
560
} else {
561
if (Tcl_GetInt(interp, argv[3], &last) != TCL_OK) {
562
Tcl_ResetResult(interp);
563
Tcl_AppendResult(interp, "expected integer or \"end\" but got \"",
564
argv[3], "\"", (char *) NULL);
565
return TCL_ERROR;
566
}
567
}
568
if ((first > last) && !firstIsEnd) {
569
return TCL_OK;
570
}
571
572
/*
573
* Extract a range of fields.
574
*/
575
576
for (count = 0, begin = argv[1]; count < first; begin = next, count++) {
577
result = TclFindElement(interp, begin, &dummy, &next, (int *) NULL,
578
(int *) NULL);
579
if (result != TCL_OK) {
580
return result;
581
}
582
if (*next == 0) {
583
if (firstIsEnd) {
584
first = count;
585
} else {
586
begin = next;
587
}
588
break;
589
}
590
}
591
for (count = first, end = begin; (count <= last) && (*end != 0);
592
count++) {
593
result = TclFindElement(interp, end, &dummy, &end, (int *) NULL,
594
(int *) NULL);
595
if (result != TCL_OK) {
596
return result;
597
}
598
}
599
if (end == begin) {
600
return TCL_OK;
601
}
602
603
/*
604
* Chop off trailing spaces.
605
*/
606
607
while ((end != begin) && (isspace(UCHAR(end[-1])))
608
&& (((end-1) == begin) || (end[-2] != '\\'))) {
609
end--;
610
}
611
c = *end;
612
*end = 0;
613
Tcl_SetResult(interp, begin, TCL_VOLATILE);
614
*end = c;
615
return TCL_OK;
616
}
617
618
/*
619
*----------------------------------------------------------------------
620
*
621
* Tcl_LreplaceCmd --
622
*
623
* This procedure is invoked to process the "lreplace" Tcl command.
624
* See the user documentation for details on what it does.
625
*
626
* Results:
627
* A standard Tcl result.
628
*
629
* Side effects:
630
* See the user documentation.
631
*
632
*----------------------------------------------------------------------
633
*/
634
635
/* ARGSUSED */
636
int
637
Tcl_LreplaceCmd(notUsed, interp, argc, argv)
638
ClientData notUsed; /* Not used. */
639
Tcl_Interp *interp; /* Current interpreter. */
640
int argc; /* Number of arguments. */
641
char **argv; /* Argument strings. */
642
{
643
char *p1, *p2, *element, savedChar, *dummy, *next;
644
int i, first, last, count, result, size, firstIsEnd;
645
646
if (argc < 4) {
647
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
648
" list first last ?element element ...?\"", (char *) NULL);
649
return TCL_ERROR;
650
}
651
if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
652
firstIsEnd = 1;
653
first = INT_MAX;
654
} else {
655
firstIsEnd = 0;
656
if (Tcl_GetInt(interp, argv[2], &first) != TCL_OK) {
657
Tcl_ResetResult(interp);
658
Tcl_AppendResult(interp, "bad index \"", argv[2],
659
"\": must be integer or \"end\"", (char *) NULL);
660
return TCL_ERROR;
661
}
662
}
663
if ((*argv[3] == 'e') && (strncmp(argv[3], "end", strlen(argv[3])) == 0)) {
664
last = INT_MAX;
665
} else {
666
if (Tcl_GetInt(interp, argv[3], &last) != TCL_OK) {
667
Tcl_ResetResult(interp);
668
Tcl_AppendResult(interp, "bad index \"", argv[3],
669
"\": must be integer or \"end\"", (char *) NULL);
670
return TCL_ERROR;
671
}
672
}
673
if (first < 0) {
674
first = 0;
675
}
676
677
/*
678
* Skip over the elements of the list before "first".
679
*/
680
681
size = 0;
682
element = argv[1];
683
for (count = 0, p1 = argv[1]; (count < first) && (*p1 != 0); count++) {
684
result = TclFindElement(interp, p1, &element, &next, &size,
685
(int *) NULL);
686
if (result != TCL_OK) {
687
return result;
688
}
689
if ((*next == 0) && firstIsEnd) {
690
break;
691
}
692
p1 = next;
693
}
694
if (*p1 == 0) {
695
Tcl_AppendResult(interp, "list doesn't contain element ",
696
argv[2], (char *) NULL);
697
return TCL_ERROR;
698
}
699
700
/*
701
* Skip over the elements of the list up through "last".
702
*/
703
704
for (p2 = p1 ; (count <= last) && (*p2 != 0); count++) {
705
result = TclFindElement(interp, p2, &dummy, &p2, (int *) NULL,
706
(int *) NULL);
707
if (result != TCL_OK) {
708
return result;
709
}
710
}
711
712
/*
713
* Add the elements before "first" to the result. Remove any
714
* trailing white space, to make the result look as clean as
715
* possible (this matters primarily if the replacement string is
716
* empty).
717
*/
718
719
while ((p1 != argv[1]) && (isspace(UCHAR(p1[-1])))
720
&& (((p1-1) == argv[1]) || (p1[-2] != '\\'))) {
721
p1--;
722
}
723
savedChar = *p1;
724
*p1 = 0;
725
Tcl_AppendResult(interp, argv[1], (char *) NULL);
726
*p1 = savedChar;
727
728
/*
729
* Add the new list elements.
730
*/
731
732
for (i = 4; i < argc; i++) {
733
Tcl_AppendElement(interp, argv[i]);
734
}
735
736
/*
737
* Append the remainder of the original list.
738
*/
739
740
if (*p2 != 0) {
741
if (*interp->result == 0) {
742
Tcl_SetResult(interp, p2, TCL_VOLATILE);
743
} else {
744
Tcl_AppendResult(interp, " ", p2, (char *) NULL);
745
}
746
}
747
return TCL_OK;
748
}
749
750
/*
751
*----------------------------------------------------------------------
752
*
753
* Tcl_LsearchCmd --
754
*
755
* This procedure is invoked to process the "lsearch" Tcl command.
756
* See the user documentation for details on what it does.
757
*
758
* Results:
759
* A standard Tcl result.
760
*
761
* Side effects:
762
* See the user documentation.
763
*
764
*----------------------------------------------------------------------
765
*/
766
767
/* ARGSUSED */
768
int
769
Tcl_LsearchCmd(notUsed, interp, argc, argv)
770
ClientData notUsed; /* Not used. */
771
Tcl_Interp *interp; /* Current interpreter. */
772
int argc; /* Number of arguments. */
773
char **argv; /* Argument strings. */
774
{
775
#define EXACT 0
776
#define GLOB 1
777
#define REGEXP 2
778
int listArgc;
779
char **listArgv;
780
int i, match, mode, index;
781
782
mode = GLOB;
783
if (argc == 4) {
784
if (strcmp(argv[1], "-exact") == 0) {
785
mode = EXACT;
786
} else if (strcmp(argv[1], "-glob") == 0) {
787
mode = GLOB;
788
} else if (strcmp(argv[1], "-regexp") == 0) {
789
mode = REGEXP;
790
} else {
791
Tcl_AppendResult(interp, "bad search mode \"", argv[1],
792
"\": must be -exact, -glob, or -regexp", (char *) NULL);
793
return TCL_ERROR;
794
}
795
} else if (argc != 3) {
796
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
797
" ?mode? list pattern\"", (char *) NULL);
798
return TCL_ERROR;
799
}
800
if (Tcl_SplitList(interp, argv[argc-2], &listArgc, &listArgv) != TCL_OK) {
801
return TCL_ERROR;
802
}
803
index = -1;
804
for (i = 0; i < listArgc; i++) {
805
match = 0;
806
switch (mode) {
807
case EXACT:
808
match = (strcmp(listArgv[i], argv[argc-1]) == 0);
809
break;
810
case GLOB:
811
match = Tcl_StringMatch(listArgv[i], argv[argc-1]);
812
break;
813
case REGEXP:
814
match = Tcl_RegExpMatch(interp, listArgv[i], argv[argc-1]);
815
if (match < 0) {
816
ckfree((char *) listArgv);
817
return TCL_ERROR;
818
}
819
break;
820
}
821
if (match) {
822
index = i;
823
break;
824
}
825
}
826
sprintf(interp->result, "%d", index);
827
ckfree((char *) listArgv);
828
return TCL_OK;
829
}
830
831
/*
832
*----------------------------------------------------------------------
833
*
834
* Tcl_LsortCmd --
835
*
836
* This procedure is invoked to process the "lsort" Tcl command.
837
* See the user documentation for details on what it does.
838
*
839
* Results:
840
* A standard Tcl result.
841
*
842
* Side effects:
843
* See the user documentation.
844
*
845
*----------------------------------------------------------------------
846
*/
847
848
/* ARGSUSED */
849
int
850
Tcl_LsortCmd(notUsed, interp, argc, argv)
851
ClientData notUsed; /* Not used. */
852
Tcl_Interp *interp; /* Current interpreter. */
853
int argc; /* Number of arguments. */
854
char **argv; /* Argument strings. */
855
{
856
int listArgc, i, c;
857
size_t length;
858
char **listArgv;
859
char *command = NULL; /* Initialization needed only to
860
* prevent compiler warning. */
861
862
if (argc < 2) {
863
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
864
" ?-ascii? ?-integer? ?-real? ?-increasing? ?-decreasing?",
865
" ?-command string? list\"", (char *) NULL);
866
return TCL_ERROR;
867
}
868
869
if (sortInterp != NULL) {
870
interp->result = "can't invoke \"lsort\" recursively";
871
return TCL_ERROR;
872
}
873
874
/*
875
* Parse arguments to set up the mode for the sort.
876
*/
877
878
sortInterp = interp;
879
sortMode = ASCII;
880
sortIncreasing = 1;
881
sortCode = TCL_OK;
882
for (i = 1; i < argc-1; i++) {
883
length = strlen(argv[i]);
884
if (length < 2) {
885
badSwitch:
886
Tcl_AppendResult(interp, "bad switch \"", argv[i],
887
"\": must be -ascii, -integer, -real, -increasing",
888
" -decreasing, or -command", (char *) NULL);
889
sortCode = TCL_ERROR;
890
goto done;
891
}
892
c = argv[i][1];
893
if ((c == 'a') && (strncmp(argv[i], "-ascii", length) == 0)) {
894
sortMode = ASCII;
895
} else if ((c == 'c') && (strncmp(argv[i], "-command", length) == 0)) {
896
if (i == argc-2) {
897
Tcl_AppendResult(interp, "\"-command\" must be",
898
" followed by comparison command", (char *) NULL);
899
sortCode = TCL_ERROR;
900
goto done;
901
}
902
sortMode = COMMAND;
903
command = argv[i+1];
904
i++;
905
} else if ((c == 'd')
906
&& (strncmp(argv[i], "-decreasing", length) == 0)) {
907
sortIncreasing = 0;
908
} else if ((c == 'i') && (length >= 4)
909
&& (strncmp(argv[i], "-increasing", length) == 0)) {
910
sortIncreasing = 1;
911
} else if ((c == 'i') && (length >= 4)
912
&& (strncmp(argv[i], "-integer", length) == 0)) {
913
sortMode = INTEGER;
914
} else if ((c == 'r')
915
&& (strncmp(argv[i], "-real", length) == 0)) {
916
sortMode = REAL;
917
} else {
918
goto badSwitch;
919
}
920
}
921
if (sortMode == COMMAND) {
922
Tcl_DStringInit(&sortCmd);
923
Tcl_DStringAppend(&sortCmd, command, -1);
924
}
925
926
if (Tcl_SplitList(interp, argv[argc-1], &listArgc, &listArgv) != TCL_OK) {
927
sortCode = TCL_ERROR;
928
goto done;
929
}
930
qsort((VOID *) listArgv, (size_t) listArgc, sizeof (char *),
931
SortCompareProc);
932
if (sortCode == TCL_OK) {
933
Tcl_ResetResult(interp);
934
interp->result = Tcl_Merge(listArgc, listArgv);
935
interp->freeProc = TCL_DYNAMIC;
936
}
937
if (sortMode == COMMAND) {
938
Tcl_DStringFree(&sortCmd);
939
}
940
ckfree((char *) listArgv);
941
942
done:
943
sortInterp = NULL;
944
return sortCode;
945
}
946
947
/*
948
*----------------------------------------------------------------------
949
*
950
* SortCompareProc --
951
*
952
* This procedure is invoked by qsort to determine the proper
953
* ordering between two elements.
954
*
955
* Results:
956
* < 0 means first is "smaller" than "second", > 0 means "first"
957
* is larger than "second", and 0 means they should be treated
958
* as equal.
959
*
960
* Side effects:
961
* None, unless a user-defined comparison command does something
962
* weird.
963
*
964
*----------------------------------------------------------------------
965
*/
966
967
static int
968
SortCompareProc(first, second)
969
CONST VOID *first, *second; /* Elements to be compared. */
970
{
971
int order;
972
char *firstString = *((char **) first);
973
char *secondString = *((char **) second);
974
975
order = 0;
976
if (sortCode != TCL_OK) {
977
/*
978
* Once an error has occurred, skip any future comparisons
979
* so as to preserve the error message in sortInterp->result.
980
*/
981
982
return order;
983
}
984
if (sortMode == ASCII) {
985
order = strcmp(firstString, secondString);
986
} else if (sortMode == INTEGER) {
987
int a, b;
988
989
if ((Tcl_GetInt(sortInterp, firstString, &a) != TCL_OK)
990
|| (Tcl_GetInt(sortInterp, secondString, &b) != TCL_OK)) {
991
Tcl_AddErrorInfo(sortInterp,
992
"\n (converting list element from string to integer)");
993
sortCode = TCL_ERROR;
994
return order;
995
}
996
if (a > b) {
997
order = 1;
998
} else if (b > a) {
999
order = -1;
1000
}
1001
} else if (sortMode == REAL) {
1002
double a, b;
1003
1004
if ((Tcl_GetDouble(sortInterp, firstString, &a) != TCL_OK)
1005
|| (Tcl_GetDouble(sortInterp, secondString, &b) != TCL_OK)) {
1006
Tcl_AddErrorInfo(sortInterp,
1007
"\n (converting list element from string to real)");
1008
sortCode = TCL_ERROR;
1009
return order;
1010
}
1011
if (a > b) {
1012
order = 1;
1013
} else if (b > a) {
1014
order = -1;
1015
}
1016
} else {
1017
int oldLength;
1018
char *end;
1019
1020
/*
1021
* Generate and evaluate a command to determine which string comes
1022
* first.
1023
*/
1024
1025
oldLength = Tcl_DStringLength(&sortCmd);
1026
Tcl_DStringAppendElement(&sortCmd, firstString);
1027
Tcl_DStringAppendElement(&sortCmd, secondString);
1028
sortCode = Tcl_Eval(sortInterp, Tcl_DStringValue(&sortCmd));
1029
Tcl_DStringTrunc(&sortCmd, oldLength);
1030
if (sortCode != TCL_OK) {
1031
Tcl_AddErrorInfo(sortInterp,
1032
"\n (user-defined comparison command)");
1033
return order;
1034
}
1035
1036
/*
1037
* Parse the result of the command.
1038
*/
1039
1040
order = strtol(sortInterp->result, &end, 0);
1041
if ((end == sortInterp->result) || (*end != 0)) {
1042
Tcl_ResetResult(sortInterp);
1043
Tcl_AppendResult(sortInterp,
1044
"comparison command returned non-numeric result",
1045
(char *) NULL);
1046
sortCode = TCL_ERROR;
1047
return order;
1048
}
1049
}
1050
if (!sortIncreasing) {
1051
order = -order;
1052
}
1053
return order;
1054
}
1055
1056