Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclCmdAH.c
1810 views
1
/*
2
* tclCmdAH.c --
3
*
4
* This file contains the top-level command routines for most of
5
* the Tcl built-in commands whose names begin with the letters
6
* A to H.
7
*
8
* Copyright (c) 1987-1993 The Regents of the University of California.
9
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
10
*
11
* See the file "license.terms" for information on usage and redistribution
12
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
*
14
* SCCS: @(#) tclCmdAH.c 1.111 96/07/30 09:33:59
15
*/
16
17
#include "tclInt.h"
18
#include "tclPort.h"
19
20
/*
21
* Prototypes for local procedures defined in this file:
22
*/
23
24
static char * GetTypeFromMode _ANSI_ARGS_((int mode));
25
static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
26
char *varName, struct stat *statPtr));
27
28
/*
29
*----------------------------------------------------------------------
30
*
31
* Tcl_BreakCmd --
32
*
33
* This procedure is invoked to process the "break" Tcl command.
34
* See the user documentation for details on what it does.
35
*
36
* Results:
37
* A standard Tcl result.
38
*
39
* Side effects:
40
* See the user documentation.
41
*
42
*----------------------------------------------------------------------
43
*/
44
45
/* ARGSUSED */
46
int
47
Tcl_BreakCmd(dummy, interp, argc, argv)
48
ClientData dummy; /* Not used. */
49
Tcl_Interp *interp; /* Current interpreter. */
50
int argc; /* Number of arguments. */
51
char **argv; /* Argument strings. */
52
{
53
if (argc != 1) {
54
Tcl_AppendResult(interp, "wrong # args: should be \"",
55
argv[0], "\"", (char *) NULL);
56
return TCL_ERROR;
57
}
58
return TCL_BREAK;
59
}
60
61
/*
62
*----------------------------------------------------------------------
63
*
64
* Tcl_CaseCmd --
65
*
66
* This procedure is invoked to process the "case" Tcl command.
67
* See the user documentation for details on what it does.
68
*
69
* Results:
70
* A standard Tcl result.
71
*
72
* Side effects:
73
* See the user documentation.
74
*
75
*----------------------------------------------------------------------
76
*/
77
78
/* ARGSUSED */
79
int
80
Tcl_CaseCmd(dummy, interp, argc, argv)
81
ClientData dummy; /* Not used. */
82
Tcl_Interp *interp; /* Current interpreter. */
83
int argc; /* Number of arguments. */
84
char **argv; /* Argument strings. */
85
{
86
int i, result;
87
int body;
88
char *string;
89
int caseArgc, splitArgs;
90
char **caseArgv;
91
92
if (argc < 3) {
93
Tcl_AppendResult(interp, "wrong # args: should be \"",
94
argv[0], " string ?in? patList body ... ?default body?\"",
95
(char *) NULL);
96
return TCL_ERROR;
97
}
98
string = argv[1];
99
body = -1;
100
if (strcmp(argv[2], "in") == 0) {
101
i = 3;
102
} else {
103
i = 2;
104
}
105
caseArgc = argc - i;
106
caseArgv = argv + i;
107
108
/*
109
* If all of the pattern/command pairs are lumped into a single
110
* argument, split them out again.
111
*/
112
113
splitArgs = 0;
114
if (caseArgc == 1) {
115
result = Tcl_SplitList(interp, caseArgv[0], &caseArgc, &caseArgv);
116
if (result != TCL_OK) {
117
return result;
118
}
119
splitArgs = 1;
120
}
121
122
for (i = 0; i < caseArgc; i += 2) {
123
int patArgc, j;
124
char **patArgv;
125
register char *p;
126
127
if (i == (caseArgc-1)) {
128
interp->result = "extra case pattern with no body";
129
result = TCL_ERROR;
130
goto cleanup;
131
}
132
133
/*
134
* Check for special case of single pattern (no list) with
135
* no backslash sequences.
136
*/
137
138
for (p = caseArgv[i]; *p != 0; p++) {
139
if (isspace(UCHAR(*p)) || (*p == '\\')) {
140
break;
141
}
142
}
143
if (*p == 0) {
144
if ((*caseArgv[i] == 'd')
145
&& (strcmp(caseArgv[i], "default") == 0)) {
146
body = i+1;
147
}
148
if (Tcl_StringMatch(string, caseArgv[i])) {
149
body = i+1;
150
goto match;
151
}
152
continue;
153
}
154
155
/*
156
* Break up pattern lists, then check each of the patterns
157
* in the list.
158
*/
159
160
result = Tcl_SplitList(interp, caseArgv[i], &patArgc, &patArgv);
161
if (result != TCL_OK) {
162
goto cleanup;
163
}
164
for (j = 0; j < patArgc; j++) {
165
if (Tcl_StringMatch(string, patArgv[j])) {
166
body = i+1;
167
break;
168
}
169
}
170
ckfree((char *) patArgv);
171
if (j < patArgc) {
172
break;
173
}
174
}
175
176
match:
177
if (body != -1) {
178
result = Tcl_Eval(interp, caseArgv[body]);
179
if (result == TCL_ERROR) {
180
char msg[100];
181
sprintf(msg, "\n (\"%.50s\" arm line %d)", caseArgv[body-1],
182
interp->errorLine);
183
Tcl_AddErrorInfo(interp, msg);
184
}
185
goto cleanup;
186
}
187
188
/*
189
* Nothing matched: return nothing.
190
*/
191
192
result = TCL_OK;
193
194
cleanup:
195
if (splitArgs) {
196
ckfree((char *) caseArgv);
197
}
198
return result;
199
}
200
201
/*
202
*----------------------------------------------------------------------
203
*
204
* Tcl_CatchCmd --
205
*
206
* This procedure is invoked to process the "catch" Tcl command.
207
* See the user documentation for details on what it does.
208
*
209
* Results:
210
* A standard Tcl result.
211
*
212
* Side effects:
213
* See the user documentation.
214
*
215
*----------------------------------------------------------------------
216
*/
217
218
/* ARGSUSED */
219
int
220
Tcl_CatchCmd(dummy, interp, argc, argv)
221
ClientData dummy; /* Not used. */
222
Tcl_Interp *interp; /* Current interpreter. */
223
int argc; /* Number of arguments. */
224
char **argv; /* Argument strings. */
225
{
226
int result;
227
228
if ((argc != 2) && (argc != 3)) {
229
Tcl_AppendResult(interp, "wrong # args: should be \"",
230
argv[0], " command ?varName?\"", (char *) NULL);
231
return TCL_ERROR;
232
}
233
result = Tcl_Eval(interp, argv[1]);
234
if (argc == 3) {
235
if (Tcl_SetVar(interp, argv[2], interp->result, 0) == NULL) {
236
Tcl_SetResult(interp, "couldn't save command result in variable",
237
TCL_STATIC);
238
return TCL_ERROR;
239
}
240
}
241
Tcl_ResetResult(interp);
242
sprintf(interp->result, "%d", result);
243
return TCL_OK;
244
}
245
246
/*
247
*----------------------------------------------------------------------
248
*
249
* Tcl_CdCmd --
250
*
251
* This procedure is invoked to process the "cd" Tcl command.
252
* See the user documentation for details on what it does.
253
*
254
* Results:
255
* A standard Tcl result.
256
*
257
* Side effects:
258
* See the user documentation.
259
*
260
*----------------------------------------------------------------------
261
*/
262
263
/* ARGSUSED */
264
int
265
Tcl_CdCmd(dummy, interp, argc, argv)
266
ClientData dummy; /* Not used. */
267
Tcl_Interp *interp; /* Current interpreter. */
268
int argc; /* Number of arguments. */
269
char **argv; /* Argument strings. */
270
{
271
char *dirName;
272
Tcl_DString buffer;
273
int result;
274
275
if (argc > 2) {
276
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
277
" dirName\"", (char *) NULL);
278
return TCL_ERROR;
279
}
280
281
if (argc == 2) {
282
dirName = argv[1];
283
} else {
284
dirName = "~";
285
}
286
dirName = Tcl_TranslateFileName(interp, dirName, &buffer);
287
if (dirName == NULL) {
288
return TCL_ERROR;
289
}
290
result = TclChdir(interp, dirName);
291
Tcl_DStringFree(&buffer);
292
return result;
293
}
294
295
/*
296
*----------------------------------------------------------------------
297
*
298
* Tcl_ConcatCmd --
299
*
300
* This procedure is invoked to process the "concat" Tcl command.
301
* See the user documentation for details on what it does.
302
*
303
* Results:
304
* A standard Tcl result.
305
*
306
* Side effects:
307
* See the user documentation.
308
*
309
*----------------------------------------------------------------------
310
*/
311
312
/* ARGSUSED */
313
int
314
Tcl_ConcatCmd(dummy, interp, argc, argv)
315
ClientData dummy; /* Not used. */
316
Tcl_Interp *interp; /* Current interpreter. */
317
int argc; /* Number of arguments. */
318
char **argv; /* Argument strings. */
319
{
320
if (argc >= 2) {
321
interp->result = Tcl_Concat(argc-1, argv+1);
322
interp->freeProc = TCL_DYNAMIC;
323
}
324
return TCL_OK;
325
}
326
327
/*
328
*----------------------------------------------------------------------
329
*
330
* Tcl_ContinueCmd --
331
*
332
* This procedure is invoked to process the "continue" Tcl command.
333
* See the user documentation for details on what it does.
334
*
335
* Results:
336
* A standard Tcl result.
337
*
338
* Side effects:
339
* See the user documentation.
340
*
341
*----------------------------------------------------------------------
342
*/
343
344
/* ARGSUSED */
345
int
346
Tcl_ContinueCmd(dummy, interp, argc, argv)
347
ClientData dummy; /* Not used. */
348
Tcl_Interp *interp; /* Current interpreter. */
349
int argc; /* Number of arguments. */
350
char **argv; /* Argument strings. */
351
{
352
if (argc != 1) {
353
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
354
"\"", (char *) NULL);
355
return TCL_ERROR;
356
}
357
return TCL_CONTINUE;
358
}
359
360
/*
361
*----------------------------------------------------------------------
362
*
363
* Tcl_ErrorCmd --
364
*
365
* This procedure is invoked to process the "error" Tcl command.
366
* See the user documentation for details on what it does.
367
*
368
* Results:
369
* A standard Tcl result.
370
*
371
* Side effects:
372
* See the user documentation.
373
*
374
*----------------------------------------------------------------------
375
*/
376
377
/* ARGSUSED */
378
int
379
Tcl_ErrorCmd(dummy, interp, argc, argv)
380
ClientData dummy; /* Not used. */
381
Tcl_Interp *interp; /* Current interpreter. */
382
int argc; /* Number of arguments. */
383
char **argv; /* Argument strings. */
384
{
385
Interp *iPtr = (Interp *) interp;
386
387
if ((argc < 2) || (argc > 4)) {
388
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
389
" message ?errorInfo? ?errorCode?\"", (char *) NULL);
390
return TCL_ERROR;
391
}
392
if ((argc >= 3) && (argv[2][0] != 0)) {
393
Tcl_AddErrorInfo(interp, argv[2]);
394
iPtr->flags |= ERR_ALREADY_LOGGED;
395
}
396
if (argc == 4) {
397
Tcl_SetVar2(interp, "errorCode", (char *) NULL, argv[3],
398
TCL_GLOBAL_ONLY);
399
iPtr->flags |= ERROR_CODE_SET;
400
}
401
Tcl_SetResult(interp, argv[1], TCL_VOLATILE);
402
return TCL_ERROR;
403
}
404
405
/*
406
*----------------------------------------------------------------------
407
*
408
* Tcl_EvalCmd --
409
*
410
* This procedure is invoked to process the "eval" Tcl command.
411
* See the user documentation for details on what it does.
412
*
413
* Results:
414
* A standard Tcl result.
415
*
416
* Side effects:
417
* See the user documentation.
418
*
419
*----------------------------------------------------------------------
420
*/
421
422
/* ARGSUSED */
423
int
424
Tcl_EvalCmd(dummy, interp, argc, argv)
425
ClientData dummy; /* Not used. */
426
Tcl_Interp *interp; /* Current interpreter. */
427
int argc; /* Number of arguments. */
428
char **argv; /* Argument strings. */
429
{
430
int result;
431
char *cmd;
432
433
if (argc < 2) {
434
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
435
" arg ?arg ...?\"", (char *) NULL);
436
return TCL_ERROR;
437
}
438
if (argc == 2) {
439
result = Tcl_Eval(interp, argv[1]);
440
} else {
441
442
/*
443
* More than one argument: concatenate them together with spaces
444
* between, then evaluate the result.
445
*/
446
447
cmd = Tcl_Concat(argc-1, argv+1);
448
result = Tcl_Eval(interp, cmd);
449
ckfree(cmd);
450
}
451
if (result == TCL_ERROR) {
452
char msg[60];
453
sprintf(msg, "\n (\"eval\" body line %d)", interp->errorLine);
454
Tcl_AddErrorInfo(interp, msg);
455
}
456
return result;
457
}
458
459
/*
460
*----------------------------------------------------------------------
461
*
462
* Tcl_ExitCmd --
463
*
464
* This procedure is invoked to process the "exit" Tcl command.
465
* See the user documentation for details on what it does.
466
*
467
* Results:
468
* A standard Tcl result.
469
*
470
* Side effects:
471
* See the user documentation.
472
*
473
*----------------------------------------------------------------------
474
*/
475
476
/* ARGSUSED */
477
int
478
Tcl_ExitCmd(dummy, interp, argc, argv)
479
ClientData dummy; /* Not used. */
480
Tcl_Interp *interp; /* Current interpreter. */
481
int argc; /* Number of arguments. */
482
char **argv; /* Argument strings. */
483
{
484
int value;
485
486
if ((argc != 1) && (argc != 2)) {
487
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
488
" ?returnCode?\"", (char *) NULL);
489
return TCL_ERROR;
490
}
491
if (argc == 1) {
492
value = 0;
493
} else if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) {
494
return TCL_ERROR;
495
}
496
Tcl_Exit(value);
497
/*NOTREACHED*/
498
return TCL_OK; /* Better not ever reach this! */
499
}
500
501
/*
502
*----------------------------------------------------------------------
503
*
504
* Tcl_ExprCmd --
505
*
506
* This procedure is invoked to process the "expr" Tcl command.
507
* See the user documentation for details on what it does.
508
*
509
* Results:
510
* A standard Tcl result.
511
*
512
* Side effects:
513
* See the user documentation.
514
*
515
*----------------------------------------------------------------------
516
*/
517
518
/* ARGSUSED */
519
int
520
Tcl_ExprCmd(dummy, interp, argc, argv)
521
ClientData dummy; /* Not used. */
522
Tcl_Interp *interp; /* Current interpreter. */
523
int argc; /* Number of arguments. */
524
char **argv; /* Argument strings. */
525
{
526
Tcl_DString buffer;
527
int i, result;
528
529
if (argc < 2) {
530
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
531
" arg ?arg ...?\"", (char *) NULL);
532
return TCL_ERROR;
533
}
534
535
if (argc == 2) {
536
return Tcl_ExprString(interp, argv[1]);
537
}
538
Tcl_DStringInit(&buffer);
539
Tcl_DStringAppend(&buffer, argv[1], -1);
540
for (i = 2; i < argc; i++) {
541
Tcl_DStringAppend(&buffer, " ", 1);
542
Tcl_DStringAppend(&buffer, argv[i], -1);
543
}
544
result = Tcl_ExprString(interp, buffer.string);
545
Tcl_DStringFree(&buffer);
546
return result;
547
}
548
549
/*
550
*----------------------------------------------------------------------
551
*
552
* Tcl_FileCmd --
553
*
554
* This procedure is invoked to process the "file" Tcl command.
555
* See the user documentation for details on what it does.
556
*
557
* Results:
558
* A standard Tcl result.
559
*
560
* Side effects:
561
* See the user documentation.
562
*
563
*----------------------------------------------------------------------
564
*/
565
566
/* ARGSUSED */
567
int
568
Tcl_FileCmd(dummy, interp, argc, argv)
569
ClientData dummy; /* Not used. */
570
Tcl_Interp *interp; /* Current interpreter. */
571
int argc; /* Number of arguments. */
572
char **argv; /* Argument strings. */
573
{
574
char *fileName, *extension;
575
int c, statOp, result;
576
size_t length;
577
int mode = 0; /* Initialized only to prevent
578
* compiler warning message. */
579
struct stat statBuf;
580
Tcl_DString buffer;
581
582
if (argc < 3) {
583
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
584
" option name ?arg ...?\"", (char *) NULL);
585
return TCL_ERROR;
586
}
587
c = argv[1][0];
588
length = strlen(argv[1]);
589
result = TCL_OK;
590
Tcl_DStringInit(&buffer);
591
592
/*
593
* First handle operations on the file name.
594
*/
595
596
if ((c == 'd') && (strncmp(argv[1], "dirname", length) == 0)) {
597
int pargc;
598
char **pargv;
599
600
if (argc != 3) {
601
argv[1] = "dirname";
602
goto not3Args;
603
}
604
605
fileName = argv[2];
606
607
/*
608
* If there is only one element, and it starts with a tilde,
609
* perform tilde substitution and resplit the path.
610
*/
611
612
Tcl_SplitPath(fileName, &pargc, &pargv);
613
if ((pargc == 1) && (*fileName == '~')) {
614
ckfree((char*) pargv);
615
fileName = Tcl_TranslateFileName(interp, fileName, &buffer);
616
if (fileName == NULL) {
617
result = TCL_ERROR;
618
goto done;
619
}
620
Tcl_SplitPath(fileName, &pargc, &pargv);
621
Tcl_DStringSetLength(&buffer, 0);
622
}
623
624
/*
625
* Return all but the last component. If there is only one
626
* component, return it if the path was non-relative, otherwise
627
* return the current directory.
628
*/
629
630
if (pargc > 1) {
631
Tcl_JoinPath(pargc-1, pargv, &buffer);
632
Tcl_DStringResult(interp, &buffer);
633
} else if ((pargc == 0)
634
|| (Tcl_GetPathType(pargv[0]) == TCL_PATH_RELATIVE)) {
635
#if 0
636
Tcl_SetResult(interp,
637
(tclPlatform == TCL_PLATFORM_MAC) ? ":" : ".", TCL_STATIC);
638
#else
639
Tcl_SetResult(interp, ".", TCL_STATIC);
640
#endif
641
} else {
642
Tcl_SetResult(interp, pargv[0], TCL_VOLATILE);
643
}
644
ckfree((char *)pargv);
645
goto done;
646
647
} else if ((c == 't') && (strncmp(argv[1], "tail", length) == 0)
648
&& (length >= 2)) {
649
int pargc;
650
char **pargv;
651
652
if (argc != 3) {
653
argv[1] = "tail";
654
goto not3Args;
655
}
656
657
fileName = argv[2];
658
659
/*
660
* If there is only one element, and it starts with a tilde,
661
* perform tilde substitution and resplit the path.
662
*/
663
664
Tcl_SplitPath(fileName, &pargc, &pargv);
665
if ((pargc == 1) && (*fileName == '~')) {
666
ckfree((char*) pargv);
667
fileName = Tcl_TranslateFileName(interp, fileName, &buffer);
668
if (fileName == NULL) {
669
result = TCL_ERROR;
670
goto done;
671
}
672
Tcl_SplitPath(fileName, &pargc, &pargv);
673
Tcl_DStringSetLength(&buffer, 0);
674
}
675
676
/*
677
* Return the last component, unless it is the only component, and it
678
* is the root of an absolute path.
679
*/
680
681
if (pargc > 0) {
682
if ((pargc > 1)
683
|| (Tcl_GetPathType(pargv[0]) == TCL_PATH_RELATIVE)) {
684
Tcl_SetResult(interp, pargv[pargc-1], TCL_VOLATILE);
685
}
686
}
687
ckfree((char *)pargv);
688
goto done;
689
690
} else if ((c == 'r') && (strncmp(argv[1], "rootname", length) == 0)
691
&& (length >= 2)) {
692
char tmp;
693
if (argc != 3) {
694
argv[1] = "rootname";
695
goto not3Args;
696
}
697
extension = TclGetExtension(argv[2]);
698
if (extension == NULL) {
699
Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
700
} else {
701
tmp = *extension;
702
*extension = 0;
703
Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
704
*extension = tmp;
705
}
706
goto done;
707
} else if ((c == 'e') && (strncmp(argv[1], "extension", length) == 0)
708
&& (length >= 3)) {
709
if (argc != 3) {
710
argv[1] = "extension";
711
goto not3Args;
712
}
713
extension = TclGetExtension(argv[2]);
714
715
if (extension != NULL) {
716
Tcl_SetResult(interp, extension, TCL_VOLATILE);
717
}
718
goto done;
719
} else if ((c == 'p') && (strncmp(argv[1], "pathtype", length) == 0)) {
720
if (argc != 3) {
721
argv[1] = "pathtype";
722
goto not3Args;
723
}
724
switch (Tcl_GetPathType(argv[2])) {
725
case TCL_PATH_ABSOLUTE:
726
Tcl_SetResult(interp, "absolute", TCL_STATIC);
727
break;
728
case TCL_PATH_RELATIVE:
729
Tcl_SetResult(interp, "relative", TCL_STATIC);
730
break;
731
case TCL_PATH_VOLUME_RELATIVE:
732
Tcl_SetResult(interp, "volumerelative", TCL_STATIC);
733
break;
734
}
735
goto done;
736
} else if ((c == 's') && (strncmp(argv[1], "split", length) == 0)
737
&& (length >= 2)) {
738
int pargc, i;
739
char **pargvList;
740
741
if (argc != 3) {
742
argv[1] = "split";
743
goto not3Args;
744
}
745
746
Tcl_SplitPath(argv[2], &pargc, &pargvList);
747
for (i = 0; i < pargc; i++) {
748
Tcl_AppendElement(interp, pargvList[i]);
749
}
750
ckfree((char *) pargvList);
751
goto done;
752
} else if ((c == 'j') && (strncmp(argv[1], "join", length) == 0)) {
753
Tcl_JoinPath(argc-2, argv+2, &buffer);
754
Tcl_DStringResult(interp, &buffer);
755
goto done;
756
}
757
758
/*
759
* Next, handle operations that can be satisfied with the "access"
760
* kernel call.
761
*/
762
763
fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
764
if (fileName == NULL) {
765
result = TCL_ERROR;
766
goto done;
767
}
768
if ((c == 'r') && (strncmp(argv[1], "readable", length) == 0)
769
&& (length >= 5)) {
770
if (argc != 3) {
771
argv[1] = "readable";
772
goto not3Args;
773
}
774
mode = R_OK;
775
checkAccess:
776
if (access(fileName, mode) == -1) {
777
interp->result = "0";
778
} else {
779
interp->result = "1";
780
}
781
goto done;
782
} else if ((c == 'w') && (strncmp(argv[1], "writable", length) == 0)) {
783
if (argc != 3) {
784
argv[1] = "writable";
785
goto not3Args;
786
}
787
mode = W_OK;
788
goto checkAccess;
789
} else if ((c == 'e') && (strncmp(argv[1], "executable", length) == 0)
790
&& (length >= 3)) {
791
if (argc != 3) {
792
argv[1] = "executable";
793
goto not3Args;
794
}
795
mode = X_OK;
796
goto checkAccess;
797
} else if ((c == 'e') && (strncmp(argv[1], "exists", length) == 0)
798
&& (length >= 3)) {
799
if (argc != 3) {
800
argv[1] = "exists";
801
goto not3Args;
802
}
803
mode = F_OK;
804
goto checkAccess;
805
}
806
807
/*
808
* Lastly, check stuff that requires the file to be stat-ed.
809
*/
810
811
if ((c == 'a') && (strncmp(argv[1], "atime", length) == 0)) {
812
if (argc != 3) {
813
argv[1] = "atime";
814
goto not3Args;
815
}
816
if (stat(fileName, &statBuf) == -1) {
817
goto badStat;
818
}
819
sprintf(interp->result, "%ld", (long) statBuf.st_atime);
820
goto done;
821
} else if ((c == 'i') && (strncmp(argv[1], "isdirectory", length) == 0)
822
&& (length >= 3)) {
823
if (argc != 3) {
824
argv[1] = "isdirectory";
825
goto not3Args;
826
}
827
statOp = 2;
828
} else if ((c == 'i') && (strncmp(argv[1], "isfile", length) == 0)
829
&& (length >= 3)) {
830
if (argc != 3) {
831
argv[1] = "isfile";
832
goto not3Args;
833
}
834
statOp = 1;
835
} else if ((c == 'l') && (strncmp(argv[1], "lstat", length) == 0)) {
836
if (argc != 4) {
837
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
838
" lstat name varName\"", (char *) NULL);
839
result = TCL_ERROR;
840
goto done;
841
}
842
843
if (lstat(fileName, &statBuf) == -1) {
844
Tcl_AppendResult(interp, "couldn't lstat \"", argv[2],
845
"\": ", Tcl_PosixError(interp), (char *) NULL);
846
result = TCL_ERROR;
847
goto done;
848
}
849
result = StoreStatData(interp, argv[3], &statBuf);
850
goto done;
851
} else if ((c == 'm') && (strncmp(argv[1], "mtime", length) == 0)) {
852
if (argc != 3) {
853
argv[1] = "mtime";
854
goto not3Args;
855
}
856
if (stat(fileName, &statBuf) == -1) {
857
goto badStat;
858
}
859
sprintf(interp->result, "%ld", (long) statBuf.st_mtime);
860
goto done;
861
} else if ((c == 'o') && (strncmp(argv[1], "owned", length) == 0)) {
862
if (argc != 3) {
863
argv[1] = "owned";
864
goto not3Args;
865
}
866
statOp = 0;
867
} else if ((c == 'r') && (strncmp(argv[1], "readlink", length) == 0)
868
&& (length >= 5)) {
869
char linkValue[MAXPATHLEN+1];
870
int linkLength;
871
872
if (argc != 3) {
873
argv[1] = "readlink";
874
goto not3Args;
875
}
876
877
/*
878
* If S_IFLNK isn't defined it means that the machine doesn't
879
* support symbolic links, so the file can't possibly be a
880
* symbolic link. Generate an EINVAL error, which is what
881
* happens on machines that do support symbolic links when
882
* you invoke readlink on a file that isn't a symbolic link.
883
*/
884
885
#ifndef S_IFLNK
886
linkLength = -1;
887
errno = EINVAL;
888
#else
889
linkLength = readlink(fileName, linkValue, sizeof(linkValue) - 1);
890
#endif /* S_IFLNK */
891
if (linkLength == -1) {
892
Tcl_AppendResult(interp, "couldn't readlink \"", argv[2],
893
"\": ", Tcl_PosixError(interp), (char *) NULL);
894
result = TCL_ERROR;
895
goto done;
896
}
897
linkValue[linkLength] = 0;
898
Tcl_SetResult(interp, linkValue, TCL_VOLATILE);
899
goto done;
900
} else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
901
&& (length >= 2)) {
902
if (argc != 3) {
903
argv[1] = "size";
904
goto not3Args;
905
}
906
if (stat(fileName, &statBuf) == -1) {
907
goto badStat;
908
}
909
sprintf(interp->result, "%lu", (unsigned long) statBuf.st_size);
910
goto done;
911
} else if ((c == 's') && (strncmp(argv[1], "stat", length) == 0)
912
&& (length >= 2)) {
913
if (argc != 4) {
914
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
915
" stat name varName\"", (char *) NULL);
916
result = TCL_ERROR;
917
goto done;
918
}
919
920
if (stat(fileName, &statBuf) == -1) {
921
badStat:
922
Tcl_AppendResult(interp, "couldn't stat \"", argv[2],
923
"\": ", Tcl_PosixError(interp), (char *) NULL);
924
result = TCL_ERROR;
925
goto done;
926
}
927
result = StoreStatData(interp, argv[3], &statBuf);
928
goto done;
929
} else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)
930
&& (length >= 2)) {
931
if (argc != 3) {
932
argv[1] = "type";
933
goto not3Args;
934
}
935
if (lstat(fileName, &statBuf) == -1) {
936
goto badStat;
937
}
938
interp->result = GetTypeFromMode((int) statBuf.st_mode);
939
goto done;
940
} else {
941
Tcl_AppendResult(interp, "bad option \"", argv[1],
942
"\": should be atime, dirname, executable, exists, ",
943
"extension, isdirectory, isfile, join, ",
944
"lstat, mtime, owned, pathtype, readable, readlink, ",
945
"root, size, split, stat, tail, type, ",
946
"or writable",
947
(char *) NULL);
948
result = TCL_ERROR;
949
goto done;
950
}
951
if (stat(fileName, &statBuf) == -1) {
952
interp->result = "0";
953
goto done;
954
}
955
switch (statOp) {
956
case 0:
957
/*
958
* For Windows and Macintosh, there are no user ids
959
* associated with a file, so we always return 1.
960
*/
961
962
#if (defined(__WIN32__) || defined(MAC_TCL))
963
mode = 1;
964
#else
965
mode = (geteuid() == statBuf.st_uid);
966
#endif
967
break;
968
case 1:
969
mode = S_ISREG(statBuf.st_mode);
970
break;
971
case 2:
972
mode = S_ISDIR(statBuf.st_mode);
973
break;
974
}
975
if (mode) {
976
interp->result = "1";
977
} else {
978
interp->result = "0";
979
}
980
981
done:
982
Tcl_DStringFree(&buffer);
983
return result;
984
985
not3Args:
986
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
987
" ", argv[1], " name\"", (char *) NULL);
988
result = TCL_ERROR;
989
goto done;
990
}
991
992
/*
993
*----------------------------------------------------------------------
994
*
995
* StoreStatData --
996
*
997
* This is a utility procedure that breaks out the fields of a
998
* "stat" structure and stores them in textual form into the
999
* elements of an associative array.
1000
*
1001
* Results:
1002
* Returns a standard Tcl return value. If an error occurs then
1003
* a message is left in interp->result.
1004
*
1005
* Side effects:
1006
* Elements of the associative array given by "varName" are modified.
1007
*
1008
*----------------------------------------------------------------------
1009
*/
1010
1011
static int
1012
StoreStatData(interp, varName, statPtr)
1013
Tcl_Interp *interp; /* Interpreter for error reports. */
1014
char *varName; /* Name of associative array variable
1015
* in which to store stat results. */
1016
struct stat *statPtr; /* Pointer to buffer containing
1017
* stat data to store in varName. */
1018
{
1019
char string[30];
1020
1021
sprintf(string, "%ld", (long) statPtr->st_dev);
1022
if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG)
1023
== NULL) {
1024
return TCL_ERROR;
1025
}
1026
sprintf(string, "%ld", (long) statPtr->st_ino);
1027
if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG)
1028
== NULL) {
1029
return TCL_ERROR;
1030
}
1031
sprintf(string, "%ld", (long) statPtr->st_mode);
1032
if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG)
1033
== NULL) {
1034
return TCL_ERROR;
1035
}
1036
sprintf(string, "%ld", (long) statPtr->st_nlink);
1037
if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG)
1038
== NULL) {
1039
return TCL_ERROR;
1040
}
1041
sprintf(string, "%ld", (long) statPtr->st_uid);
1042
if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG)
1043
== NULL) {
1044
return TCL_ERROR;
1045
}
1046
sprintf(string, "%ld", (long) statPtr->st_gid);
1047
if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG)
1048
== NULL) {
1049
return TCL_ERROR;
1050
}
1051
sprintf(string, "%lu", (unsigned long) statPtr->st_size);
1052
if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG)
1053
== NULL) {
1054
return TCL_ERROR;
1055
}
1056
sprintf(string, "%ld", (long) statPtr->st_atime);
1057
if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG)
1058
== NULL) {
1059
return TCL_ERROR;
1060
}
1061
sprintf(string, "%ld", (long) statPtr->st_mtime);
1062
if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG)
1063
== NULL) {
1064
return TCL_ERROR;
1065
}
1066
sprintf(string, "%ld", (long) statPtr->st_ctime);
1067
if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG)
1068
== NULL) {
1069
return TCL_ERROR;
1070
}
1071
if (Tcl_SetVar2(interp, varName, "type",
1072
GetTypeFromMode((int) statPtr->st_mode), TCL_LEAVE_ERR_MSG) == NULL) {
1073
return TCL_ERROR;
1074
}
1075
return TCL_OK;
1076
}
1077
1078
/*
1079
*----------------------------------------------------------------------
1080
*
1081
* GetTypeFromMode --
1082
*
1083
* Given a mode word, returns a string identifying the type of a
1084
* file.
1085
*
1086
* Results:
1087
* A static text string giving the file type from mode.
1088
*
1089
* Side effects:
1090
* None.
1091
*
1092
*----------------------------------------------------------------------
1093
*/
1094
1095
static char *
1096
GetTypeFromMode(mode)
1097
int mode;
1098
{
1099
if (S_ISREG(mode)) {
1100
return "file";
1101
} else if (S_ISDIR(mode)) {
1102
return "directory";
1103
} else if (S_ISCHR(mode)) {
1104
return "characterSpecial";
1105
} else if (S_ISBLK(mode)) {
1106
return "blockSpecial";
1107
} else if (S_ISFIFO(mode)) {
1108
return "fifo";
1109
} else if (S_ISLNK(mode)) {
1110
return "link";
1111
} else if (S_ISSOCK(mode)) {
1112
return "socket";
1113
}
1114
return "unknown";
1115
}
1116
1117
/*
1118
*----------------------------------------------------------------------
1119
*
1120
* Tcl_ForCmd --
1121
*
1122
* This procedure is invoked to process the "for" Tcl command.
1123
* See the user documentation for details on what it does.
1124
*
1125
* Results:
1126
* A standard Tcl result.
1127
*
1128
* Side effects:
1129
* See the user documentation.
1130
*
1131
*----------------------------------------------------------------------
1132
*/
1133
1134
/* ARGSUSED */
1135
int
1136
Tcl_ForCmd(dummy, interp, argc, argv)
1137
ClientData dummy; /* Not used. */
1138
Tcl_Interp *interp; /* Current interpreter. */
1139
int argc; /* Number of arguments. */
1140
char **argv; /* Argument strings. */
1141
{
1142
int result, value;
1143
1144
if (argc != 5) {
1145
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1146
" start test next command\"", (char *) NULL);
1147
return TCL_ERROR;
1148
}
1149
1150
result = Tcl_Eval(interp, argv[1]);
1151
if (result != TCL_OK) {
1152
if (result == TCL_ERROR) {
1153
Tcl_AddErrorInfo(interp, "\n (\"for\" initial command)");
1154
}
1155
return result;
1156
}
1157
while (1) {
1158
result = Tcl_ExprBoolean(interp, argv[2], &value);
1159
if (result != TCL_OK) {
1160
return result;
1161
}
1162
if (!value) {
1163
break;
1164
}
1165
result = Tcl_Eval(interp, argv[4]);
1166
if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
1167
if (result == TCL_ERROR) {
1168
char msg[60];
1169
sprintf(msg, "\n (\"for\" body line %d)", interp->errorLine);
1170
Tcl_AddErrorInfo(interp, msg);
1171
}
1172
break;
1173
}
1174
result = Tcl_Eval(interp, argv[3]);
1175
if (result == TCL_BREAK) {
1176
break;
1177
} else if (result != TCL_OK) {
1178
if (result == TCL_ERROR) {
1179
Tcl_AddErrorInfo(interp, "\n (\"for\" loop-end command)");
1180
}
1181
return result;
1182
}
1183
}
1184
if (result == TCL_BREAK) {
1185
result = TCL_OK;
1186
}
1187
if (result == TCL_OK) {
1188
Tcl_ResetResult(interp);
1189
}
1190
return result;
1191
}
1192
1193
/*
1194
*----------------------------------------------------------------------
1195
*
1196
* Tcl_ForeachCmd --
1197
*
1198
* This procedure is invoked to process the "foreach" Tcl command.
1199
* See the user documentation for details on what it does.
1200
*
1201
* Results:
1202
* A standard Tcl result.
1203
*
1204
* Side effects:
1205
* See the user documentation.
1206
*
1207
*----------------------------------------------------------------------
1208
*/
1209
1210
/* ARGSUSED */
1211
int
1212
Tcl_ForeachCmd(dummy, interp, argc, argv)
1213
ClientData dummy; /* Not used. */
1214
Tcl_Interp *interp; /* Current interpreter. */
1215
int argc; /* Number of arguments. */
1216
char **argv; /* Argument strings. */
1217
{
1218
int result = TCL_OK;
1219
int i; /* i selects a value list */
1220
int j, maxj; /* Number of loop iterations */
1221
int v; /* v selects a loop variable */
1222
int numLists; /* Count of value lists */
1223
#define STATIC_SIZE 4
1224
int indexArray[STATIC_SIZE]; /* Array of value list indices */
1225
int varcListArray[STATIC_SIZE]; /* Number of loop variables per list */
1226
char **varvListArray[STATIC_SIZE]; /* Array of variable name lists */
1227
int argcListArray[STATIC_SIZE]; /* Array of value list sizes */
1228
char **argvListArray[STATIC_SIZE]; /* Array of value lists */
1229
1230
int *index = indexArray;
1231
int *varcList = varcListArray;
1232
char ***varvList = varvListArray;
1233
int *argcList = argcListArray;
1234
char ***argvList = argvListArray;
1235
1236
if (argc < 4 || (argc%2 != 0)) {
1237
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1238
" varList list ?varList list ...? command\"", (char *) NULL);
1239
return TCL_ERROR;
1240
}
1241
1242
/*
1243
* Manage numList parallel value lists.
1244
* argvList[i] is a value list counted by argcList[i]
1245
* varvList[i] is the list of variables associated with the value list
1246
* varcList[i] is the number of variables associated with the value list
1247
* index[i] is the current pointer into the value list argvList[i]
1248
*/
1249
1250
numLists = (argc-2)/2;
1251
if (numLists > STATIC_SIZE) {
1252
index = (int *) ckalloc(numLists * sizeof(int));
1253
varcList = (int *) ckalloc(numLists * sizeof(int));
1254
varvList = (char ***) ckalloc(numLists * sizeof(char **));
1255
argcList = (int *) ckalloc(numLists * sizeof(int));
1256
argvList = (char ***) ckalloc(numLists * sizeof(char **));
1257
}
1258
for (i=0 ; i<numLists ; i++) {
1259
index[i] = 0;
1260
varcList[i] = 0;
1261
varvList[i] = (char **)NULL;
1262
argcList[i] = 0;
1263
argvList[i] = (char **)NULL;
1264
}
1265
1266
/*
1267
* Break up the value lists and variable lists into elements
1268
*/
1269
1270
maxj = 0;
1271
for (i=0 ; i<numLists ; i++) {
1272
result = Tcl_SplitList(interp, argv[1+i*2], &varcList[i], &varvList[i]);
1273
if (result != TCL_OK) {
1274
goto errorReturn;
1275
}
1276
result = Tcl_SplitList(interp, argv[2+i*2], &argcList[i], &argvList[i]);
1277
if (result != TCL_OK) {
1278
goto errorReturn;
1279
}
1280
j = argcList[i] / varcList[i];
1281
if ((argcList[i] % varcList[i]) != 0) {
1282
j++;
1283
}
1284
if (j > maxj) {
1285
maxj = j;
1286
}
1287
}
1288
1289
/*
1290
* Iterate maxj times through the lists in parallel
1291
* If some value lists run out of values, set loop vars to ""
1292
*/
1293
for (j = 0; j < maxj; j++) {
1294
for (i=0 ; i<numLists ; i++) {
1295
for (v=0 ; v<varcList[i] ; v++) {
1296
int k = index[i]++;
1297
char *value = "";
1298
if (k < argcList[i]) {
1299
value = argvList[i][k];
1300
}
1301
if (Tcl_SetVar(interp, varvList[i][v], value, 0) == NULL) {
1302
Tcl_AppendResult(interp, "couldn't set loop variable: \"",
1303
varvList[i][v], "\"", (char *)NULL);
1304
result = TCL_ERROR;
1305
goto errorReturn;
1306
}
1307
}
1308
}
1309
1310
result = Tcl_Eval(interp, argv[argc-1]);
1311
if (result != TCL_OK) {
1312
if (result == TCL_CONTINUE) {
1313
result = TCL_OK;
1314
} else if (result == TCL_BREAK) {
1315
result = TCL_OK;
1316
break;
1317
} else if (result == TCL_ERROR) {
1318
char msg[100];
1319
sprintf(msg, "\n (\"foreach\" body line %d)",
1320
interp->errorLine);
1321
Tcl_AddErrorInfo(interp, msg);
1322
break;
1323
} else {
1324
break;
1325
}
1326
}
1327
}
1328
if (result == TCL_OK) {
1329
Tcl_ResetResult(interp);
1330
}
1331
errorReturn:
1332
for (i=0 ; i<numLists ; i++) {
1333
if (argvList[i] != (char **)NULL) {
1334
ckfree((char *) argvList[i]);
1335
}
1336
if (varvList[i] != (char **)NULL) {
1337
ckfree((char *) varvList[i]);
1338
}
1339
}
1340
if (numLists > STATIC_SIZE) {
1341
ckfree((char *) index);
1342
ckfree((char *) varcList);
1343
ckfree((char *) argcList);
1344
ckfree((char *) varvList);
1345
ckfree((char *) argvList);
1346
}
1347
#undef STATIC_SIZE
1348
return result;
1349
}
1350
1351
/*
1352
*----------------------------------------------------------------------
1353
*
1354
* Tcl_FormatCmd --
1355
*
1356
* This procedure is invoked to process the "format" Tcl command.
1357
* See the user documentation for details on what it does.
1358
*
1359
* Results:
1360
* A standard Tcl result.
1361
*
1362
* Side effects:
1363
* See the user documentation.
1364
*
1365
*----------------------------------------------------------------------
1366
*/
1367
1368
/* ARGSUSED */
1369
int
1370
Tcl_FormatCmd(dummy, interp, argc, argv)
1371
ClientData dummy; /* Not used. */
1372
Tcl_Interp *interp; /* Current interpreter. */
1373
int argc; /* Number of arguments. */
1374
char **argv; /* Argument strings. */
1375
{
1376
register char *format; /* Used to read characters from the format
1377
* string. */
1378
char newFormat[40]; /* A new format specifier is generated here. */
1379
int width; /* Field width from field specifier, or 0 if
1380
* no width given. */
1381
int precision; /* Field precision from field specifier, or 0
1382
* if no precision given. */
1383
int size; /* Number of bytes needed for result of
1384
* conversion, based on type of conversion
1385
* ("e", "s", etc.), width, and precision. */
1386
int intValue; /* Used to hold value to pass to sprintf, if
1387
* it's a one-word integer or char value */
1388
char *ptrValue = NULL; /* Used to hold value to pass to sprintf, if
1389
* it's a one-word value. */
1390
double doubleValue; /* Used to hold value to pass to sprintf if
1391
* it's a double value. */
1392
int whichValue; /* Indicates which of intValue, ptrValue,
1393
* or doubleValue has the value to pass to
1394
* sprintf, according to the following
1395
* definitions: */
1396
# define INT_VALUE 0
1397
# define PTR_VALUE 1
1398
# define DOUBLE_VALUE 2
1399
char *dst = interp->result; /* Where result is stored. Starts off at
1400
* interp->resultSpace, but may get dynamically
1401
* re-allocated if this isn't enough. */
1402
int dstSize = 0; /* Number of non-null characters currently
1403
* stored at dst. */
1404
int dstSpace = TCL_RESULT_SIZE;
1405
/* Total amount of storage space available
1406
* in dst (not including null terminator. */
1407
int noPercent; /* Special case for speed: indicates there's
1408
* no field specifier, just a string to copy. */
1409
int argIndex; /* Index of argument to substitute next. */
1410
int gotXpg = 0; /* Non-zero means that an XPG3 %n$-style
1411
* specifier has been seen. */
1412
int gotSequential = 0; /* Non-zero means that a regular sequential
1413
* (non-XPG3) conversion specifier has been
1414
* seen. */
1415
int useShort; /* Value to be printed is short (half word). */
1416
char *end; /* Used to locate end of numerical fields. */
1417
1418
/*
1419
* This procedure is a bit nasty. The goal is to use sprintf to
1420
* do most of the dirty work. There are several problems:
1421
* 1. this procedure can't trust its arguments.
1422
* 2. we must be able to provide a large enough result area to hold
1423
* whatever's generated. This is hard to estimate.
1424
* 2. there's no way to move the arguments from argv to the call
1425
* to sprintf in a reasonable way. This is particularly nasty
1426
* because some of the arguments may be two-word values (doubles).
1427
* So, what happens here is to scan the format string one % group
1428
* at a time, making many individual calls to sprintf.
1429
*/
1430
1431
if (argc < 2) {
1432
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1433
" formatString ?arg arg ...?\"", (char *) NULL);
1434
return TCL_ERROR;
1435
}
1436
argIndex = 2;
1437
for (format = argv[1]; *format != 0; ) {
1438
register char *newPtr = newFormat;
1439
1440
width = precision = noPercent = useShort = 0;
1441
whichValue = PTR_VALUE;
1442
1443
/*
1444
* Get rid of any characters before the next field specifier.
1445
*/
1446
1447
if (*format != '%') {
1448
register char *p;
1449
1450
ptrValue = p = format;
1451
while ((*format != '%') && (*format != 0)) {
1452
*p = *format;
1453
p++;
1454
format++;
1455
}
1456
size = p - ptrValue;
1457
noPercent = 1;
1458
goto doField;
1459
}
1460
1461
if (format[1] == '%') {
1462
ptrValue = format;
1463
size = 1;
1464
noPercent = 1;
1465
format += 2;
1466
goto doField;
1467
}
1468
1469
/*
1470
* Parse off a field specifier, compute how many characters
1471
* will be needed to store the result, and substitute for
1472
* "*" size specifiers.
1473
*/
1474
1475
*newPtr = '%';
1476
newPtr++;
1477
format++;
1478
if (isdigit(UCHAR(*format))) {
1479
int tmp;
1480
1481
/*
1482
* Check for an XPG3-style %n$ specification. Note: there
1483
* must not be a mixture of XPG3 specs and non-XPG3 specs
1484
* in the same format string.
1485
*/
1486
1487
tmp = strtoul(format, &end, 10);
1488
if (*end != '$') {
1489
goto notXpg;
1490
}
1491
format = end+1;
1492
gotXpg = 1;
1493
if (gotSequential) {
1494
goto mixedXPG;
1495
}
1496
argIndex = tmp+1;
1497
if ((argIndex < 2) || (argIndex >= argc)) {
1498
goto badIndex;
1499
}
1500
goto xpgCheckDone;
1501
}
1502
1503
notXpg:
1504
gotSequential = 1;
1505
if (gotXpg) {
1506
goto mixedXPG;
1507
}
1508
1509
xpgCheckDone:
1510
while ((*format == '-') || (*format == '#') || (*format == '0')
1511
|| (*format == ' ') || (*format == '+')) {
1512
*newPtr = *format;
1513
newPtr++;
1514
format++;
1515
}
1516
if (isdigit(UCHAR(*format))) {
1517
width = strtoul(format, &end, 10);
1518
format = end;
1519
} else if (*format == '*') {
1520
if (argIndex >= argc) {
1521
goto badIndex;
1522
}
1523
if (Tcl_GetInt(interp, argv[argIndex], &width) != TCL_OK) {
1524
goto fmtError;
1525
}
1526
argIndex++;
1527
format++;
1528
}
1529
if (width > 100000) {
1530
/*
1531
* Don't allow arbitrarily large widths: could cause core
1532
* dump when we try to allocate a zillion bytes of memory
1533
* below.
1534
*/
1535
1536
width = 100000;
1537
} else if (width < 0) {
1538
width = 0;
1539
}
1540
if (width != 0) {
1541
sprintf(newPtr, "%d", width);
1542
while (*newPtr != 0) {
1543
newPtr++;
1544
}
1545
}
1546
if (*format == '.') {
1547
*newPtr = '.';
1548
newPtr++;
1549
format++;
1550
}
1551
if (isdigit(UCHAR(*format))) {
1552
precision = strtoul(format, &end, 10);
1553
format = end;
1554
} else if (*format == '*') {
1555
if (argIndex >= argc) {
1556
goto badIndex;
1557
}
1558
if (Tcl_GetInt(interp, argv[argIndex], &precision) != TCL_OK) {
1559
goto fmtError;
1560
}
1561
argIndex++;
1562
format++;
1563
}
1564
if (precision != 0) {
1565
sprintf(newPtr, "%d", precision);
1566
while (*newPtr != 0) {
1567
newPtr++;
1568
}
1569
}
1570
if (*format == 'l') {
1571
format++;
1572
} else if (*format == 'h') {
1573
useShort = 1;
1574
*newPtr = 'h';
1575
newPtr++;
1576
format++;
1577
}
1578
*newPtr = *format;
1579
newPtr++;
1580
*newPtr = 0;
1581
if (argIndex >= argc) {
1582
goto badIndex;
1583
}
1584
switch (*format) {
1585
case 'i':
1586
newPtr[-1] = 'd';
1587
case 'd':
1588
case 'o':
1589
case 'u':
1590
case 'x':
1591
case 'X':
1592
if (Tcl_GetInt(interp, argv[argIndex], (int *) &intValue)
1593
!= TCL_OK) {
1594
goto fmtError;
1595
}
1596
whichValue = INT_VALUE;
1597
size = 40 + precision;
1598
break;
1599
case 's':
1600
ptrValue = argv[argIndex];
1601
size = strlen(argv[argIndex]);
1602
break;
1603
case 'c':
1604
if (Tcl_GetInt(interp, argv[argIndex], (int *) &intValue)
1605
!= TCL_OK) {
1606
goto fmtError;
1607
}
1608
whichValue = INT_VALUE;
1609
size = 1;
1610
break;
1611
case 'e':
1612
case 'E':
1613
case 'f':
1614
case 'g':
1615
case 'G':
1616
if (Tcl_GetDouble(interp, argv[argIndex], &doubleValue)
1617
!= TCL_OK) {
1618
goto fmtError;
1619
}
1620
whichValue = DOUBLE_VALUE;
1621
size = 320;
1622
if (precision > 10) {
1623
size += precision;
1624
}
1625
break;
1626
case 0:
1627
interp->result =
1628
"format string ended in middle of field specifier";
1629
goto fmtError;
1630
default:
1631
sprintf(interp->result, "bad field specifier \"%c\"", *format);
1632
goto fmtError;
1633
}
1634
argIndex++;
1635
format++;
1636
1637
/*
1638
* Make sure that there's enough space to hold the formatted
1639
* result, then format it.
1640
*/
1641
1642
doField:
1643
if (width > size) {
1644
size = width;
1645
}
1646
if ((dstSize + size) > dstSpace) {
1647
char *newDst;
1648
int newSpace;
1649
1650
newSpace = 2*(dstSize + size);
1651
newDst = (char *) ckalloc((unsigned) newSpace+1);
1652
if (dstSize != 0) {
1653
memcpy((VOID *) newDst, (VOID *) dst, (size_t) dstSize);
1654
}
1655
if (dstSpace != TCL_RESULT_SIZE) {
1656
ckfree(dst);
1657
}
1658
dst = newDst;
1659
dstSpace = newSpace;
1660
}
1661
if (noPercent) {
1662
memcpy((VOID *) (dst+dstSize), (VOID *) ptrValue, (size_t) size);
1663
dstSize += size;
1664
dst[dstSize] = 0;
1665
} else {
1666
if (whichValue == DOUBLE_VALUE) {
1667
sprintf(dst+dstSize, newFormat, doubleValue);
1668
} else if (whichValue == INT_VALUE) {
1669
if (useShort) {
1670
sprintf(dst+dstSize, newFormat, (short) intValue);
1671
} else {
1672
sprintf(dst+dstSize, newFormat, intValue);
1673
}
1674
} else {
1675
sprintf(dst+dstSize, newFormat, ptrValue);
1676
}
1677
dstSize += strlen(dst+dstSize);
1678
}
1679
}
1680
1681
interp->result = dst;
1682
if (dstSpace != TCL_RESULT_SIZE) {
1683
interp->freeProc = TCL_DYNAMIC;
1684
} else {
1685
interp->freeProc = 0;
1686
}
1687
return TCL_OK;
1688
1689
mixedXPG:
1690
interp->result = "cannot mix \"%\" and \"%n$\" conversion specifiers";
1691
goto fmtError;
1692
1693
badIndex:
1694
if (gotXpg) {
1695
interp->result = "\"%n$\" argument index out of range";
1696
} else {
1697
interp->result = "not enough arguments for all format specifiers";
1698
}
1699
1700
fmtError:
1701
if (dstSpace != TCL_RESULT_SIZE) {
1702
ckfree(dst);
1703
}
1704
return TCL_ERROR;
1705
}
1706
1707