]>
git.zerfleddert.de Git - micropolis/blob - src/tcl/tclunxut.c
4 * This file contains a collection of utility procedures that
5 * are present in the Tcl's UNIX core but not in the generic
6 * core. For example, they do file manipulation and process
9 * The Tcl_Fork and Tcl_WaitPids procedures are based on code
10 * contributed by Karl Lehenbauer, Mark Diekhans and Peter
13 * Copyright 1991 Regents of the University of California
14 * Permission to use, copy, modify, and distribute this
15 * software and its documentation for any purpose and without
16 * fee is hereby granted, provided that this copyright
17 * notice appears in all copies. The University of California
18 * makes no representations about the suitability of this
19 * software for any purpose. It is provided "as is" without
20 * express or implied warranty.
24 static char rcsid
[] = "$Header: /user6/ouster/tcl/RCS/tclUnixUtil.c,v 1.18 91/11/21 14:53:46 ouster Exp $ SPRITE (Berkeley)";
31 * Data structures of the following type are used by Tcl_Fork and
32 * Tcl_WaitPids to keep track of child processes.
36 int pid
; /* Process id of child. */
37 WAIT_STATUS_TYPE status
; /* Status returned when child exited or
39 int flags
; /* Various flag bits; see below for
44 * Flag bits in WaitInfo structures:
46 * WI_READY - Non-zero means process has exited or
47 * suspended since it was forked or last
48 * returned by Tcl_WaitPids.
49 * WI_DETACHED - Non-zero means no-one cares about the
50 * process anymore. Ignore it until it
51 * exits, then forget about it.
57 static WaitInfo
*waitTable
= NULL
;
58 static int waitTableSize
= 0; /* Total number of entries available in
60 static int waitTableUsed
= 0; /* Number of entries in waitTable that
61 * are actually in use right now. Active
62 * entries are always at the beginning
64 #define WAIT_TABLE_GROW_BY 4
67 *----------------------------------------------------------------------
71 * Read in a file and process the entire file as one gigantic
75 * A standard Tcl result, which is either the result of executing
76 * the file or an error indicating why the file couldn't be read.
79 * Depends on the commands in the file.
81 *----------------------------------------------------------------------
85 Tcl_EvalFile(interp
, fileName
)
86 Tcl_Interp
*interp
; /* Interpreter in which to process file. */
87 char *fileName
; /* Name of file to process. Tilde-substitution
88 * will be performed on this name. */
92 char *cmdBuffer
, *end
, *oldScriptFile
;
93 Interp
*iPtr
= (Interp
*) interp
;
95 oldScriptFile
= iPtr
->scriptFile
;
96 iPtr
->scriptFile
= fileName
;
97 fileName
= Tcl_TildeSubst(interp
, fileName
);
98 if (fileName
== NULL
) {
102 filename2DOS(fileName
);
104 fileId
= open(fileName
, O_RDONLY
, 0);
107 Tcl_AppendResult(interp
, "couldn't read file \"", fileName
,
108 "\": ", Tcl_UnixError(interp
), (char *) NULL
);
111 if (fstat(fileId
, &statBuf
) == -1) {
112 Tcl_AppendResult(interp
, "couldn't stat file \"", fileName
,
113 "\": ", Tcl_UnixError(interp
), (char *) NULL
);
117 cmdBuffer
= (char *) ckalloc((unsigned) statBuf
.st_size
+1);
119 if (read(fileId
, cmdBuffer
, (int) statBuf
.st_size
) < 0) {
121 if (read(fileId
, cmdBuffer
, (int) statBuf
.st_size
) != statBuf
.st_size
) {
123 Tcl_AppendResult(interp
, "error in reading file \"", fileName
,
124 "\": ", Tcl_UnixError(interp
), (char *) NULL
);
128 if (close(fileId
) != 0) {
129 Tcl_AppendResult(interp
, "error closing file \"", fileName
,
130 "\": ", Tcl_UnixError(interp
), (char *) NULL
);
133 cmdBuffer
[statBuf
.st_size
] = 0;
134 result
= Tcl_Eval(interp
, cmdBuffer
, 0, &end
);
135 if (result
== TCL_RETURN
) {
138 if (result
== TCL_ERROR
) {
142 * Record information telling where the error occurred.
145 sprintf(msg
, "\n (file \"%.150s\" line %d)", fileName
,
147 Tcl_AddErrorInfo(interp
, msg
);
150 iPtr
->scriptFile
= oldScriptFile
;
154 iPtr
->scriptFile
= oldScriptFile
;
159 *----------------------------------------------------------------------
163 * Create a new process using the vfork system call, and keep
164 * track of it for "safe" waiting with Tcl_WaitPids.
167 * The return value is the value returned by the vfork system
168 * call (0 means child, > 0 means parent (value is child id),
172 * A new process is created, and an entry is added to an internal
173 * table of child processes if the process is created successfully.
175 *----------------------------------------------------------------------
185 * Disable SIGPIPE signals: if they were allowed, this process
186 * might go away unexpectedly if children misbehave. This code
187 * can potentially interfere with other application code that
188 * expects to handle SIGPIPEs; what's really needed is an
189 * arbiter for signals to allow them to be "shared".
192 if (waitTable
== NULL
) {
193 (void) signal(SIGPIPE
, SIG_IGN
);
197 * Enlarge the wait table if there isn't enough space for a new
201 if (waitTableUsed
== waitTableSize
) {
203 WaitInfo
*newWaitTable
;
205 newSize
= waitTableSize
+ WAIT_TABLE_GROW_BY
;
206 newWaitTable
= (WaitInfo
*) ckalloc((unsigned)
207 (newSize
* sizeof(WaitInfo
)));
208 memcpy((VOID
*) newWaitTable
, (VOID
*) waitTable
,
209 (waitTableSize
* sizeof(WaitInfo
)));
210 if (waitTable
!= NULL
) {
211 ckfree((char *) waitTable
);
213 waitTable
= newWaitTable
;
214 waitTableSize
= newSize
;
218 * Make a new process and enter it into the table if the fork
222 waitPtr
= &waitTable
[waitTableUsed
];
233 *----------------------------------------------------------------------
237 * This procedure is used to wait for one or more processes created
238 * by Tcl_Fork to exit or suspend. It records information about
239 * all processes that exit or suspend, even those not waited for,
240 * so that later waits for them will be able to get the status
244 * -1 is returned if there is an error in the wait kernel call.
245 * Otherwise the pid of an exited/suspended process from *pidPtr
246 * is returned and *statusPtr is set to the status value returned
247 * by the wait kernel call.
250 * Doesn't return until one of the pids at *pidPtr exits or suspends.
252 *----------------------------------------------------------------------
256 Tcl_WaitPids(numPids
, pidPtr
, statusPtr
)
257 int numPids
; /* Number of pids to wait on: gives size
258 * of array pointed to by pidPtr. */
259 int *pidPtr
; /* Pids to wait on: return when one of
260 * these processes exits or suspends. */
261 int *statusPtr
; /* Wait status is returned here. */
264 register WaitInfo
*waitPtr
;
266 WAIT_STATUS_TYPE status
;
270 * Scan the table of child processes to see if one of the
271 * specified children has already exited or suspended. If so,
272 * remove it from the table and return its status.
276 for (waitPtr
= waitTable
, count
= waitTableUsed
;
277 count
> 0; waitPtr
++, count
--) {
278 for (i
= 0; i
< numPids
; i
++) {
279 if (pidPtr
[i
] != waitPtr
->pid
) {
283 if (waitPtr
->flags
& WI_READY
) {
284 *statusPtr
= *((int *) &waitPtr
->status
);
286 if (WIFEXITED(waitPtr
->status
)
287 || WIFSIGNALED(waitPtr
->status
)) {
288 *waitPtr
= waitTable
[waitTableUsed
-1];
291 waitPtr
->flags
&= ~WI_READY
;
299 * Make sure that the caller at least specified one valid
300 * process to wait for.
309 * Wait for a process to exit or suspend, then update its
310 * entry in the table and go back to the beginning of the
311 * loop to see if it's one of the desired processes.
318 for (waitPtr
= waitTable
, count
= waitTableUsed
; ;
319 waitPtr
++, count
--) {
321 break; /* Ignore unknown processes. */
323 if (pid
!= waitPtr
->pid
) {
328 * If the process has been detached, then ignore anything
329 * other than an exit, and drop the entry on exit.
332 if (waitPtr
->flags
& WI_DETACHED
) {
333 if (WIFEXITED(status
) || WIFSIGNALED(status
)) {
334 *waitPtr
= waitTable
[waitTableUsed
-1];
338 waitPtr
->status
= status
;
339 waitPtr
->flags
|= WI_READY
;
347 *----------------------------------------------------------------------
351 * This procedure is called to indicate that one or more child
352 * processes have been placed in background and are no longer
353 * cared about. They should be ignored in future calls to
362 *----------------------------------------------------------------------
366 Tcl_DetachPids(numPids
, pidPtr
)
367 int numPids
; /* Number of pids to detach: gives size
368 * of array pointed to by pidPtr. */
369 int *pidPtr
; /* Array of pids to detach: must have
370 * been created by Tcl_Fork. */
372 register WaitInfo
*waitPtr
;
375 for (i
= 0; i
< numPids
; i
++) {
377 for (waitPtr
= waitTable
, count
= waitTableUsed
;
378 count
> 0; waitPtr
++, count
--) {
379 if (pid
!= waitPtr
->pid
) {
384 * If the process has already exited then destroy its
388 if ((waitPtr
->flags
& WI_READY
) && (WIFEXITED(waitPtr
->status
)
389 || WIFSIGNALED(waitPtr
->status
))) {
390 *waitPtr
= waitTable
[waitTableUsed
-1];
393 waitPtr
->flags
|= WI_DETACHED
;
397 panic("Tcl_Detach couldn't find process");
405 *----------------------------------------------------------------------
407 * Tcl_CreatePipeline --
409 * Given an argc/argv array, instantiate a pipeline of processes
410 * as described by the argv.
413 * The return value is a count of the number of new processes
414 * created, or -1 if an error occurred while creating the pipeline.
415 * *pidArrayPtr is filled in with the address of a dynamically
416 * allocated array giving the ids of all of the processes. It
417 * is up to the caller to free this array when it isn't needed
418 * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
419 * with the file id for the input pipe for the pipeline (if any):
420 * the caller must eventually close this file. If outPipePtr
421 * isn't NULL, then *outPipePtr is filled in with the file id
422 * for the output pipe from the pipeline: the caller must close
423 * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
424 * with a file id that may be used to read error output after the
425 * pipeline completes.
428 * Processes and pipes are created.
430 *----------------------------------------------------------------------
434 Tcl_CreatePipeline(interp
, argc
, argv
, pidArrayPtr
, inPipePtr
,
435 outPipePtr
, errFilePtr
)
436 Tcl_Interp
*interp
; /* Interpreter to use for error reporting. */
437 int argc
; /* Number of entries in argv. */
438 char **argv
; /* Array of strings describing commands in
439 * pipeline plus I/O redirection with <,
440 * <<, and >. Argv[argc] must be NULL. */
441 int **pidArrayPtr
; /* Word at *pidArrayPtr gets filled in with
442 * address of array of pids for processes
443 * in pipeline (first pid is first process
445 int *inPipePtr
; /* If non-NULL, input to the pipeline comes
446 * from a pipe (unless overridden by
447 * redirection in the command). The file
448 * id with which to write to this pipe is
449 * stored at *inPipePtr. -1 means command
450 * specified its own input source. */
451 int *outPipePtr
; /* If non-NULL, output to the pipeline goes
452 * to a pipe, unless overriden by redirection
453 * in the command. The file id with which to
454 * read frome this pipe is stored at
455 * *outPipePtr. -1 means command specified
456 * its own output sink. */
457 int *errFilePtr
; /* If non-NULL, all stderr output from the
458 * pipeline will go to a temporary file
459 * created here, and a descriptor to read
460 * the file will be left at *errFilePtr.
461 * The file will be removed already, so
462 * closing this descriptor will be the end
463 * of the file. If this is NULL, then
464 * all stderr output goes to our stderr. */
466 int *pidPtr
= NULL
; /* Points to malloc-ed array holding all
467 * the pids of child processes. */
468 int numPids
= 0; /* Actual number of processes that exist
469 * at *pidPtr right now. */
470 int cmdCount
; /* Count of number of distinct commands
471 * found in argc/argv. */
472 char *input
= NULL
; /* Describes input for pipeline, depending
473 * on "inputFile". NULL means take input
474 * from stdin/pipe. */
475 int inputFile
= 0; /* Non-zero means input is name of input
476 * file. Zero means input holds actual
477 * text to be input to command. */
478 char *output
= NULL
; /* Holds name of output file to pipe to,
479 * or NULL if output goes to stdout/pipe. */
480 int inputId
= -1; /* Readable file id input to current command in
481 * pipeline (could be file or pipe). -1
482 * means use stdin. */
483 int outputId
= -1; /* Writable file id for output from current
484 * command in pipeline (could be file or pipe).
485 * -1 means use stdout. */
486 int errorId
= -1; /* Writable file id for all standard error
487 * output from all commands in pipeline. -1
488 * means use stderr. */
489 int lastOutputId
= -1; /* Write file id for output from last command
490 * in pipeline (could be file or pipe).
491 * -1 means use stdout. */
492 int pipeIds
[2]; /* File ids for pipe that's being created. */
493 int firstArg
, lastArg
; /* Indexes of first and last arguments in
494 * current command. */
499 if (inPipePtr
!= NULL
) {
502 if (outPipePtr
!= NULL
) {
505 if (errFilePtr
!= NULL
) {
508 pipeIds
[0] = pipeIds
[1] = -1;
511 * First, scan through all the arguments to figure out the structure
512 * of the pipeline. Count the number of distinct processes (it's the
513 * number of "|" arguments). If there are "<", "<<", or ">" arguments
514 * then make note of input and output redirection and remove these
515 * arguments and the arguments that follow them.
520 for (i
= 0; i
< argc
; i
++) {
521 if ((argv
[i
][0] == '|') && ((argv
[i
][1] == 0))) {
522 if ((i
== (lastBar
+1)) || (i
== (argc
-1))) {
523 interp
->result
= "illegal use of | in command";
529 } else if (argv
[i
][0] == '<') {
530 if (argv
[i
][1] == 0) {
533 } else if ((argv
[i
][1] == '<') && (argv
[i
][2] == 0)) {
539 } else if ((argv
[i
][0] == '>') && (argv
[i
][1] == 0)) {
545 Tcl_AppendResult(interp
, "can't specify \"", argv
[i
],
546 "\" as last word in command", (char *) NULL
);
549 for (j
= i
+2; j
< argc
; j
++) {
553 i
--; /* Process new arg from same position. */
556 interp
->result
= "didn't specify command to execute";
561 * Set up the redirected input source for the pipeline, if
568 * Immediate data in command. Create temporary file and
569 * put data into file.
573 # define TMP_STDIN_NAME "tcl.in"
575 # define TMP_STDIN_NAME "/tmp/tcl.in.XXXXXX"
577 char inName
[sizeof(TMP_STDIN_NAME
) + 1];
580 strcpy(inName
, TMP_STDIN_NAME
);
582 inputId
= open(inName
, O_RDWR
|O_CREAT
|O_TRUNC
, 0600);
584 Tcl_AppendResult(interp
,
585 "couldn't create input file for command: ",
586 Tcl_UnixError(interp
), (char *) NULL
);
589 length
= strlen(input
);
591 if (write(inputId
, input
, length
) < 0) {
593 if (write(inputId
, input
, length
) != length
) {
595 Tcl_AppendResult(interp
,
596 "couldn't write file input for command: ",
597 Tcl_UnixError(interp
), (char *) NULL
);
600 if ((lseek(inputId
, 0L, 0) == -1) || (unlink(inName
) == -1)) {
601 Tcl_AppendResult(interp
,
602 "couldn't reset or remove input file for command: ",
603 Tcl_UnixError(interp
), (char *) NULL
);
608 * File redirection. Just open the file.
611 inputId
= open(input
, O_RDONLY
, 0);
613 Tcl_AppendResult(interp
,
614 "couldn't read file \"", input
, "\": ",
615 Tcl_UnixError(interp
), (char *) NULL
);
619 } else if (inPipePtr
!= NULL
) {
620 if (pipe(pipeIds
) != 0) {
621 Tcl_AppendResult(interp
,
622 "couldn't create input pipe for command: ",
623 Tcl_UnixError(interp
), (char *) NULL
);
626 inputId
= pipeIds
[0];
627 *inPipePtr
= pipeIds
[1];
628 pipeIds
[0] = pipeIds
[1] = -1;
632 * Set up the redirected output sink for the pipeline from one
633 * of two places, if requested.
636 if (output
!= NULL
) {
638 * Output is to go to a file.
641 lastOutputId
= open(output
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
642 if (lastOutputId
< 0) {
643 Tcl_AppendResult(interp
,
644 "couldn't write file \"", output
, "\": ",
645 Tcl_UnixError(interp
), (char *) NULL
);
648 } else if (outPipePtr
!= NULL
) {
650 * Output is to go to a pipe.
653 if (pipe(pipeIds
) != 0) {
654 Tcl_AppendResult(interp
,
655 "couldn't create output pipe: ",
656 Tcl_UnixError(interp
), (char *) NULL
);
659 lastOutputId
= pipeIds
[1];
660 *outPipePtr
= pipeIds
[0];
661 pipeIds
[0] = pipeIds
[1] = -1;
665 * Set up the standard error output sink for the pipeline, if
666 * requested. Use a temporary file which is opened, then deleted.
667 * Could potentially just use pipe, but if it filled up it could
668 * cause the pipeline to deadlock: we'd be waiting for processes
669 * to complete before reading stderr, and processes couldn't complete
670 * because stderr was backed up.
673 if (errFilePtr
!= NULL
) {
674 # define TMP_STDERR_NAME "/tmp/tcl.err.XXXXXX"
675 char errName
[sizeof(TMP_STDERR_NAME
) + 1];
677 strcpy(errName
, TMP_STDERR_NAME
);
679 errorId
= open(errName
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
682 Tcl_AppendResult(interp
,
683 "couldn't create error file for command: ",
684 Tcl_UnixError(interp
), (char *) NULL
);
687 *errFilePtr
= open(errName
, O_RDONLY
, 0);
688 if (*errFilePtr
< 0) {
691 if (unlink(errName
) == -1) {
692 Tcl_AppendResult(interp
,
693 "couldn't remove error file for command: ",
694 Tcl_UnixError(interp
), (char *) NULL
);
700 * Scan through the argc array, forking off a process for each
701 * group of arguments between "|" arguments.
704 pidPtr
= (int *) ckalloc((unsigned) (cmdCount
* sizeof(int)));
705 for (i
= 0; i
< numPids
; i
++) {
708 for (firstArg
= 0; firstArg
< argc
; numPids
++, firstArg
= lastArg
+1) {
709 for (lastArg
= firstArg
; lastArg
< argc
; lastArg
++) {
710 if ((argv
[lastArg
][0] == '|') && (argv
[lastArg
][1] == 0)) {
714 argv
[lastArg
] = NULL
;
715 if (lastArg
== argc
) {
716 outputId
= lastOutputId
;
718 if (pipe(pipeIds
) != 0) {
719 Tcl_AppendResult(interp
, "couldn't create pipe: ",
720 Tcl_UnixError(interp
), (char *) NULL
);
723 outputId
= pipeIds
[1];
725 execName
= Tcl_TildeSubst(interp
, argv
[firstArg
]);
728 Tcl_AppendResult(interp
, "couldn't fork child process: ",
729 Tcl_UnixError(interp
), (char *) NULL
);
735 if (((inputId
!= -1) && (dup2(inputId
, 0) == -1))
736 || ((outputId
!= -1) && (dup2(outputId
, 1) == -1))
737 || ((errorId
!= -1) && (dup2(errorId
, 2) == -1))) {
739 err
= "forked process couldn't set up input/output\n";
740 write(errorId
< 0 ? 2 : errorId
, err
, strlen(err
));
743 for (i
= 3; (i
<= outputId
) || (i
<= inputId
) || (i
<= errorId
);
747 execvp(execName
, &argv
[firstArg
]);
748 sprintf(errSpace
, "couldn't find \"%.150s\" to execute\n",
750 write(2, errSpace
, strlen(errSpace
));
753 pidPtr
[numPids
] = pid
;
757 * Close off our copies of file descriptors that were set up for
758 * this child, then set up the input for the next child.
764 if (outputId
!= -1) {
767 inputId
= pipeIds
[0];
768 pipeIds
[0] = pipeIds
[1] = -1;
770 *pidArrayPtr
= pidPtr
;
773 * All done. Cleanup open files lying around and then return.
780 if (lastOutputId
!= -1) {
789 * An error occurred. There could have been extra files open, such
790 * as pipes between children. Clean them all up. Detach any child
791 * processes that have been created.
795 if ((inPipePtr
!= NULL
) && (*inPipePtr
!= -1)) {
799 if ((outPipePtr
!= NULL
) && (*outPipePtr
!= -1)) {
803 if ((errFilePtr
!= NULL
) && (*errFilePtr
!= -1)) {
807 if (pipeIds
[0] != -1) {
810 if (pipeIds
[1] != -1) {
813 if (pidPtr
!= NULL
) {
814 for (i
= 0; i
< numPids
; i
++) {
815 if (pidPtr
[i
] != -1) {
816 Tcl_DetachPids(1, &pidPtr
[i
]);
819 ckfree((char *) pidPtr
);
826 *----------------------------------------------------------------------
830 * This procedure is typically called after UNIX kernel calls
831 * return errors. It stores machine-readable information about
832 * the error in $errorCode returns an information string for
836 * The return value is a human-readable string describing the
837 * error, as returned by strerror.
840 * The global variable $errorCode is reset.
842 *----------------------------------------------------------------------
846 Tcl_UnixError(interp
)
847 Tcl_Interp
*interp
; /* Interpreter whose $errorCode variable
848 * is to be changed. */
853 msg
= strerror(errno
);
854 Tcl_SetErrorCode(interp
, "UNIX", id
, msg
, (char *) NULL
);
859 *----------------------------------------------------------------------
861 * TclMakeFileTable --
863 * Create or enlarge the file table for the interpreter, so that
864 * there is room for a given index.
870 * The file table for iPtr will be created if it doesn't exist
871 * (and entries will be added for stdin, stdout, and stderr).
872 * If it already exists, then it will be grown if necessary.
874 *----------------------------------------------------------------------
878 TclMakeFileTable(iPtr
, index
)
879 Interp
*iPtr
; /* Interpreter whose table of files is
880 * to be manipulated. */
881 int index
; /* Make sure table is large enough to
882 * hold at least this index. */
885 * If the table doesn't even exist, then create it and initialize
886 * entries for standard files.
889 if (iPtr
->numFiles
== 0) {
896 iPtr
->numFiles
= index
+1;
898 iPtr
->filePtrArray
= (OpenFile
**) ckalloc((unsigned)
899 ((iPtr
->numFiles
)*sizeof(OpenFile
*)));
900 for (i
= iPtr
->numFiles
-1; i
>= 0; i
--) {
901 iPtr
->filePtrArray
[i
] = NULL
;
904 filePtr
= (OpenFile
*) ckalloc(sizeof(OpenFile
));
907 filePtr
->readable
= 1;
908 filePtr
->writable
= 0;
909 filePtr
->numPids
= 0;
910 filePtr
->pidPtr
= NULL
;
911 filePtr
->errorId
= -1;
912 iPtr
->filePtrArray
[0] = filePtr
;
914 filePtr
= (OpenFile
*) ckalloc(sizeof(OpenFile
));
917 filePtr
->readable
= 0;
918 filePtr
->writable
= 1;
919 filePtr
->numPids
= 0;
920 filePtr
->pidPtr
= NULL
;
921 filePtr
->errorId
= -1;
922 iPtr
->filePtrArray
[1] = filePtr
;
924 filePtr
= (OpenFile
*) ckalloc(sizeof(OpenFile
));
927 filePtr
->readable
= 0;
928 filePtr
->writable
= 1;
929 filePtr
->numPids
= 0;
930 filePtr
->pidPtr
= NULL
;
931 filePtr
->errorId
= -1;
932 iPtr
->filePtrArray
[2] = filePtr
;
933 } else if (index
>= iPtr
->numFiles
) {
935 OpenFile
**newPtrArray
;
939 newPtrArray
= (OpenFile
**) ckalloc((unsigned)
940 ((newSize
)*sizeof(OpenFile
*)));
941 memcpy((VOID
*) newPtrArray
, (VOID
*) iPtr
->filePtrArray
,
942 iPtr
->numFiles
*sizeof(OpenFile
*));
943 for (i
= iPtr
->numFiles
; i
< newSize
; i
++) {
944 newPtrArray
[i
] = NULL
;
946 ckfree((char *) iPtr
->filePtrArray
);
947 iPtr
->numFiles
= newSize
;
948 iPtr
->filePtrArray
= newPtrArray
;
953 *----------------------------------------------------------------------
957 * Given a string identifier for an open file, find the corresponding
958 * open file structure, if there is one.
961 * A standard Tcl return value. If the open file is successfully
962 * located, *filePtrPtr is modified to point to its structure.
963 * If TCL_ERROR is returned then interp->result contains an error
969 *----------------------------------------------------------------------
973 TclGetOpenFile(interp
, string
, filePtrPtr
)
974 Tcl_Interp
*interp
; /* Interpreter in which to find file. */
975 char *string
; /* String that identifies file. */
976 OpenFile
**filePtrPtr
; /* Address of word in which to store pointer
977 * to structure about open file. */
979 int fd
= 0; /* Initial value needed only to stop compiler
981 Interp
*iPtr
= (Interp
*) interp
;
983 if ((string
[0] == 'f') && (string
[1] == 'i') && (string
[2] == 'l')
984 & (string
[3] == 'e')) {
987 fd
= strtoul(string
+4, &end
, 10);
988 if ((end
== string
+4) || (*end
!= 0)) {
991 } else if ((string
[0] == 's') && (string
[1] == 't')
992 && (string
[2] == 'd')) {
993 if (strcmp(string
+3, "in") == 0) {
995 } else if (strcmp(string
+3, "out") == 0) {
997 } else if (strcmp(string
+3, "err") == 0) {
1004 Tcl_AppendResult(interp
, "bad file identifier \"", string
,
1005 "\"", (char *) NULL
);
1009 if (fd
>= iPtr
->numFiles
) {
1010 if ((iPtr
->numFiles
== 0) && (fd
<= 2)) {
1011 TclMakeFileTable(iPtr
, fd
);
1014 Tcl_AppendResult(interp
, "file \"", string
, "\" isn't open",
1019 if (iPtr
->filePtrArray
[fd
] == NULL
) {
1022 *filePtrPtr
= iPtr
->filePtrArray
[fd
];
1031 for ( ; *name
; name
++) if (*name
== '/') *name
= '\\';