Annotation of micropolis/src/tcl/tclunxaz.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.