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