]> cvs.zerfleddert.de Git - micropolis/blob - src/tcl/tclunxaz.c
Fixes for compilation with gcc 15
[micropolis] / src / tcl / tclunxaz.c
1 /*
2 * tclUnixAZ.c --
3 *
4 * This file contains the top-level command procedures for
5 * commands in the Tcl core that require UNIX facilities
6 * such as files and process execution. Much of the code
7 * in this file is based on earlier versions contributed
8 * by Karl Lehenbauer, Mark Diekhans and Peter da Silva.
9 *
10 * Copyright 1991 Regents of the University of California
11 * Permission to use, copy, modify, and distribute this
12 * software and its documentation for any purpose and without
13 * fee is hereby granted, provided that this copyright
14 * notice appears in all copies. The University of California
15 * makes no representations about the suitability of this
16 * software for any purpose. It is provided "as is" without
17 * express or implied warranty.
18 */
19
20 #ifndef lint
21 static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclUnixAZ.c,v 1.36 92/04/16 13:32:02 ouster Exp $ sprite (Berkeley)";
22 #endif /* not lint */
23
24 #include "tclint.h"
25 #include "tclunix.h"
26
27 /*
28 * The variable below caches the name of the current working directory
29 * in order to avoid repeated calls to getwd. The string is malloc-ed.
30 * NULL means the cache needs to be refreshed.
31 */
32
33 static char *currentDir = NULL;
34
35 /*
36 * Prototypes for local procedures defined in this file:
37 */
38
39 static int CleanupChildren _ANSI_ARGS_((Tcl_Interp *interp,
40 int numPids, int *pidPtr, int errorId));
41 static char * GetFileType _ANSI_ARGS_((int mode));
42 static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
43 char *varName, struct stat *statPtr));
44 \f
45 /*
46 *----------------------------------------------------------------------
47 *
48 * Tcl_CdCmd --
49 *
50 * This procedure is invoked to process the "cd" Tcl command.
51 * See the user documentation for details on what it does.
52 *
53 * Results:
54 * A standard Tcl result.
55 *
56 * Side effects:
57 * See the user documentation.
58 *
59 *----------------------------------------------------------------------
60 */
61
62 /* ARGSUSED */
63 int
64 Tcl_CdCmd (
65 ClientData dummy, /* Not used. */
66 Tcl_Interp *interp, /* Current interpreter. */
67 int argc, /* Number of arguments. */
68 char **argv /* Argument strings. */
69 )
70 {
71 char *dirName;
72
73 if (argc > 2) {
74 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
75 " dirName\"", (char *) NULL);
76 return TCL_ERROR;
77 }
78
79 if (argc == 2) {
80 dirName = argv[1];
81 } else {
82 dirName = "~";
83 }
84 dirName = Tcl_TildeSubst(interp, dirName);
85 if (dirName == NULL) {
86 return TCL_ERROR;
87 }
88 if (currentDir != NULL) {
89 ckfree(currentDir);
90 currentDir = NULL;
91 }
92 if (chdir(dirName) != 0) {
93 Tcl_AppendResult(interp, "couldn't change working directory to \"",
94 dirName, "\": ", Tcl_UnixError(interp), (char *) NULL);
95 return TCL_ERROR;
96 }
97 return TCL_OK;
98 }
99 \f
100 /*
101 *----------------------------------------------------------------------
102 *
103 * Tcl_CloseCmd --
104 *
105 * This procedure is invoked to process the "close" Tcl command.
106 * See the user documentation for details on what it does.
107 *
108 * Results:
109 * A standard Tcl result.
110 *
111 * Side effects:
112 * See the user documentation.
113 *
114 *----------------------------------------------------------------------
115 */
116
117 /* ARGSUSED */
118 int
119 Tcl_CloseCmd (
120 ClientData dummy, /* Not used. */
121 Tcl_Interp *interp, /* Current interpreter. */
122 int argc, /* Number of arguments. */
123 char **argv /* Argument strings. */
124 )
125 {
126 OpenFile *filePtr;
127 int result = TCL_OK;
128
129 if (argc != 2) {
130 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
131 " fileId\"", (char *) NULL);
132 return TCL_ERROR;
133 }
134 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
135 return TCL_ERROR;
136 }
137 ((Interp *) interp)->filePtrArray[fileno(filePtr->f)] = NULL;
138
139 /*
140 * First close the file (in the case of a process pipeline, there may
141 * be two files, one for the pipe at each end of the pipeline).
142 */
143
144 if (filePtr->f2 != NULL) {
145 if (fclose(filePtr->f2) == EOF) {
146 Tcl_AppendResult(interp, "error closing \"", argv[1],
147 "\": ", Tcl_UnixError(interp), "\n", (char *) NULL);
148 result = TCL_ERROR;
149 }
150 }
151 if (fclose(filePtr->f) == EOF) {
152 Tcl_AppendResult(interp, "error closing \"", argv[1],
153 "\": ", Tcl_UnixError(interp), "\n", (char *) NULL);
154 result = TCL_ERROR;
155 }
156
157 /*
158 * If the file was a connection to a pipeline, clean up everything
159 * associated with the child processes.
160 */
161
162 if (filePtr->numPids > 0) {
163 if (CleanupChildren(interp, filePtr->numPids, filePtr->pidPtr,
164 filePtr->errorId) != TCL_OK) {
165 result = TCL_ERROR;
166 }
167 }
168
169 ckfree((char *) filePtr);
170 return result;
171 }
172 \f
173 /*
174 *----------------------------------------------------------------------
175 *
176 * Tcl_EofCmd --
177 *
178 * This procedure is invoked to process the "eof" Tcl command.
179 * See the user documentation for details on what it does.
180 *
181 * Results:
182 * A standard Tcl result.
183 *
184 * Side effects:
185 * See the user documentation.
186 *
187 *----------------------------------------------------------------------
188 */
189
190 /* ARGSUSED */
191 int
192 Tcl_EofCmd (
193 ClientData notUsed, /* Not used. */
194 Tcl_Interp *interp, /* Current interpreter. */
195 int argc, /* Number of arguments. */
196 char **argv /* Argument strings. */
197 )
198 {
199 OpenFile *filePtr;
200
201 if (argc != 2) {
202 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
203 " fileId\"", (char *) NULL);
204 return TCL_ERROR;
205 }
206 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
207 return TCL_ERROR;
208 }
209 if (feof(filePtr->f)) {
210 interp->result = "1";
211 } else {
212 interp->result = "0";
213 }
214 return TCL_OK;
215 }
216 \f
217 /*
218 *----------------------------------------------------------------------
219 *
220 * Tcl_ExecCmd --
221 *
222 * This procedure is invoked to process the "exec" Tcl command.
223 * See the user documentation for details on what it does.
224 *
225 * Results:
226 * A standard Tcl result.
227 *
228 * Side effects:
229 * See the user documentation.
230 *
231 *----------------------------------------------------------------------
232 */
233
234 /* ARGSUSED */
235 int
236 Tcl_ExecCmd (
237 ClientData dummy, /* Not used. */
238 Tcl_Interp *interp, /* Current interpreter. */
239 int argc, /* Number of arguments. */
240 char **argv /* Argument strings. */
241 )
242 {
243 int outputId; /* File id for output pipe. -1
244 * means command overrode. */
245 int errorId; /* File id for temporary file
246 * containing error output. */
247 int *pidPtr;
248 int numPids, result;
249
250 /*
251 * See if the command is to be run in background; if so, create
252 * the command, detach it, and return.
253 */
254
255 if ((argv[argc-1][0] == '&') && (argv[argc-1][1] == 0)) {
256 argc--;
257 argv[argc] = NULL;
258 numPids = Tcl_CreatePipeline(interp, argc-1, argv+1, &pidPtr,
259 (int *) NULL, (int *) NULL, (int *) NULL);
260 if (numPids < 0) {
261 return TCL_ERROR;
262 }
263 Tcl_DetachPids(numPids, pidPtr);
264 ckfree((char *) pidPtr);
265 return TCL_OK;
266 }
267
268 /*
269 * Create the command's pipeline.
270 */
271
272 numPids = Tcl_CreatePipeline(interp, argc-1, argv+1, &pidPtr,
273 (int *) NULL, &outputId, &errorId);
274 if (numPids < 0) {
275 return TCL_ERROR;
276 }
277
278 /*
279 * Read the child's output (if any) and put it into the result.
280 */
281
282 result = TCL_OK;
283 if (outputId != -1) {
284 while (1) {
285 # define BUFFER_SIZE 1000
286 char buffer[BUFFER_SIZE+1];
287 int count;
288
289 count = read(outputId, buffer, BUFFER_SIZE);
290
291 if (count == 0) {
292 break;
293 }
294 if (count < 0) {
295 Tcl_ResetResult(interp);
296 Tcl_AppendResult(interp,
297 "error reading from output pipe: ",
298 Tcl_UnixError(interp), (char *) NULL);
299 result = TCL_ERROR;
300 break;
301 }
302 buffer[count] = 0;
303 Tcl_AppendResult(interp, buffer, (char *) NULL);
304 }
305 close(outputId);
306 }
307
308 if (CleanupChildren(interp, numPids, pidPtr, errorId) != TCL_OK) {
309 result = TCL_ERROR;
310 }
311 return result;
312 }
313 \f
314 /*
315 *----------------------------------------------------------------------
316 *
317 * Tcl_ExitCmd --
318 *
319 * This procedure is invoked to process the "exit" Tcl command.
320 * See the user documentation for details on what it does.
321 *
322 * Results:
323 * A standard Tcl result.
324 *
325 * Side effects:
326 * See the user documentation.
327 *
328 *----------------------------------------------------------------------
329 */
330
331 /* ARGSUSED */
332 int
333 Tcl_ExitCmd (
334 ClientData dummy, /* Not used. */
335 Tcl_Interp *interp, /* Current interpreter. */
336 int argc, /* Number of arguments. */
337 char **argv /* Argument strings. */
338 )
339 {
340 int value;
341
342 if ((argc != 1) && (argc != 2)) {
343 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
344 " ?returnCode?\"", (char *) NULL);
345 return TCL_ERROR;
346 }
347 if (argc == 1) {
348 exit(0);
349 }
350 if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) {
351 return TCL_ERROR;
352 }
353 exit(value);
354 #if 0
355 return TCL_OK; /* Better not ever reach this! */
356 #endif
357 }
358 \f
359 /*
360 *----------------------------------------------------------------------
361 *
362 * Tcl_FileCmd --
363 *
364 * This procedure is invoked to process the "file" Tcl command.
365 * See the user documentation for details on what it does.
366 *
367 * Results:
368 * A standard Tcl result.
369 *
370 * Side effects:
371 * See the user documentation.
372 *
373 *----------------------------------------------------------------------
374 */
375
376 /* ARGSUSED */
377 int
378 Tcl_FileCmd (
379 ClientData dummy, /* Not used. */
380 Tcl_Interp *interp, /* Current interpreter. */
381 int argc, /* Number of arguments. */
382 char **argv /* Argument strings. */
383 )
384 {
385 char *p;
386 int length, statOp;
387 int mode = 0; /* Initialized only to prevent
388 * compiler warning message. */
389 struct stat statBuf;
390 char *fileName, c;
391
392 if (argc < 3) {
393 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
394 " option name ?arg ...?\"", (char *) NULL);
395 return TCL_ERROR;
396 }
397 c = argv[1][0];
398 length = strlen(argv[1]);
399
400 /*
401 * First handle operations on the file name.
402 */
403
404 fileName = Tcl_TildeSubst(interp, argv[2]);
405 if (fileName == NULL) {
406 return TCL_ERROR;
407 }
408 if ((c == 'd') && (strncmp(argv[1], "dirname", length) == 0)) {
409 if (argc != 3) {
410 argv[1] = "dirname";
411 not3Args:
412 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
413 " ", argv[1], " name\"", (char *) NULL);
414 return TCL_ERROR;
415 }
416 #ifdef MSDOS
417 p = strrchr(fileName, '\\');
418 #else
419 p = strrchr(fileName, '/');
420 #endif
421 if (p == NULL) {
422 interp->result = ".";
423 } else if (p == fileName) {
424 #ifdef MSDOS
425 interp->result = "\\";
426 #else
427 interp->result = "/";
428 #endif
429 } else {
430 *p = 0;
431 Tcl_SetResult(interp, fileName, TCL_VOLATILE);
432 *p = '/';
433 }
434 return TCL_OK;
435 } else if ((c == 'r') && (strncmp(argv[1], "rootname", length) == 0)
436 && (length >= 2)) {
437 char *lastSlash;
438
439 if (argc != 3) {
440 argv[1] = "rootname";
441 goto not3Args;
442 }
443 p = strrchr(fileName, '.');
444 #ifdef MSDOS
445 lastSlash = strrchr(fileName, '\\');
446 #else
447 lastSlash = strrchr(fileName, '/');
448 #endif
449 if ((p == NULL) || ((lastSlash != NULL) && (lastSlash > p))) {
450 Tcl_SetResult(interp, fileName, TCL_VOLATILE);
451 } else {
452 *p = 0;
453 Tcl_SetResult(interp, fileName, TCL_VOLATILE);
454 *p = '.';
455 }
456 return TCL_OK;
457 } else if ((c == 'e') && (strncmp(argv[1], "extension", length) == 0)
458 && (length >= 3)) {
459 char *lastSlash;
460
461 if (argc != 3) {
462 argv[1] = "extension";
463 goto not3Args;
464 }
465 p = strrchr(fileName, '.');
466 #ifdef MSDOS
467 lastSlash = strrchr(fileName, '\\');
468 #else
469 lastSlash = strrchr(fileName, '/');
470 #endif
471 if ((p != NULL) && ((lastSlash == NULL) || (lastSlash < p))) {
472 Tcl_SetResult(interp, p, TCL_VOLATILE);
473 }
474 return TCL_OK;
475 } else if ((c == 't') && (strncmp(argv[1], "tail", length) == 0)
476 && (length >= 2)) {
477 if (argc != 3) {
478 argv[1] = "tail";
479 goto not3Args;
480 }
481 #ifdef MSDOS
482 p = strrchr(fileName, '\\');
483 #else
484 p = strrchr(fileName, '/');
485 #endif
486 if (p != NULL) {
487 Tcl_SetResult(interp, p+1, TCL_VOLATILE);
488 } else {
489 Tcl_SetResult(interp, fileName, TCL_VOLATILE);
490 }
491 return TCL_OK;
492 }
493
494 /*
495 * Next, handle operations that can be satisfied with the "access"
496 * kernel call.
497 */
498
499 if (fileName == NULL) {
500 return TCL_ERROR;
501 }
502 if ((c == 'r') && (strncmp(argv[1], "readable", length) == 0)
503 && (length >= 5)) {
504 if (argc != 3) {
505 argv[1] = "readable";
506 goto not3Args;
507 }
508 mode = R_OK;
509 checkAccess:
510 if (access(fileName, mode) == -1) {
511 interp->result = "0";
512 } else {
513 interp->result = "1";
514 }
515 return TCL_OK;
516 } else if ((c == 'w') && (strncmp(argv[1], "writable", length) == 0)) {
517 if (argc != 3) {
518 argv[1] = "writable";
519 goto not3Args;
520 }
521 mode = W_OK;
522 goto checkAccess;
523 } else if ((c == 'e') && (strncmp(argv[1], "executable", length) == 0)
524 && (length >= 3)) {
525 if (argc != 3) {
526 argv[1] = "executable";
527 goto not3Args;
528 }
529 mode = X_OK;
530 goto checkAccess;
531 } else if ((c == 'e') && (strncmp(argv[1], "exists", length) == 0)
532 && (length >= 3)) {
533 if (argc != 3) {
534 argv[1] = "exists";
535 goto not3Args;
536 }
537 mode = F_OK;
538 goto checkAccess;
539 }
540
541 /*
542 * Lastly, check stuff that requires the file to be stat-ed.
543 */
544
545 if ((c == 'a') && (strncmp(argv[1], "atime", length) == 0)) {
546 if (argc != 3) {
547 argv[1] = "atime";
548 goto not3Args;
549 }
550 if (stat(fileName, &statBuf) == -1) {
551 goto badStat;
552 }
553 sprintf(interp->result, "%ld", statBuf.st_atime);
554 return TCL_OK;
555 } else if ((c == 'i') && (strncmp(argv[1], "isdirectory", length) == 0)
556 && (length >= 3)) {
557 if (argc != 3) {
558 argv[1] = "isdirectory";
559 goto not3Args;
560 }
561 statOp = 2;
562 } else if ((c == 'i') && (strncmp(argv[1], "isfile", length) == 0)
563 && (length >= 3)) {
564 if (argc != 3) {
565 argv[1] = "isfile";
566 goto not3Args;
567 }
568 statOp = 1;
569 } else if ((c == 'l') && (strncmp(argv[1], "lstat", length) == 0)) {
570 if (argc != 4) {
571 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
572 " lstat name varName\"", (char *) NULL);
573 return TCL_ERROR;
574 }
575
576 if (lstat(fileName, &statBuf) == -1) {
577 Tcl_AppendResult(interp, "couldn't lstat \"", argv[2],
578 "\": ", Tcl_UnixError(interp), (char *) NULL);
579 return TCL_ERROR;
580 }
581 return StoreStatData(interp, argv[3], &statBuf);
582 } else if ((c == 'm') && (strncmp(argv[1], "mtime", length) == 0)) {
583 if (argc != 3) {
584 argv[1] = "mtime";
585 goto not3Args;
586 }
587 if (stat(fileName, &statBuf) == -1) {
588 goto badStat;
589 }
590 sprintf(interp->result, "%ld", statBuf.st_mtime);
591 return TCL_OK;
592 } else if ((c == 'o') && (strncmp(argv[1], "owned", length) == 0)) {
593 if (argc != 3) {
594 argv[1] = "owned";
595 goto not3Args;
596 }
597 statOp = 0;
598 #ifdef S_IFLNK
599 /*
600 * This option is only included if symbolic links exist on this system
601 * (in which case S_IFLNK should be defined).
602 */
603 } else if ((c == 'r') && (strncmp(argv[1], "readlink", length) == 0)
604 && (length >= 5)) {
605 char linkValue[MAXPATHLEN+1];
606 int linkLength;
607
608 if (argc != 3) {
609 argv[1] = "readlink";
610 goto not3Args;
611 }
612 linkLength = readlink(fileName, linkValue, sizeof(linkValue) - 1);
613 if (linkLength == -1) {
614 Tcl_AppendResult(interp, "couldn't readlink \"", argv[2],
615 "\": ", Tcl_UnixError(interp), (char *) NULL);
616 return TCL_ERROR;
617 }
618 linkValue[linkLength] = 0;
619 Tcl_SetResult(interp, linkValue, TCL_VOLATILE);
620 return TCL_OK;
621 #endif
622 } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
623 && (length >= 2)) {
624 if (argc != 3) {
625 argv[1] = "size";
626 goto not3Args;
627 }
628 if (stat(fileName, &statBuf) == -1) {
629 goto badStat;
630 }
631 sprintf(interp->result, "%ld", statBuf.st_size);
632 return TCL_OK;
633 } else if ((c == 's') && (strncmp(argv[1], "stat", length) == 0)
634 && (length >= 2)) {
635 if (argc != 4) {
636 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
637 " stat name varName\"", (char *) NULL);
638 return TCL_ERROR;
639 }
640
641 if (stat(fileName, &statBuf) == -1) {
642 badStat:
643 Tcl_AppendResult(interp, "couldn't stat \"", argv[2],
644 "\": ", Tcl_UnixError(interp), (char *) NULL);
645 return TCL_ERROR;
646 }
647 return StoreStatData(interp, argv[3], &statBuf);
648 } else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)
649 && (length >= 2)) {
650 if (argc != 3) {
651 argv[1] = "type";
652 goto not3Args;
653 }
654 if (lstat(fileName, &statBuf) == -1) {
655 goto badStat;
656 }
657 interp->result = GetFileType((int) statBuf.st_mode);
658 return TCL_OK;
659 } else {
660 Tcl_AppendResult(interp, "bad option \"", argv[1],
661 "\": should be atime, dirname, executable, exists, ",
662 "extension, isdirectory, isfile, lstat, mtime, owned, ",
663 "readable, ",
664 #ifdef S_IFLNK
665 "readlink, ",
666 #endif
667 "root, size, stat, tail, type, ",
668 "or writable",
669 (char *) NULL);
670 return TCL_ERROR;
671 }
672 if (stat(fileName, &statBuf) == -1) {
673 interp->result = "0";
674 return TCL_OK;
675 }
676 switch (statOp) {
677 case 0:
678 mode = (geteuid() == statBuf.st_uid);
679 break;
680 case 1:
681 mode = S_ISREG(statBuf.st_mode);
682 break;
683 case 2:
684 mode = S_ISDIR(statBuf.st_mode);
685 break;
686 }
687 if (mode) {
688 interp->result = "1";
689 } else {
690 interp->result = "0";
691 }
692 return TCL_OK;
693 }
694 \f
695 /*
696 *----------------------------------------------------------------------
697 *
698 * StoreStatData --
699 *
700 * This is a utility procedure that breaks out the fields of a
701 * "stat" structure and stores them in textual form into the
702 * elements of an associative array.
703 *
704 * Results:
705 * Returns a standard Tcl return value. If an error occurs then
706 * a message is left in interp->result.
707 *
708 * Side effects:
709 * Elements of the associative array given by "varName" are modified.
710 *
711 *----------------------------------------------------------------------
712 */
713
714 static int
715 StoreStatData(
716 Tcl_Interp *interp, /* Interpreter for error reports. */
717 char *varName, /* Name of associative array variable
718 * in which to store stat results. */
719 struct stat *statPtr /* Pointer to buffer containing
720 * stat data to store in varName. */
721 )
722 {
723 char string[30];
724
725 sprintf(string, "%d", statPtr->st_dev);
726 if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG)
727 == NULL) {
728 return TCL_ERROR;
729 }
730 sprintf(string, "%d", statPtr->st_ino);
731 if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG)
732 == NULL) {
733 return TCL_ERROR;
734 }
735 sprintf(string, "%d", statPtr->st_mode);
736 if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG)
737 == NULL) {
738 return TCL_ERROR;
739 }
740 sprintf(string, "%d", statPtr->st_nlink);
741 if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG)
742 == NULL) {
743 return TCL_ERROR;
744 }
745 sprintf(string, "%d", statPtr->st_uid);
746 if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG)
747 == NULL) {
748 return TCL_ERROR;
749 }
750 sprintf(string, "%d", statPtr->st_gid);
751 if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG)
752 == NULL) {
753 return TCL_ERROR;
754 }
755 sprintf(string, "%ld", statPtr->st_size);
756 if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG)
757 == NULL) {
758 return TCL_ERROR;
759 }
760 sprintf(string, "%ld", statPtr->st_atime);
761 if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG)
762 == NULL) {
763 return TCL_ERROR;
764 }
765 sprintf(string, "%ld", statPtr->st_mtime);
766 if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG)
767 == NULL) {
768 return TCL_ERROR;
769 }
770 sprintf(string, "%ld", statPtr->st_ctime);
771 if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG)
772 == NULL) {
773 return TCL_ERROR;
774 }
775 if (Tcl_SetVar2(interp, varName, "type",
776 GetFileType((int) statPtr->st_mode), TCL_LEAVE_ERR_MSG) == NULL) {
777 return TCL_ERROR;
778 }
779 return TCL_OK;
780 }
781 \f
782 /*
783 *----------------------------------------------------------------------
784 *
785 * GetFileType --
786 *
787 * Given a mode word, returns a string identifying the type of a
788 * file.
789 *
790 * Results:
791 * A static text string giving the file type from mode.
792 *
793 * Side effects:
794 * None.
795 *
796 *----------------------------------------------------------------------
797 */
798
799 static char *
800 GetFileType (int mode)
801 {
802 if (S_ISREG(mode)) {
803 return "file";
804 } else if (S_ISDIR(mode)) {
805 return "directory";
806 } else if (S_ISCHR(mode)) {
807 return "characterSpecial";
808 } else if (S_ISBLK(mode)) {
809 return "blockSpecial";
810 } else if (S_ISFIFO(mode)) {
811 return "fifo";
812 } else if (S_ISLNK(mode)) {
813 return "link";
814 } else if (S_ISSOCK(mode)) {
815 return "socket";
816 }
817 return "unknown";
818 }
819 \f
820 /*
821 *----------------------------------------------------------------------
822 *
823 * Tcl_FlushCmd --
824 *
825 * This procedure is invoked to process the "flush" Tcl command.
826 * See the user documentation for details on what it does.
827 *
828 * Results:
829 * A standard Tcl result.
830 *
831 * Side effects:
832 * See the user documentation.
833 *
834 *----------------------------------------------------------------------
835 */
836
837 /* ARGSUSED */
838 int
839 Tcl_FlushCmd (
840 ClientData notUsed, /* Not used. */
841 Tcl_Interp *interp, /* Current interpreter. */
842 int argc, /* Number of arguments. */
843 char **argv /* Argument strings. */
844 )
845 {
846 OpenFile *filePtr;
847 FILE *f;
848
849 if (argc != 2) {
850 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
851 " fileId\"", (char *) NULL);
852 return TCL_ERROR;
853 }
854 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
855 return TCL_ERROR;
856 }
857 if (!filePtr->writable) {
858 Tcl_AppendResult(interp, "\"", argv[1],
859 "\" wasn't opened for writing", (char *) NULL);
860 return TCL_ERROR;
861 }
862 f = filePtr->f2;
863 if (f == NULL) {
864 f = filePtr->f;
865 }
866 if (fflush(f) == EOF) {
867 Tcl_AppendResult(interp, "error flushing \"", argv[1],
868 "\": ", Tcl_UnixError(interp), (char *) NULL);
869 clearerr(f);
870 return TCL_ERROR;
871 }
872 return TCL_OK;
873 }
874 \f
875 /*
876 *----------------------------------------------------------------------
877 *
878 * Tcl_GetsCmd --
879 *
880 * This procedure is invoked to process the "gets" Tcl command.
881 * See the user documentation for details on what it does.
882 *
883 * Results:
884 * A standard Tcl result.
885 *
886 * Side effects:
887 * See the user documentation.
888 *
889 *----------------------------------------------------------------------
890 */
891
892 /* ARGSUSED */
893 int
894 Tcl_GetsCmd (
895 ClientData notUsed, /* Not used. */
896 Tcl_Interp *interp, /* Current interpreter. */
897 int argc, /* Number of arguments. */
898 char **argv /* Argument strings. */
899 )
900 {
901 # define BUF_SIZE 200
902 char buffer[BUF_SIZE+1];
903 int totalCount, done, flags;
904 OpenFile *filePtr;
905 register FILE *f;
906
907 if ((argc != 2) && (argc != 3)) {
908 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
909 " fileId ?varName?\"", (char *) NULL);
910 return TCL_ERROR;
911 }
912 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
913 return TCL_ERROR;
914 }
915 if (!filePtr->readable) {
916 Tcl_AppendResult(interp, "\"", argv[1],
917 "\" wasn't opened for reading", (char *) NULL);
918 return TCL_ERROR;
919 }
920
921 /*
922 * We can't predict how large a line will be, so read it in
923 * pieces, appending to the current result or to a variable.
924 */
925
926 totalCount = 0;
927 done = 0;
928 flags = 0;
929 f = filePtr->f;
930 while (!done) {
931 register int c, count;
932 register char *p;
933
934 for (p = buffer, count = 0; count < BUF_SIZE-1; count++, p++) {
935 c = getc(f);
936 if (c == EOF) {
937 if (ferror(filePtr->f)) {
938 Tcl_ResetResult(interp);
939 Tcl_AppendResult(interp, "error reading \"", argv[1],
940 "\": ", Tcl_UnixError(interp), (char *) NULL);
941 clearerr(filePtr->f);
942 return TCL_ERROR;
943 } else if (feof(filePtr->f)) {
944 if ((totalCount == 0) && (count == 0)) {
945 totalCount = -1;
946 }
947 done = 1;
948 break;
949 }
950 }
951 if (c == '\n') {
952 done = 1;
953 break;
954 }
955 *p = c;
956 }
957 *p = 0;
958 if (argc == 2) {
959 Tcl_AppendResult(interp, buffer, (char *) NULL);
960 } else {
961 if (Tcl_SetVar(interp, argv[2], buffer, flags|TCL_LEAVE_ERR_MSG)
962 == NULL) {
963 return TCL_ERROR;
964 }
965 flags = TCL_APPEND_VALUE;
966 }
967 totalCount += count;
968 }
969
970 if (argc == 3) {
971 sprintf(interp->result, "%d", totalCount);
972 }
973 return TCL_OK;
974 }
975 \f
976 /*
977 *----------------------------------------------------------------------
978 *
979 * Tcl_OpenCmd --
980 *
981 * This procedure is invoked to process the "open" Tcl command.
982 * See the user documentation for details on what it does.
983 *
984 * Results:
985 * A standard Tcl result.
986 *
987 * Side effects:
988 * See the user documentation.
989 *
990 *----------------------------------------------------------------------
991 */
992
993 /* ARGSUSED */
994 int
995 Tcl_OpenCmd (
996 ClientData notUsed, /* Not used. */
997 Tcl_Interp *interp, /* Current interpreter. */
998 int argc, /* Number of arguments. */
999 char **argv /* Argument strings. */
1000 )
1001 {
1002 Interp *iPtr = (Interp *) interp;
1003 int pipeline, fd;
1004 char *access;
1005 register OpenFile *filePtr;
1006
1007 if (argc == 2) {
1008 access = "r";
1009 } else if (argc == 3) {
1010 access = argv[2];
1011 } else {
1012 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1013 " filename ?access?\"", (char *) NULL);
1014 return TCL_ERROR;
1015 }
1016
1017 filePtr = (OpenFile *) ckalloc(sizeof(OpenFile));
1018 filePtr->f = NULL;
1019 filePtr->f2 = NULL;
1020 filePtr->readable = 0;
1021 filePtr->writable = 0;
1022 filePtr->numPids = 0;
1023 filePtr->pidPtr = NULL;
1024 filePtr->errorId = -1;
1025
1026 /*
1027 * Verify the requested form of access.
1028 */
1029
1030 pipeline = 0;
1031 if (argv[1][0] == '|') {
1032 pipeline = 1;
1033 }
1034 switch (access[0]) {
1035 case 'r':
1036 filePtr->readable = 1;
1037 break;
1038 case 'w':
1039 filePtr->writable = 1;
1040 break;
1041 case 'a':
1042 filePtr->writable = 1;
1043 break;
1044 default:
1045 badAccess:
1046 Tcl_AppendResult(interp, "illegal access mode \"", access,
1047 "\"", (char *) NULL);
1048 goto error;
1049 }
1050 if (access[1] == '+') {
1051 filePtr->readable = filePtr->writable = 1;
1052 if (access[2] != 0) {
1053 goto badAccess;
1054 }
1055 } else if (access[1] != 0) {
1056 goto badAccess;
1057 }
1058
1059 /*
1060 * Open the file or create a process pipeline.
1061 */
1062
1063 if (!pipeline) {
1064 char *fileName = argv[1];
1065
1066 if (fileName[0] == '~') {
1067 fileName = Tcl_TildeSubst(interp, fileName);
1068 if (fileName == NULL) {
1069 goto error;
1070 }
1071 }
1072 filePtr->f = fopen(fileName, access);
1073 if (filePtr->f == NULL) {
1074 Tcl_AppendResult(interp, "couldn't open \"", argv[1],
1075 "\": ", Tcl_UnixError(interp), (char *) NULL);
1076 goto error;
1077 }
1078 } else {
1079 int *inPipePtr, *outPipePtr;
1080 int cmdArgc, inPipe, outPipe;
1081 char **cmdArgv;
1082
1083 if (Tcl_SplitList(interp, argv[1]+1, &cmdArgc, &cmdArgv) != TCL_OK) {
1084 goto error;
1085 }
1086 inPipePtr = (filePtr->writable) ? &inPipe : NULL;
1087 outPipePtr = (filePtr->readable) ? &outPipe : NULL;
1088 inPipe = outPipe = -1;
1089 filePtr->numPids = Tcl_CreatePipeline(interp, cmdArgc, cmdArgv,
1090 &filePtr->pidPtr, inPipePtr, outPipePtr, &filePtr->errorId);
1091 ckfree((char *) cmdArgv);
1092 if (filePtr->numPids < 0) {
1093 goto error;
1094 }
1095 if (filePtr->readable) {
1096 if (outPipe == -1) {
1097 if (inPipe != -1) {
1098 close(inPipe);
1099 }
1100 Tcl_AppendResult(interp, "can't read output from command:",
1101 " standard output was redirected", (char *) NULL);
1102 goto error;
1103 }
1104 filePtr->f = fdopen(outPipe, "r");
1105 }
1106 if (filePtr->writable) {
1107 if (inPipe == -1) {
1108 Tcl_AppendResult(interp, "can't write input to command:",
1109 " standard input was redirected", (char *) NULL);
1110 goto error;
1111 }
1112 if (filePtr->f != NULL) {
1113 filePtr->f2 = fdopen(inPipe, "w");
1114 } else {
1115 filePtr->f = fdopen(inPipe, "w");
1116 }
1117 }
1118 }
1119
1120 /*
1121 * Enter this new OpenFile structure in the table for the
1122 * interpreter. May have to expand the table to do this.
1123 */
1124
1125 fd = fileno(filePtr->f);
1126 TclMakeFileTable(iPtr, fd);
1127 if (iPtr->filePtrArray[fd] != NULL) {
1128 panic("Tcl_OpenCmd found file already open");
1129 }
1130 iPtr->filePtrArray[fd] = filePtr;
1131 sprintf(interp->result, "file%d", fd);
1132 return TCL_OK;
1133
1134 error:
1135 if (filePtr->f != NULL) {
1136 fclose(filePtr->f);
1137 }
1138 if (filePtr->f2 != NULL) {
1139 fclose(filePtr->f2);
1140 }
1141 if (filePtr->numPids > 0) {
1142 Tcl_DetachPids(filePtr->numPids, filePtr->pidPtr);
1143 ckfree((char *) filePtr->pidPtr);
1144 }
1145 if (filePtr->errorId != -1) {
1146 close(filePtr->errorId);
1147 }
1148 ckfree((char *) filePtr);
1149 return TCL_ERROR;
1150 }
1151 \f
1152 /*
1153 *----------------------------------------------------------------------
1154 *
1155 * Tcl_PwdCmd --
1156 *
1157 * This procedure is invoked to process the "pwd" Tcl command.
1158 * See the user documentation for details on what it does.
1159 *
1160 * Results:
1161 * A standard Tcl result.
1162 *
1163 * Side effects:
1164 * See the user documentation.
1165 *
1166 *----------------------------------------------------------------------
1167 */
1168
1169 /* ARGSUSED */
1170 int
1171 Tcl_PwdCmd (
1172 ClientData dummy, /* Not used. */
1173 Tcl_Interp *interp, /* Current interpreter. */
1174 int argc, /* Number of arguments. */
1175 char **argv /* Argument strings. */
1176 )
1177 {
1178 char buffer[MAXPATHLEN+1];
1179
1180 if (argc != 1) {
1181 Tcl_AppendResult(interp, "wrong # args: should be \"",
1182 argv[0], "\"", (char *) NULL);
1183 return TCL_ERROR;
1184 }
1185 if (currentDir == NULL) {
1186 #if TCL_GETWD
1187 if (getwd(buffer) == NULL) {
1188 Tcl_AppendResult(interp, "error getting working directory name: ",
1189 buffer, (char *) NULL);
1190 return TCL_ERROR;
1191 }
1192 #else
1193 if (getcwd(buffer, MAXPATHLEN) == 0) {
1194 if (errno == ERANGE) {
1195 interp->result = "working directory name is too long";
1196 } else {
1197 Tcl_AppendResult(interp,
1198 "error getting working directory name: ",
1199 Tcl_UnixError(interp), (char *) NULL);
1200 }
1201 return TCL_ERROR;
1202 }
1203 #endif
1204 currentDir = (char *) ckalloc((unsigned) (strlen(buffer) + 1));
1205 strcpy(currentDir, buffer);
1206 }
1207 interp->result = currentDir;
1208 return TCL_OK;
1209 }
1210 \f
1211 /*
1212 *----------------------------------------------------------------------
1213 *
1214 * Tcl_PutsCmd --
1215 *
1216 * This procedure is invoked to process the "puts" Tcl command.
1217 * See the user documentation for details on what it does.
1218 *
1219 * Results:
1220 * A standard Tcl result.
1221 *
1222 * Side effects:
1223 * See the user documentation.
1224 *
1225 *----------------------------------------------------------------------
1226 */
1227
1228 /* ARGSUSED */
1229 int
1230 Tcl_PutsCmd (
1231 ClientData dummy, /* Not used. */
1232 Tcl_Interp *interp, /* Current interpreter. */
1233 int argc, /* Number of arguments. */
1234 char **argv /* Argument strings. */
1235 )
1236 {
1237 OpenFile *filePtr;
1238 FILE *f;
1239
1240 if (argc == 4) {
1241 if (strncmp(argv[3], "nonewline", strlen(argv[3])) != 0) {
1242 Tcl_AppendResult(interp, "bad argument \"", argv[3],
1243 "\": should be \"nonewline\"", (char *) NULL);
1244 return TCL_ERROR;
1245 }
1246 } else if (argc != 3) {
1247 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1248 " fileId string ?nonewline?\"", (char *) NULL);
1249 return TCL_ERROR;
1250 }
1251 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
1252 return TCL_ERROR;
1253 }
1254 if (!filePtr->writable) {
1255 Tcl_AppendResult(interp, "\"", argv[1],
1256 "\" wasn't opened for writing", (char *) NULL);
1257 return TCL_ERROR;
1258 }
1259
1260 f = filePtr->f2;
1261 if (f == NULL) {
1262 f = filePtr->f;
1263 }
1264 fputs(argv[2], f);
1265 if (argc == 3) {
1266 fputc('\n', f);
1267 }
1268 if (ferror(f)) {
1269 Tcl_AppendResult(interp, "error writing \"", argv[1],
1270 "\": ", Tcl_UnixError(interp), (char *) NULL);
1271 clearerr(f);
1272 return TCL_ERROR;
1273 }
1274 return TCL_OK;
1275 }
1276 \f
1277 /*
1278 *----------------------------------------------------------------------
1279 *
1280 * Tcl_ReadCmd --
1281 *
1282 * This procedure is invoked to process the "read" Tcl command.
1283 * See the user documentation for details on what it does.
1284 *
1285 * Results:
1286 * A standard Tcl result.
1287 *
1288 * Side effects:
1289 * See the user documentation.
1290 *
1291 *----------------------------------------------------------------------
1292 */
1293
1294 /* ARGSUSED */
1295 int
1296 Tcl_ReadCmd (
1297 ClientData dummy, /* Not used. */
1298 Tcl_Interp *interp, /* Current interpreter. */
1299 int argc, /* Number of arguments. */
1300 char **argv /* Argument strings. */
1301 )
1302 {
1303 OpenFile *filePtr;
1304 int bytesLeft, bytesRead, count;
1305 #define READ_BUF_SIZE 4096
1306 char buffer[READ_BUF_SIZE+1];
1307 int newline;
1308
1309 if ((argc != 2) && (argc != 3)) {
1310 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1311 " fileId ?numBytes|nonewline?\"", (char *) NULL);
1312 return TCL_ERROR;
1313 }
1314 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
1315 return TCL_ERROR;
1316 }
1317 if (!filePtr->readable) {
1318 Tcl_AppendResult(interp, "\"", argv[1],
1319 "\" wasn't opened for reading", (char *) NULL);
1320 return TCL_ERROR;
1321 }
1322
1323 /*
1324 * Compute how many bytes to read, and see whether the final
1325 * newline should be dropped.
1326 */
1327
1328 newline = 1;
1329 if ((argc > 2) && isdigit(argv[2][0])) {
1330 if (Tcl_GetInt(interp, argv[2], &bytesLeft) != TCL_OK) {
1331 return TCL_ERROR;
1332 }
1333 } else {
1334 bytesLeft = 1<<30;
1335 if (argc > 2) {
1336 if (strncmp(argv[2], "nonewline", strlen(argv[2])) == 0) {
1337 newline = 0;
1338 } else {
1339 Tcl_AppendResult(interp, "bad argument \"", argv[2],
1340 "\": should be \"nonewline\"", (char *) NULL);
1341 return TCL_ERROR;
1342 }
1343 }
1344 }
1345
1346 /*
1347 * Read the file in one or more chunks.
1348 */
1349
1350 bytesRead = 0;
1351 while (bytesLeft > 0) {
1352 count = READ_BUF_SIZE;
1353 if (bytesLeft < READ_BUF_SIZE) {
1354 count = bytesLeft;
1355 }
1356 count = fread(buffer, 1, count, filePtr->f);
1357 if (ferror(filePtr->f)) {
1358 Tcl_ResetResult(interp);
1359 Tcl_AppendResult(interp, "error reading \"", argv[1],
1360 "\": ", Tcl_UnixError(interp), (char *) NULL);
1361 clearerr(filePtr->f);
1362 return TCL_ERROR;
1363 }
1364 if (count == 0) {
1365 break;
1366 }
1367 buffer[count] = 0;
1368 Tcl_AppendResult(interp, buffer, (char *) NULL);
1369 bytesLeft -= count;
1370 bytesRead += count;
1371 }
1372 if ((newline == 0) && (interp->result[bytesRead-1] == '\n')) {
1373 interp->result[bytesRead-1] = 0;
1374 }
1375 return TCL_OK;
1376 }
1377 \f
1378 /*
1379 *----------------------------------------------------------------------
1380 *
1381 * Tcl_SeekCmd --
1382 *
1383 * This procedure is invoked to process the "seek" Tcl command.
1384 * See the user documentation for details on what it does.
1385 *
1386 * Results:
1387 * A standard Tcl result.
1388 *
1389 * Side effects:
1390 * See the user documentation.
1391 *
1392 *----------------------------------------------------------------------
1393 */
1394
1395 /* ARGSUSED */
1396 int
1397 Tcl_SeekCmd (
1398 ClientData notUsed, /* Not used. */
1399 Tcl_Interp *interp, /* Current interpreter. */
1400 int argc, /* Number of arguments. */
1401 char **argv /* Argument strings. */
1402 )
1403 {
1404 OpenFile *filePtr;
1405 int offset, mode;
1406
1407 if ((argc != 3) && (argc != 4)) {
1408 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1409 " fileId offset ?origin?\"", (char *) NULL);
1410 return TCL_ERROR;
1411 }
1412 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
1413 return TCL_ERROR;
1414 }
1415 if (Tcl_GetInt(interp, argv[2], &offset) != TCL_OK) {
1416 return TCL_ERROR;
1417 }
1418 mode = SEEK_SET;
1419 if (argc == 4) {
1420 int length;
1421 char c;
1422
1423 length = strlen(argv[3]);
1424 c = argv[3][0];
1425 if ((c == 's') && (strncmp(argv[3], "start", length) == 0)) {
1426 mode = SEEK_SET;
1427 } else if ((c == 'c') && (strncmp(argv[3], "current", length) == 0)) {
1428 mode = SEEK_CUR;
1429 } else if ((c == 'e') && (strncmp(argv[3], "end", length) == 0)) {
1430 mode = SEEK_END;
1431 } else {
1432 Tcl_AppendResult(interp, "bad origin \"", argv[3],
1433 "\": should be start, current, or end", (char *) NULL);
1434 return TCL_ERROR;
1435 }
1436 }
1437 if (fseek(filePtr->f, offset, mode) == -1) {
1438 Tcl_AppendResult(interp, "error during seek: ",
1439 Tcl_UnixError(interp), (char *) NULL);
1440 clearerr(filePtr->f);
1441 return TCL_ERROR;
1442 }
1443
1444 return TCL_OK;
1445 }
1446 \f
1447 /*
1448 *----------------------------------------------------------------------
1449 *
1450 * Tcl_SourceCmd --
1451 *
1452 * This procedure is invoked to process the "source" Tcl command.
1453 * See the user documentation for details on what it does.
1454 *
1455 * Results:
1456 * A standard Tcl result.
1457 *
1458 * Side effects:
1459 * See the user documentation.
1460 *
1461 *----------------------------------------------------------------------
1462 */
1463
1464 /* ARGSUSED */
1465 int
1466 Tcl_SourceCmd (
1467 ClientData dummy, /* Not used. */
1468 Tcl_Interp *interp, /* Current interpreter. */
1469 int argc, /* Number of arguments. */
1470 char **argv /* Argument strings. */
1471 )
1472 {
1473 if (argc != 2) {
1474 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1475 " fileName\"", (char *) NULL);
1476 return TCL_ERROR;
1477 }
1478 return Tcl_EvalFile(interp, argv[1]);
1479 }
1480 \f
1481 /*
1482 *----------------------------------------------------------------------
1483 *
1484 * Tcl_TellCmd --
1485 *
1486 * This procedure is invoked to process the "tell" Tcl command.
1487 * See the user documentation for details on what it does.
1488 *
1489 * Results:
1490 * A standard Tcl result.
1491 *
1492 * Side effects:
1493 * See the user documentation.
1494 *
1495 *----------------------------------------------------------------------
1496 */
1497
1498 /* ARGSUSED */
1499 int
1500 Tcl_TellCmd (
1501 ClientData notUsed, /* Not used. */
1502 Tcl_Interp *interp, /* Current interpreter. */
1503 int argc, /* Number of arguments. */
1504 char **argv /* Argument strings. */
1505 )
1506 {
1507 OpenFile *filePtr;
1508
1509 if (argc != 2) {
1510 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1511 " fileId\"", (char *) NULL);
1512 return TCL_ERROR;
1513 }
1514 if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
1515 return TCL_ERROR;
1516 }
1517 sprintf(interp->result, "%d", ftell(filePtr->f));
1518 return TCL_OK;
1519 }
1520 \f
1521 /*
1522 *----------------------------------------------------------------------
1523 *
1524 * Tcl_TimeCmd --
1525 *
1526 * This procedure is invoked to process the "time" Tcl command.
1527 * See the user documentation for details on what it does.
1528 *
1529 * Results:
1530 * A standard Tcl result.
1531 *
1532 * Side effects:
1533 * See the user documentation.
1534 *
1535 *----------------------------------------------------------------------
1536 */
1537
1538 /* ARGSUSED */
1539 int
1540 Tcl_TimeCmd (
1541 ClientData dummy, /* Not used. */
1542 Tcl_Interp *interp, /* Current interpreter. */
1543 int argc, /* Number of arguments. */
1544 char **argv /* Argument strings. */
1545 )
1546 {
1547 int count, i, result;
1548 double timePer;
1549 #if TCL_GETTOD
1550 struct timeval start, stop;
1551 struct timezone tz;
1552 int micros;
1553 #else
1554 struct tms dummy2;
1555 long start, stop;
1556 #endif
1557
1558 if (argc == 2) {
1559 count = 1;
1560 } else if (argc == 3) {
1561 if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
1562 return TCL_ERROR;
1563 }
1564 } else {
1565 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1566 " command ?count?\"", (char *) NULL);
1567 return TCL_ERROR;
1568 }
1569 #if TCL_GETTOD
1570 gettimeofday(&start, &tz);
1571 #else
1572 start = times(&dummy2);
1573 #endif
1574 for (i = count ; i > 0; i--) {
1575 result = Tcl_Eval(interp, argv[1], 0, (char **) NULL);
1576 if (result != TCL_OK) {
1577 if (result == TCL_ERROR) {
1578 char msg[60];
1579 sprintf(msg, "\n (\"time\" body line %d)",
1580 interp->errorLine);
1581 Tcl_AddErrorInfo(interp, msg);
1582 }
1583 return result;
1584 }
1585 }
1586 #if TCL_GETTOD
1587 gettimeofday(&stop, &tz);
1588 micros = (stop.tv_sec - start.tv_sec)*1000000
1589 + (stop.tv_usec - start.tv_usec);
1590 timePer = micros;
1591 #else
1592 stop = times(&dummy2);
1593 timePer = (((double) (stop - start))*1000000.0)/CLK_TCK;
1594 #endif
1595 Tcl_ResetResult(interp);
1596 sprintf(interp->result, "%.0f microseconds per iteration", timePer/count);
1597 return TCL_OK;
1598 }
1599 \f
1600 /*
1601 *----------------------------------------------------------------------
1602 *
1603 * CleanupChildren --
1604 *
1605 * This is a utility procedure used to wait for child processes
1606 * to exit, record information about abnormal exits, and then
1607 * collect any stderr output generated by them.
1608 *
1609 * Results:
1610 * The return value is a standard Tcl result. If anything at
1611 * weird happened with the child processes, TCL_ERROR is returned
1612 * and a message is left in interp->result.
1613 *
1614 * Side effects:
1615 * If the last character of interp->result is a newline, then it
1616 * is removed. File errorId gets closed, and pidPtr is freed
1617 * back to the storage allocator.
1618 *
1619 *----------------------------------------------------------------------
1620 */
1621
1622 static int
1623 CleanupChildren (
1624 Tcl_Interp *interp, /* Used for error messages. */
1625 int numPids, /* Number of entries in pidPtr array. */
1626 int *pidPtr, /* Array of process ids of children. */
1627 int errorId /* File descriptor index for file containing
1628 * stderr output from pipeline. -1 means
1629 * there isn't any stderr output. */
1630 )
1631 {
1632 int result = TCL_OK;
1633 int i, pid, length;
1634 WAIT_STATUS_TYPE waitStatus;
1635
1636 for (i = 0; i < numPids; i++) {
1637 pid = Tcl_WaitPids(1, &pidPtr[i], (int *) &waitStatus);
1638 if (pid == -1) {
1639 Tcl_AppendResult(interp, "error waiting for process to exit: ",
1640 Tcl_UnixError(interp), (char *) NULL);
1641 continue;
1642 }
1643
1644 /*
1645 * Create error messages for unusual process exits. An
1646 * extra newline gets appended to each error message, but
1647 * it gets removed below (in the same fashion that an
1648 * extra newline in the command's output is removed).
1649 */
1650
1651 if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
1652 char msg1[20], msg2[20];
1653
1654 result = TCL_ERROR;
1655 sprintf(msg1, "%d", pid);
1656 if (WIFEXITED(waitStatus)) {
1657 sprintf(msg2, "%d", WEXITSTATUS(waitStatus));
1658 Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
1659 (char *) NULL);
1660 } else if (WIFSIGNALED(waitStatus)) {
1661 char *p;
1662
1663 p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
1664 Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
1665 Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
1666 (char *) NULL);
1667 Tcl_AppendResult(interp, "child killed: ", p, "\n",
1668 (char *) NULL);
1669 } else if (WIFSTOPPED(waitStatus)) {
1670 char *p;
1671
1672 p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
1673 Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
1674 Tcl_SignalId((int) (WSTOPSIG(waitStatus))), p, (char *) NULL);
1675 Tcl_AppendResult(interp, "child suspended: ", p, "\n",
1676 (char *) NULL);
1677 } else {
1678 Tcl_AppendResult(interp,
1679 "child wait status didn't make sense\n",
1680 (char *) NULL);
1681 }
1682 }
1683 }
1684 ckfree((char *) pidPtr);
1685
1686 /*
1687 * Read the standard error file. If there's anything there,
1688 * then return an error and add the file's contents to the result
1689 * string.
1690 */
1691
1692 if (errorId >= 0) {
1693 while (1) {
1694 # define BUFFER_SIZE 1000
1695 char buffer[BUFFER_SIZE+1];
1696 int count;
1697
1698 count = read(errorId, buffer, BUFFER_SIZE);
1699
1700 if (count == 0) {
1701 break;
1702 }
1703 if (count < 0) {
1704 Tcl_AppendResult(interp,
1705 "error reading stderr output file: ",
1706 Tcl_UnixError(interp), (char *) NULL);
1707 break;
1708 }
1709 buffer[count] = 0;
1710 Tcl_AppendResult(interp, buffer, (char *) NULL);
1711 }
1712 close(errorId);
1713 }
1714
1715 /*
1716 * If the last character of interp->result is a newline, then remove
1717 * the newline character (the newline would just confuse things).
1718 */
1719
1720 length = strlen(interp->result);
1721 if ((length > 0) && (interp->result[length-1] == '\n')) {
1722 interp->result[length-1] = '\0';
1723 }
1724
1725 return result;
1726 }
Impressum, Datenschutz