Logo Search packages:      
Sourcecode: tcl8.0 version File versions  Download package

tclUnixFile.c

/* 
 * tclUnixFile.c --
 *
 *      This file contains wrappers around UNIX file handling functions.
 *      These wrappers mask differences between Windows and UNIX.
 *
 * Copyright (c) 1995 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclUnixFile.c,v 1.4 1998/09/14 18:40:17 stanton Exp $
 */

#include "tclInt.h"
#include "tclPort.h"

/*
 * The variable below caches the name of the current working directory
 * in order to avoid repeated calls to getcwd.  The string is malloc-ed.
 * NULL means the cache needs to be refreshed.
 */

static char *currentDir =  NULL;
static int currentDirExitHandlerSet = 0;

/*
 * The variable below is set if the exit routine for deleting the string
 * containing the executable name has been registered.
 */

static int executableNameExitHandlerSet = 0;

extern pid_t waitpid _ANSI_ARGS_((pid_t pid, int *stat_loc, int options));

/*
 * Static routines for this file:
 */

static void FreeCurrentDir _ANSI_ARGS_((ClientData clientData));
static void FreeExecutableName _ANSI_ARGS_((ClientData clientData));

/*
 *----------------------------------------------------------------------
 *
 * FreeCurrentDir --
 *
 *    Frees the string stored in the currentDir variable. This routine
 *    is registered as an exit handler and will be called during shutdown.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Frees the memory occuppied by the currentDir value.
 *
 *----------------------------------------------------------------------
 */

      /* ARGSUSED */
static void
FreeCurrentDir(clientData)
    ClientData clientData;    /* Not used. */
{
    if (currentDir != (char *) NULL) {
        ckfree(currentDir);
        currentDir = (char *) NULL;
        currentDirExitHandlerSet = 0;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * FreeExecutableName --
 *
 *    Frees the string stored in the tclExecutableName variable. This
 *    routine is registered as an exit handler and will be called
 *    during shutdown.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Frees the memory occuppied by the tclExecutableName value.
 *
 *----------------------------------------------------------------------
 */

      /* ARGSUSED */
static void
FreeExecutableName(clientData)
    ClientData clientData;    /* Not used. */
{
    if (tclExecutableName != (char *) NULL) {
        ckfree(tclExecutableName);
        tclExecutableName = (char *) NULL;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclChdir --
 *
 *    Change the current working directory.
 *
 * Results:
 *    The result is a standard Tcl result.  If an error occurs and 
 *    interp isn't NULL, an error message is left in interp->result.
 *
 * Side effects:
 *    The working directory for this application is changed.  Also
 *    the cache maintained used by TclGetCwd is deallocated and
 *    set to NULL.
 *
 *----------------------------------------------------------------------
 */

int
TclChdir(interp, dirName)
    Tcl_Interp *interp;       /* If non NULL, used for error reporting. */
    char *dirName;            /* Path to new working directory. */
{
    if (currentDir != NULL) {
      ckfree(currentDir);
      currentDir = NULL;
    }
    if (chdir(dirName) != 0) {
      if (interp != NULL) {
          Tcl_AppendResult(interp, "couldn't change working directory to \"",
                dirName, "\": ", Tcl_PosixError(interp), (char *) NULL);
      }
      return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclGetCwd --
 *
 *    Return the path name of the current working directory.
 *
 * Results:
 *    The result is the full path name of the current working
 *    directory, or NULL if an error occurred while figuring it out.
 *    The returned string is owned by the TclGetCwd routine and must
 *    not be freed by the caller.  If an error occurs and interp
 *    isn't NULL, an error message is left in interp->result.
 *
 * Side effects:
 *    The path name is cached to avoid having to recompute it
 *    on future calls;  if it is already cached, the cached
 *    value is returned.
 *
 *----------------------------------------------------------------------
 */

char *
TclGetCwd(interp)
    Tcl_Interp *interp;       /* If non NULL, used for error reporting. */
{
    char buffer[MAXPATHLEN+1];

    if (currentDir == NULL) {
        if (!currentDirExitHandlerSet) {
            currentDirExitHandlerSet = 1;
            Tcl_CreateExitHandler(FreeCurrentDir, (ClientData) NULL);
        }
#ifdef USEGETWD
      if ((int)getwd(buffer) == (int)NULL) {
          if (interp != NULL) {
            Tcl_AppendResult(interp,
                  "error getting working directory name: ",
                  buffer, (char *)NULL);
          }
          return NULL;
      }
#else
      if (getcwd(buffer, MAXPATHLEN+1) == NULL) {
          if (interp != NULL) {
            if (errno == ERANGE) {
                Tcl_SetResult(interp,
                      "working directory name is too long",
                        TCL_STATIC);
            } else {
                Tcl_AppendResult(interp,
                      "error getting working directory name: ",
                      Tcl_PosixError(interp), (char *) NULL);
            }
          }
          return NULL;
      }
#endif
      currentDir = (char *) ckalloc((unsigned) (strlen(buffer) + 1));
      strcpy(currentDir, buffer);
    }
    return currentDir;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_FindExecutable --
 *
 *    This procedure computes the absolute path name of the current
 *    application, given its argv[0] value.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The variable tclExecutableName gets filled in with the file
 *    name for the application, if we figured it out.  If we couldn't
 *    figure it out, Tcl_FindExecutable is set to NULL.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_FindExecutable(argv0)
    char *argv0;        /* The value of the application's argv[0]. */
{
    char *name, *p, *cwd;
    Tcl_DString buffer;
    int length;
    struct stat statBuf;

    Tcl_DStringInit(&buffer);
    if (tclExecutableName != NULL) {
      ckfree(tclExecutableName);
      tclExecutableName = NULL;
    }

    name = argv0;
    for (p = name; *p != 0; p++) {
      if (*p == '/') {
          /*
           * The name contains a slash, so use the name directly
           * without doing a path search.
           */

          goto gotName;
      }
    }

    p = getenv("PATH");
    if (p == NULL) {
      /*
       * There's no PATH environment variable; use the default that
       * is used by sh.
       */

      p = ":/bin:/usr/bin";
    } else if (*p == '\0') {
      /*
       * An empty path is equivalent to ".".
       */

      p = "./";
    }

    /*
     * Search through all the directories named in the PATH variable
     * to see if argv[0] is in one of them.  If so, use that file
     * name.
     */

    while (*p != 0) {
      while (isspace(UCHAR(*p))) {
          p++;
      }
      name = p;
      while ((*p != ':') && (*p != 0)) {
          p++;
      }
      Tcl_DStringSetLength(&buffer, 0);
      if (p != name) {
          Tcl_DStringAppend(&buffer, name, p-name);
          if (p[-1] != '/') {
            Tcl_DStringAppend(&buffer, "/", 1);
          }
      }
      Tcl_DStringAppend(&buffer, argv0, -1);
      if ((TclAccess(Tcl_DStringValue(&buffer), X_OK) == 0)
            && (TclStat(Tcl_DStringValue(&buffer), &statBuf) == 0)
            && S_ISREG(statBuf.st_mode)) {
          name = Tcl_DStringValue(&buffer);
          goto gotName;
      }
      if (*p == 0) {
          break;
      } else if (*(p+1) == 0) {
          p = "./";
      } else {
          p++;
      }
    }
    goto done;

    /*
     * If the name starts with "/" then just copy it to tclExecutableName.
     */

    gotName:
    if (name[0] == '/')  {
      tclExecutableName = (char *) ckalloc((unsigned) (strlen(name) + 1));
      strcpy(tclExecutableName, name);
      goto done;
    }

    /*
     * The name is relative to the current working directory.  First
     * strip off a leading "./", if any, then add the full path name of
     * the current working directory.
     */

    if ((name[0] == '.') && (name[1] == '/')) {
      name += 2;
    }
    cwd = TclGetCwd((Tcl_Interp *) NULL);
    if (cwd == NULL) {
      tclExecutableName = NULL;
      goto done;
    }
    length = strlen(cwd);
    tclExecutableName = (char *) ckalloc((unsigned)
          (length + strlen(name) + 2));
    strcpy(tclExecutableName, cwd);
    tclExecutableName[length] = '/';
    strcpy(tclExecutableName + length + 1, name);

    done:
    Tcl_DStringFree(&buffer);

    if (!executableNameExitHandlerSet) {
        executableNameExitHandlerSet = 1;
        Tcl_CreateExitHandler(FreeExecutableName, (ClientData) NULL);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclGetUserHome --
 *
 *    This function takes the passed in user name and finds the
 *    corresponding home directory specified in the password file.
 *
 * Results:
 *    The result is a pointer to a static string containing
 *    the new name.  If there was an error in processing the
 *    user name then the return value is NULL.  Otherwise the
 *    result is stored in bufferPtr, and the caller must call
 *    Tcl_DStringFree(bufferPtr) to free the result.
 *
 * Side effects:
 *    Information may be left in bufferPtr.
 *
 *----------------------------------------------------------------------
 */

char *
TclGetUserHome(name, bufferPtr)
    char *name;               /* User name to use to find home directory. */
    Tcl_DString *bufferPtr;   /* May be used to hold result.  Must not hold
                         * anything at the time of the call, and need
                         * not even be initialized. */
{
    struct passwd *pwPtr;

    pwPtr = getpwnam(name);
    if (pwPtr == NULL) {
      endpwent();
      return NULL;
    }
    Tcl_DStringInit(bufferPtr);
    Tcl_DStringAppend(bufferPtr, pwPtr->pw_dir, -1);
    endpwent();
    return bufferPtr->string;
}

/*
 *----------------------------------------------------------------------
 *
 * TclMatchFiles --
 *
 *    This routine is used by the globbing code to search a
 *    directory for all files which match a given pattern.
 *
 * Results: 
 *    If the tail argument is NULL, then the matching files are
 *    added to the interp->result.  Otherwise, TclDoGlob is called
 *    recursively for each matching subdirectory.  The return value
 *    is a standard Tcl result indicating whether an error occurred
 *    in globbing.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

int
TclMatchFiles(interp, separators, dirPtr, pattern, tail)
    Tcl_Interp *interp;       /* Interpreter to receive results. */
    char *separators;         /* Path separators to pass to TclDoGlob. */
    Tcl_DString *dirPtr;      /* Contains path to directory to search. */
    char *pattern;            /* Pattern to match against. */
    char *tail;               /* Pointer to end of pattern. */
{
    char *dirName, *patternEnd = tail;
    char savedChar = 0;       /* Initialization needed only to prevent
                         * compiler warning from gcc. */
    DIR *d;
    struct stat statBuf;
    struct dirent *entryPtr;
    int matchHidden;
    int result = TCL_OK;
    int baseLength = Tcl_DStringLength(dirPtr);

    /*
     * Make sure that the directory part of the name really is a
     * directory.  If the directory name is "", use the name "."
     * instead, because some UNIX systems don't treat "" like "."
     * automatically.  Keep the "" for use in generating file names,
     * otherwise "glob foo.c" would return "./foo.c".
     */

    if (dirPtr->string[0] == '\0') {
      dirName = ".";
    } else {
      dirName = dirPtr->string;
    }
    if ((TclStat(dirName, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
      return TCL_OK;
    }

    /*
     * Check to see if the pattern needs to compare with hidden files.
     */

    if ((pattern[0] == '.')
          || ((pattern[0] == '\\') && (pattern[1] == '.'))) {
      matchHidden = 1;
    } else {
      matchHidden = 0;
    }

    /*
     * Now open the directory for reading and iterate over the contents.
     */

    d = opendir(dirName);
    if (d == NULL) {
      Tcl_ResetResult(interp);

      /*
       * Strip off a trailing '/' if necessary, before reporting the error.
       */

      if (baseLength > 0) {
          savedChar = dirPtr->string[baseLength-1];
          if (savedChar == '/') {
            dirPtr->string[baseLength-1] = '\0';
          }
      }
      Tcl_AppendResult(interp, "couldn't read directory \"",
            dirPtr->string, "\": ", Tcl_PosixError(interp), (char *) NULL);
      if (baseLength > 0) {
          dirPtr->string[baseLength-1] = savedChar;
      }
      return TCL_ERROR;
    }

    /*
     * Clean up the end of the pattern and the tail pointer.  Leave
     * the tail pointing to the first character after the path separator
     * following the pattern, or NULL.  Also, ensure that the pattern
     * is null-terminated.
     */

    if (*tail == '\\') {
      tail++;
    }
    if (*tail == '\0') {
      tail = NULL;
    } else {
      tail++;
    }
    savedChar = *patternEnd;
    *patternEnd = '\0';

    while (1) {
      entryPtr = readdir(d);
      if (entryPtr == NULL) {
          break;
      }

      /*
       * Don't match names starting with "." unless the "." is
       * present in the pattern.
       */

      if (!matchHidden && (*entryPtr->d_name == '.')) {
          continue;
      }

      /*
       * Now check to see if the file matches.  If there are more
       * characters to be processed, then ensure matching files are
       * directories before calling TclDoGlob. Otherwise, just add
       * the file to the result.
       */

      if (Tcl_StringMatch(entryPtr->d_name, pattern)) {
          Tcl_DStringSetLength(dirPtr, baseLength);
          Tcl_DStringAppend(dirPtr, entryPtr->d_name, -1);
          if (tail == NULL) {
            Tcl_AppendElement(interp, dirPtr->string);
          } else if ((TclStat(dirPtr->string, &statBuf) == 0)
                && S_ISDIR(statBuf.st_mode)) {
            Tcl_DStringAppend(dirPtr, "/", 1);
            result = TclDoGlob(interp, separators, dirPtr, tail);
            if (result != TCL_OK) {
                break;
            }
          }
      }
    }
    *patternEnd = savedChar;

    closedir(d);
    return result;
}

Generated by  Doxygen 1.6.0   Back to index