diff options
Diffstat (limited to 'unix/tclUnixFile.c')
| -rw-r--r-- | unix/tclUnixFile.c | 1241 | 
1 files changed, 871 insertions, 370 deletions
| diff --git a/unix/tclUnixFile.c b/unix/tclUnixFile.c index befa699..2cb0027 100644 --- a/unix/tclUnixFile.c +++ b/unix/tclUnixFile.c @@ -1,70 +1,75 @@ -/*  +/*   * tclUnixFile.c --   * - *      This file contains wrappers around UNIX file handling functions. - *      These wrappers mask differences between Windows and UNIX. + *	This file contains wrappers around UNIX file handling functions. + *	These wrappers mask differences between Windows and UNIX.   *   * Copyright (c) 1995-1998 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.12 2001/08/30 08:53:15 vincentdarley Exp $   */  #include "tclInt.h" -#include "tclPort.h" +#include "tclFileSystem.h" +static int NativeMatchType(Tcl_Interp *interp, const char* nativeEntry, +	const char* nativeName, Tcl_GlobTypeData *types);  /*   *---------------------------------------------------------------------------   *   * TclpFindExecutable --   * - *	This procedure computes the absolute path name of the current - *	application, given its argv[0] value. + *	This function computes the absolute path name of the current + *	application, given its argv[0] value. For Cygwin, argv[0] is + *	ignored and the path is determined the same as under win32.   *   * Results: - *	A dirty UTF string that is the path to the executable.  At this - *	point we may not know the system encoding.  Convert the native - *	string value to UTF using the default encoding.  The assumption - *	is that we will still be able to parse the path given the path - *	name contains ASCII string and '/' chars do not conflict with - *	other UTF chars. + *	None.   *   * Side effects: - *	The variable tclNativeExecutableName gets filled in with the file - *	name for the application, if we figured it out.  If we couldn't - *	figure it out, tclNativeExecutableName is set to NULL. + *	The computed path name is stored as a ProcessGlobalValue.   *   *---------------------------------------------------------------------------   */ -char * -TclpFindExecutable(argv0) -    CONST char *argv0;		/* The value of the application's argv[0] +void +TclpFindExecutable( +    const char *argv0)		/* The value of the application's argv[0]  				 * (native). */  { -    CONST char *name, *p; -    struct stat statBuf; +    Tcl_Encoding encoding; +#ifdef __CYGWIN__      int length; -    Tcl_DString buffer, nameString; +    char buf[PATH_MAX * 2]; +    char name[PATH_MAX * TCL_UTF_MAX + 1]; +    GetModuleFileNameW(NULL, buf, PATH_MAX); +    cygwin_conv_path(3, buf, name, PATH_MAX); +    length = strlen(name); +    if ((length > 4) && !strcasecmp(name + length - 4, ".exe")) { +	/* Strip '.exe' part. */ +	length -= 4; +    } +    encoding = Tcl_GetEncoding(NULL, NULL); +    TclSetObjNameOfExecutable( +	    Tcl_NewStringObj(name, length), encoding); +#else +    const char *name, *p; +    Tcl_StatBuf statBuf; +    Tcl_DString buffer, nameString, cwd, utfName;      if (argv0 == NULL) { -	return NULL; -    } -    if (tclNativeExecutableName != NULL) { -	return tclNativeExecutableName; +	return;      } -      Tcl_DStringInit(&buffer);      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. +	     * The name contains a slash, so use the name directly without +	     * doing a path search.  	     */  	    goto gotName; @@ -74,8 +79,8 @@ TclpFindExecutable(argv0)      p = getenv("PATH");					/* INTL: Native. */      if (p == NULL) {  	/* -	 * There's no PATH environment variable; use the default that -	 * is used by sh. +	 * There's no PATH environment variable; use the default that is used +	 * by sh.  	 */  	p = ":/bin:/usr/bin"; @@ -88,24 +93,23 @@ TclpFindExecutable(argv0)      }      /* -     * 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. +     * 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 (1) { -	while (isspace(UCHAR(*p))) {		/* INTL: BUG */ +	while (TclIsSpaceProc(*p)) {  	    p++;  	}  	name = p;  	while ((*p != ':') && (*p != 0)) {  	    p++;  	} -	Tcl_DStringSetLength(&buffer, 0); +	TclDStringClear(&buffer);  	if (p != name) {  	    Tcl_DStringAppend(&buffer, name, p - name);  	    if (p[-1] != '/') { -		Tcl_DStringAppend(&buffer, "/", 1); +		TclDStringAppendLiteral(&buffer, "/");  	    }  	}  	name = Tcl_DStringAppend(&buffer, argv0, -1); @@ -116,8 +120,8 @@ TclpFindExecutable(argv0)  	 * strings directly.  	 */ -	if ((access(name, X_OK) == 0)		/* INTL: Native. */ -		&& (stat(name, &statBuf) == 0)	/* INTL: Native. */ +	if ((access(name, X_OK) == 0)			/* INTL: Native. */ +		&& (TclOSstat(name, &statBuf) == 0)	/* INTL: Native. */  		&& S_ISREG(statBuf.st_mode)) {  	    goto gotName;  	} @@ -129,48 +133,63 @@ TclpFindExecutable(argv0)  	    p++;  	}      } +    TclSetObjNameOfExecutable(Tcl_NewObj(), NULL);      goto done;      /* -     * If the name starts with "/" then just copy it to tclExecutableName. +     * If the name starts with "/" then just store it       */ -    gotName: -    if (name[0] == '/')  { -	Tcl_ExternalToUtfDString(NULL, name, -1, &nameString); -	tclNativeExecutableName = (char *) -		ckalloc((unsigned) (Tcl_DStringLength(&nameString) + 1)); -	strcpy(tclNativeExecutableName, Tcl_DStringValue(&nameString)); -	Tcl_DStringFree(&nameString); +  gotName: +#ifdef DJGPP +    if (name[1] == ':') +#else +    if (name[0] == '/') +#endif +    { +	encoding = Tcl_GetEncoding(NULL, NULL); +	Tcl_ExternalToUtfDString(encoding, name, -1, &utfName); +	TclSetObjNameOfExecutable( +		Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding); +	Tcl_DStringFree(&utfName);  	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. +     * 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;      } -    Tcl_ExternalToUtfDString(NULL, name, -1, &nameString); +    Tcl_DStringInit(&nameString); +    Tcl_DStringAppend(&nameString, name, -1); + +    TclpGetCwd(NULL, &cwd);      Tcl_DStringFree(&buffer); -    TclpGetCwd(NULL, &buffer); - -    length = Tcl_DStringLength(&buffer) + Tcl_DStringLength(&nameString) + 2; -    tclNativeExecutableName = (char *) ckalloc((unsigned) length); -    strcpy(tclNativeExecutableName, Tcl_DStringValue(&buffer)); -    tclNativeExecutableName[Tcl_DStringLength(&buffer)] = '/'; -    strcpy(tclNativeExecutableName + Tcl_DStringLength(&buffer) + 1, -	    Tcl_DStringValue(&nameString)); +    Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&cwd), +	    Tcl_DStringLength(&cwd), &buffer); +    if (Tcl_DStringValue(&cwd)[Tcl_DStringLength(&cwd) -1] != '/') { +	TclDStringAppendLiteral(&buffer, "/"); +    } +    Tcl_DStringFree(&cwd); +    TclDStringAppendDString(&buffer, &nameString);      Tcl_DStringFree(&nameString); -     -    done: + +    encoding = Tcl_GetEncoding(NULL, NULL); +    Tcl_ExternalToUtfDString(encoding, Tcl_DStringValue(&buffer), -1, +	    &utfName); +    TclSetObjNameOfExecutable( +	    Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding); +    Tcl_DStringFree(&utfName); + +  done:      Tcl_DStringFree(&buffer); -    return tclNativeExecutableName; +#endif  }  /* @@ -178,258 +197,363 @@ TclpFindExecutable(argv0)   *   * TclpMatchInDirectory --   * - *	This routine is used by the globbing code to search a - *	directory for all files which match a given pattern. + *	This routine is used by the globbing code to search a directory for + *	all files which match a given pattern.   * - * Results:  - *	The return value is a standard Tcl result indicating whether an - *	error occurred in globbing.  Errors are left in interp, good - *	results are lappended to resultPtr (which must be a valid object) + * Results: + *	The return value is a standard Tcl result indicating whether an error + *	occurred in globbing. Errors are left in interp, good results are + *	[lappend]ed to resultPtr (which must be a valid object).   *   * Side effects:   *	None.   * - *---------------------------------------------------------------------- */ + *---------------------------------------------------------------------- + */  int -TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types) -    Tcl_Interp *interp;		/* Interpreter to receive errors. */ -    Tcl_Obj *resultPtr;		/* List object to lappend results. */ -    Tcl_Obj *pathPtr;	        /* Contains path to directory to search. */ -    char *pattern;		/* Pattern to match against. */ -    Tcl_GlobTypeData *types;	/* Object containing list of acceptable types. +TclpMatchInDirectory( +    Tcl_Interp *interp,		/* Interpreter to receive errors. */ +    Tcl_Obj *resultPtr,		/* List object to lappend results. */ +    Tcl_Obj *pathPtr,		/* Contains path to directory to search. */ +    const char *pattern,	/* Pattern to match against. */ +    Tcl_GlobTypeData *types)	/* Object containing list of acceptable types.  				 * May be NULL. In particular the directory  				 * flag is very important. */  { -    char *native, *fname, *dirName; -    DIR *d; -    Tcl_DString ds; -    struct stat statBuf; -    int matchHidden; -    int nativeDirLen; -    int result = TCL_OK; -    Tcl_DString dsOrig; +    const char *native;      Tcl_Obj *fileNamePtr; -    int baseLength; +    int matchResult = 0; + +    if (types != NULL && types->type == TCL_GLOB_TYPE_MOUNT) { +	/* +	 * The native filesystem never adds mounts. +	 */ + +	return TCL_OK; +    }      fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);      if (fileNamePtr == NULL) {  	return TCL_ERROR;      } -    Tcl_DStringInit(&dsOrig); -    Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1); -    baseLength = Tcl_DStringLength(&dsOrig); -     -    /* -     * 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 (baseLength == 0) { -	dirName = "."; -    } else { -	dirName = Tcl_DStringValue(&dsOrig); -	/* Make sure we have a trailing directory delimiter */ -	if (dirName[baseLength-1] != '/') { -	    Tcl_DStringAppend(&dsOrig, "/", 1); -	    dirName = Tcl_DStringValue(&dsOrig); -	    baseLength++; -	} -    } +    if (pattern == NULL || (*pattern == '\0')) { +	/* +	 * Match a file directly. +	 */ -    /* -     * Check to see if the pattern needs to compare with hidden files. -     */ +	Tcl_Obj *tailPtr; +	const char *nativeTail; -    if ((pattern[0] == '.') -	    || ((pattern[0] == '\\') && (pattern[1] == '.'))) { -	matchHidden = 1; +	native = Tcl_FSGetNativePath(pathPtr); +	tailPtr = TclPathPart(interp, pathPtr, TCL_PATH_TAIL); +	nativeTail = Tcl_FSGetNativePath(tailPtr); +	matchResult = NativeMatchType(interp, native, nativeTail, types); +	if (matchResult == 1) { +	    Tcl_ListObjAppendElement(interp, resultPtr, pathPtr); +	} +	Tcl_DecrRefCount(tailPtr); +	Tcl_DecrRefCount(fileNamePtr);      } else { -	matchHidden = 0; -    } +	DIR *d; +	Tcl_DirEntry *entryPtr; +	const char *dirName; +	int dirLength, nativeDirLen; +	int matchHidden, matchHiddenPat; +	Tcl_StatBuf statBuf; +	Tcl_DString ds;		/* native encoding of dir */ +	Tcl_DString dsOrig;	/* utf-8 encoding of dir */ -    /* -     * Now open the directory for reading and iterate over the contents. -     */ +	Tcl_DStringInit(&dsOrig); +	dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength); +	Tcl_DStringAppend(&dsOrig, dirName, dirLength); -    native = Tcl_UtfToExternalDString(NULL, dirName, -1, &ds); +	/* +	 * 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 ((stat(native, &statBuf) != 0)		/* INTL: UTF-8. */ -	    || !S_ISDIR(statBuf.st_mode)) { -	Tcl_DStringFree(&dsOrig); -	Tcl_DStringFree(&ds); -	return TCL_OK; -    } +	if (dirLength == 0) { +	    dirName = "."; +	} else { +	    dirName = Tcl_DStringValue(&dsOrig); -    d = opendir(native);				/* INTL: Native. */ -    if (d == NULL) { -        char savedChar = '\0'; -	Tcl_ResetResult(interp); -	Tcl_DStringFree(&ds); +	    /* +	     * Make sure we have a trailing directory delimiter. +	     */ + +	    if (dirName[dirLength-1] != '/') { +		dirName = TclDStringAppendLiteral(&dsOrig, "/"); +		dirLength++; +	    } +	}  	/* -	 * Strip off a trailing '/' if necessary, before reporting the error. +	 * Now open the directory for reading and iterate over the contents.  	 */ -	if (baseLength > 0) { -	    savedChar = (Tcl_DStringValue(&dsOrig))[baseLength-1]; -	    if (savedChar == '/') { -		(Tcl_DStringValue(&dsOrig))[baseLength-1] = '\0'; +	native = Tcl_UtfToExternalDString(NULL, dirName, -1, &ds); + +	if ((TclOSstat(native, &statBuf) != 0)		/* INTL: Native. */ +		|| !S_ISDIR(statBuf.st_mode)) { +	    Tcl_DStringFree(&dsOrig); +	    Tcl_DStringFree(&ds); +	    Tcl_DecrRefCount(fileNamePtr); +	    return TCL_OK; +	} + +	d = opendir(native);				/* INTL: Native. */ +	if (d == NULL) { +	    Tcl_DStringFree(&ds); +	    if (interp != NULL) { +		Tcl_SetObjResult(interp, Tcl_ObjPrintf( +			"couldn't read directory \"%s\": %s", +			Tcl_DStringValue(&dsOrig), Tcl_PosixError(interp)));  	    } +	    Tcl_DStringFree(&dsOrig); +	    Tcl_DecrRefCount(fileNamePtr); +	    return TCL_ERROR;  	} -	Tcl_AppendResult(interp, "couldn't read directory \"", -		Tcl_DStringValue(&dsOrig), "\": ", -		Tcl_PosixError(interp), (char *) NULL); -	if (baseLength > 0) { -	    (Tcl_DStringValue(&dsOrig))[baseLength-1] = savedChar; + +	nativeDirLen = Tcl_DStringLength(&ds); + +	/* +	 * Check to see if -type or the pattern requests hidden files. +	 */ + +	matchHiddenPat = (pattern[0] == '.') +		|| ((pattern[0] == '\\') && (pattern[1] == '.')); +	matchHidden = matchHiddenPat +		|| (types && (types->perm & TCL_GLOB_PERM_HIDDEN)); +	while ((entryPtr = TclOSreaddir(d)) != NULL) {	/* INTL: Native. */ +	    Tcl_DString utfDs; +	    const char *utfname; + +	    /* +	     * Skip this file if it doesn't agree with the hidden parameters +	     * requested by the user (via -type or pattern). +	     */ + +	    if (*entryPtr->d_name == '.') { +		if (!matchHidden) { +		    continue; +		} +	    } else { +#ifdef MAC_OSX_TCL +		if (matchHiddenPat) { +		    continue; +		} +		/* Also need to check HFS hidden flag in TclMacOSXMatchType. */ +#else +		if (matchHidden) { +		    continue; +		} +#endif +	    } + +	    /* +	     * Now check to see if the file matches, according to both type +	     * and pattern. If so, add the file to the result. +	     */ + +	    utfname = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name, -1, +		    &utfDs); +	    if (Tcl_StringCaseMatch(utfname, pattern, 0)) { +		int typeOk = 1; + +		if (types != NULL) { +		    Tcl_DStringSetLength(&ds, nativeDirLen); +		    native = Tcl_DStringAppend(&ds, entryPtr->d_name, -1); +		    matchResult = NativeMatchType(interp, native, +			    entryPtr->d_name, types); +		    typeOk = (matchResult == 1); +		} +		if (typeOk) { +		    Tcl_ListObjAppendElement(interp, resultPtr, +			    TclNewFSPathObj(pathPtr, utfname, +			    Tcl_DStringLength(&utfDs))); +		} +	    } +	    Tcl_DStringFree(&utfDs); +	    if (matchResult < 0) { +		break; +	    }  	} + +	closedir(d); +	Tcl_DStringFree(&ds);  	Tcl_DStringFree(&dsOrig); +	Tcl_DecrRefCount(fileNamePtr); +    } +    if (matchResult < 0) {  	return TCL_ERROR;      } +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NativeMatchType -- + * + *	This routine is used by the globbing code to check if a file matches a + *	given type description. + * + * Results: + *	The return value is 1, 0 or -1 indicating whether the file matches the + *	given criteria, does not match them, or an error occurred (in which + *	case an error is left in interp). + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +static int +NativeMatchType( +    Tcl_Interp *interp,       /* Interpreter to receive errors. */ +    const char *nativeEntry,  /* Native path to check. */ +    const char *nativeName,   /* Native filename to check. */ +    Tcl_GlobTypeData *types)  /* Type description to match against. */ +{ +    Tcl_StatBuf buf; -    nativeDirLen = Tcl_DStringLength(&ds); +    if (types == NULL) { +	/* +	 * Simply check for the file's existence, but do it with lstat, in +	 * case it is a link to a file which doesn't exist (since that case +	 * would not show up if we used 'access' or 'stat') +	 */ -    while (1) { -        Tcl_DString utfDs; -	char *utf; -	struct dirent *entryPtr; -	 -	entryPtr = readdir(d);				/* INTL: Native. */ -	if (entryPtr == NULL) { -	    break; +	if (TclOSlstat(nativeEntry, &buf) != 0) { +	    return 0;  	} +	return 1; +    } -	if (types != NULL && (types->perm & TCL_GLOB_PERM_HIDDEN)) { -	    /*  -	     * We explicitly asked for hidden files, so turn around -	     * and ignore any file which isn't hidden. -	     */ -	    if (*entryPtr->d_name != '.') { -	        continue; -	    } -	} else if (!matchHidden && (*entryPtr->d_name == '.')) { +    if (types->perm != 0) { +	if (TclOSstat(nativeEntry, &buf) != 0) {  	    /* -	     * Don't match names starting with "." unless the "." is -	     * present in the pattern. +	     * Either the file has disappeared between the 'readdir' call and +	     * the 'stat' call, or the file is a link to a file which doesn't +	     * exist (which we could ascertain with lstat), or there is some +	     * other strange problem. In all these cases, we define this to +	     * mean the file does not match any defined permission, and +	     * therefore it is not added to the list of files to return.  	     */ -	    continue; + +	    return 0;  	}  	/* -	 * Now check to see if the file matches, according to both type -	 * and pattern.  If so, add the file to the result. +	 * readonly means that there are NO write permissions (even for user), +	 * but execute is OK for anybody OR that the user immutable flag is +	 * set (where supported).  	 */ -	utf = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name, -1, &utfDs); -	if (Tcl_StringMatch(utf, pattern) != 0) { -	    int typeOk = 1; - -	    Tcl_DStringSetLength(&dsOrig, baseLength); -	    Tcl_DStringAppend(&dsOrig, utf, -1); -	    fname = Tcl_DStringValue(&dsOrig); -	    if (types != NULL) { -		struct stat buf; -		char *nativeEntry; -		Tcl_DStringSetLength(&ds, nativeDirLen); -		Tcl_DStringAppend(&ds, entryPtr->d_name, -1); -		nativeEntry = Tcl_DStringValue(&ds); -		/*  -		 * The native name of the file is in entryPtr->d_name. -		 * We can use this below. +	if (((types->perm & TCL_GLOB_PERM_RONLY) && +#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) +		!(buf.st_flags & UF_IMMUTABLE) && +#endif +		(buf.st_mode & (S_IWOTH|S_IWGRP|S_IWUSR))) || +	    ((types->perm & TCL_GLOB_PERM_R) && +		(access(nativeEntry, R_OK) != 0)) || +	    ((types->perm & TCL_GLOB_PERM_W) && +		(access(nativeEntry, W_OK) != 0)) || +	    ((types->perm & TCL_GLOB_PERM_X) && +		(access(nativeEntry, X_OK) != 0)) +#ifndef MAC_OSX_TCL +	    || ((types->perm & TCL_GLOB_PERM_HIDDEN) && +		(*nativeName != '.')) +#endif /* MAC_OSX_TCL */ +		) { +	    return 0; +	} +    } +    if (types->type != 0) { +	if (types->perm == 0) { +	    /* +	     * We haven't yet done a stat on the file. +	     */ + +	    if (TclOSstat(nativeEntry, &buf) != 0) { +		/* +		 * Posix error occurred. The only ok case is if this is a link +		 * to a nonexistent file, and the user did 'glob -l'. So we +		 * check that here:  		 */ -		 -		if (types->perm != 0) { -		    if (stat(nativeEntry, &buf) != 0) { -			/*  -			 * Either the file has disappeared between the -			 * 'readdir' call and the 'stat' call, or -			 * the file is a link to a file which doesn't -			 * exist (which we could ascertain with -			 * lstat), or there is some other strange -			 * problem.  In all these cases, we define this -			 * to mean the file does not match any defined -			 * permission, and therefore it is not  -			 * added to the list of files to return. -			 */ -			typeOk = 0; -		    } -		     -		    /*  -		     * readonly means that there are NO write permissions -		     * (even for user), but execute is OK for anybody -		     */ -		    if (typeOk && ( -			((types->perm & TCL_GLOB_PERM_RONLY) && -				(buf.st_mode & (S_IWOTH|S_IWGRP|S_IWUSR))) || -			((types->perm & TCL_GLOB_PERM_R) && -				(access(entryPtr->d_name, R_OK) != 0)) || -			((types->perm & TCL_GLOB_PERM_W) && -				(access(entryPtr->d_name, W_OK) != 0)) || -			((types->perm & TCL_GLOB_PERM_X) && -				(access(entryPtr->d_name, X_OK) != 0)) -			)) { -			typeOk = 0; -		    } + +		if ((types->type & TCL_GLOB_TYPE_LINK) +			&& (TclOSlstat(nativeEntry, &buf) == 0) +			&& S_ISLNK(buf.st_mode)) { +		    return 1;  		} -		if (typeOk && (types->type != 0)) { -		    if (types->perm == 0) { -			/* We haven't yet done a stat on the file */ -			if (stat(nativeEntry, &buf) != 0) { -			    /* Posix error occurred */ -			    typeOk = 0; -			} -		    } -		    if (typeOk) { -			/* -			 * In order bcdpfls as in 'find -t' -			 */ -			if ( -			    ((types->type & TCL_GLOB_TYPE_BLOCK) && -				    S_ISBLK(buf.st_mode)) || -			    ((types->type & TCL_GLOB_TYPE_CHAR) && -				    S_ISCHR(buf.st_mode)) || -			    ((types->type & TCL_GLOB_TYPE_DIR) && -				    S_ISDIR(buf.st_mode)) || -			    ((types->type & TCL_GLOB_TYPE_PIPE) && -				    S_ISFIFO(buf.st_mode)) || -			    ((types->type & TCL_GLOB_TYPE_FILE) && -				    S_ISREG(buf.st_mode)) +		return 0; +	    } +	} + +	/* +	 * In order bcdpsfl as in 'find -t' +	 */ + +	if (    ((types->type & TCL_GLOB_TYPE_BLOCK)&& S_ISBLK(buf.st_mode)) || +		((types->type & TCL_GLOB_TYPE_CHAR) && S_ISCHR(buf.st_mode)) || +		((types->type & TCL_GLOB_TYPE_DIR)  && S_ISDIR(buf.st_mode)) || +		((types->type & TCL_GLOB_TYPE_PIPE) && S_ISFIFO(buf.st_mode))||  #ifdef S_ISSOCK -			    || ((types->type & TCL_GLOB_TYPE_SOCK) && -				    S_ISSOCK(buf.st_mode)) -#endif -			    ) { -			    /* Do nothing -- this file is ok */ -			} else { -			    typeOk = 0; +		((types->type & TCL_GLOB_TYPE_SOCK) && S_ISSOCK(buf.st_mode))|| +#endif /* S_ISSOCK */ +		((types->type & TCL_GLOB_TYPE_FILE) && S_ISREG(buf.st_mode))) { +	    /* +	     * Do nothing - this file is ok. +	     */ +	} else {  #ifdef S_ISLNK -			    if (types->type & TCL_GLOB_TYPE_LINK) { -				if (lstat(nativeEntry, &buf) == 0) { -				    if (S_ISLNK(buf.st_mode)) { -				        typeOk = 1; -				    } -				} -			    } -#endif -			} -		    } -		} +	    if ((types->type & TCL_GLOB_TYPE_LINK) +		    && (TclOSlstat(nativeEntry, &buf) == 0) +		    && S_ISLNK(buf.st_mode)) { +		goto filetypeOK;  	    } -	    if (typeOk) { -		Tcl_ListObjAppendElement(interp, resultPtr,  -			Tcl_NewStringObj(fname, Tcl_DStringLength(&dsOrig))); +#endif /* S_ISLNK */ +	    return 0; +	} +    } +  filetypeOK: + +    /* +     * If we're on OSX, we also have to worry about matching the file creator +     * code (if specified). Do that now. +     */ + +#ifdef MAC_OSX_TCL +    if (types->macType != NULL || types->macCreator != NULL || +	    (types->perm & TCL_GLOB_PERM_HIDDEN)) { +	int matchResult; + +	if (types->perm == 0 && types->type == 0) { +	    /* +	     * We haven't yet done a stat on the file. +	     */ + +	    if (TclOSstat(nativeEntry, &buf) != 0) { +		return 0;  	    }  	} -	Tcl_DStringFree(&utfDs); + +	matchResult = TclMacOSXMatchType(interp, nativeEntry, nativeName, +		&buf, types); +	if (matchResult != 1) { +	    return matchResult; +	}      } +#endif /* MAC_OSX_TCL */ -    closedir(d); -    Tcl_DStringFree(&ds); -    Tcl_DStringFree(&dsOrig); -    return result; +    return 1;  }  /* @@ -437,15 +561,15 @@ TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)   *   * TclpGetUserHome --   * - *	This function takes the specified user name and finds their - *	home directory. + *	This function takes the specified user name and finds their home + *	directory.   *   * Results:   *	The result is a pointer to a string specifying the user's home   *	directory, or NULL if the user's home directory could not be - *	determined.  Storage for the result string is allocated in - *	bufferPtr; the caller must call Tcl_DStringFree() when the result - *	is no longer needed. + *	determined. Storage for the result string is allocated in bufferPtr; + *	the caller must call Tcl_DStringFree() when the result is no longer + *	needed.   *   * Side effects:   *	None. @@ -453,26 +577,23 @@ TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)   *----------------------------------------------------------------------   */ -char * -TclpGetUserHome(name, bufferPtr) -    CONST char *name;		/* User name for desired home directory. */ -    Tcl_DString *bufferPtr;	/* Uninitialized or free DString filled -				 * with name of user's home directory. */ +const char * +TclpGetUserHome( +    const char *name,		/* User name for desired home directory. */ +    Tcl_DString *bufferPtr)	/* Uninitialized or free DString filled with +				 * name of user's home directory. */  {      struct passwd *pwPtr;      Tcl_DString ds; -    char *native; +    const char *native = Tcl_UtfToExternalDString(NULL, name, -1, &ds); -    native = Tcl_UtfToExternalDString(NULL, name, -1, &ds); -    pwPtr = getpwnam(native);				/* INTL: Native. */ +    pwPtr = TclpGetPwNam(native);			/* INTL: Native. */      Tcl_DStringFree(&ds); -     +      if (pwPtr == NULL) { -	endpwent();  	return NULL;      }      Tcl_ExternalToUtfDString(NULL, pwPtr->pw_dir, -1, bufferPtr); -    endpwent();      return Tcl_DStringValue(bufferPtr);  } @@ -492,17 +613,17 @@ TclpGetUserHome(name, bufferPtr)   *---------------------------------------------------------------------------   */ -int  -TclpObjAccess(pathPtr, mode) -    Tcl_Obj *pathPtr;        /* Path of file to access */ -    int mode;                /* Permission setting. */ +int +TclpObjAccess( +    Tcl_Obj *pathPtr,		/* Path of file to access */ +    int mode)			/* Permission setting. */  { -    char *path = Tcl_FSGetNativePath(pathPtr); +    const char *path = Tcl_FSGetNativePath(pathPtr); +      if (path == NULL) {  	return -1; -    } else { -	return access(path, mode);      } +    return access(path, mode);  }  /* @@ -516,21 +637,21 @@ TclpObjAccess(pathPtr, mode)   *	See chdir() documentation.   *   * Side effects: - *	See chdir() documentation.   + *	See chdir() documentation.   *   *---------------------------------------------------------------------------   */ -int  -TclpObjChdir(pathPtr) -    Tcl_Obj *pathPtr;          /* Path to new working directory */ +int +TclpObjChdir( +    Tcl_Obj *pathPtr)		/* Path to new working directory */  { -    char *path = Tcl_FSGetNativePath(pathPtr); +    const char *path = Tcl_FSGetNativePath(pathPtr); +      if (path == NULL) {  	return -1; -    } else { -	return chdir(path);      } +    return chdir(path);  }  /* @@ -549,33 +670,28 @@ TclpObjChdir(pathPtr)   *----------------------------------------------------------------------   */ -int  -TclpObjLstat(pathPtr, bufPtr) -    Tcl_Obj *pathPtr;		/* Path of file to stat */ -    struct stat *bufPtr;	/* Filled with results of stat call. */ +int +TclpObjLstat( +    Tcl_Obj *pathPtr,		/* Path of file to stat */ +    Tcl_StatBuf *bufPtr)	/* Filled with results of stat call. */  { -    char *path = Tcl_FSGetNativePath(pathPtr); -    if (path == NULL) { -	return -1; -    } else { -	return lstat(path, bufPtr); -    } +    return TclOSlstat(Tcl_FSGetNativePath(pathPtr), bufPtr);  }  /*   *---------------------------------------------------------------------------   * - * TclpObjGetCwd -- + * TclpGetNativeCwd --   *   *	This function replaces the library version of getcwd().   *   * Results: - *	The result is a pointer to a string specifying the current - *	directory, or NULL if the current directory could not be - *	determined.  If NULL is returned, an error message is left in the - *	interp's result.  Storage for the result string is allocated in - *	bufferPtr; the caller must call Tcl_DStringFree() when the result - *	is no longer needed. + *	The input and output are filesystem paths in native form. The result + *	is either the given clientData, if the working directory hasn't + *	changed, or a new clientData (owned by our caller), giving the new + *	native path, or NULL if the current directory could not be determined. + *	If NULL is returned, the caller can examine the standard posix error + *	codes to determine the cause of the problem.   *   * Side effects:   *	None. @@ -583,39 +699,76 @@ TclpObjLstat(pathPtr, bufPtr)   *----------------------------------------------------------------------   */ -Tcl_Obj*  -TclpObjGetCwd(interp) -    Tcl_Interp *interp; +ClientData +TclpGetNativeCwd( +    ClientData clientData)  { -    Tcl_DString ds; -    if (TclpGetCwd(interp, &ds) != NULL) { -	Tcl_Obj *cwdPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); -	Tcl_IncrRefCount(cwdPtr); -	Tcl_DStringFree(&ds); -	return cwdPtr; -    } else { +    char buffer[MAXPATHLEN+1]; + +#ifdef USEGETWD +    if (getwd(buffer) == NULL) {			/* INTL: Native. */  	return NULL;      } +#else +    if (getcwd(buffer, MAXPATHLEN+1) == NULL) {		/* INTL: Native. */ +	return NULL; +    } +#endif /* USEGETWD */ + +    if ((clientData == NULL) || strcmp(buffer, (const char *) clientData)) { +	char *newCd = ckalloc(strlen(buffer) + 1); + +	strcpy(newCd, buffer); +	return newCd; +    } + +    /* +     * No change to pwd. +     */ + +    return clientData;  } + +/* + *--------------------------------------------------------------------------- + * + * TclpGetCwd -- + * + *	This function replaces the library version of getcwd(). (Obsolete + *	function, only retained for old extensions which may call it + *	directly). + * + * Results: + *	The result is a pointer to a string specifying the current directory, + *	or NULL if the current directory could not be determined. If NULL is + *	returned, an error message is left in the interp's result. Storage for + *	the result string is allocated in bufferPtr; the caller must call + *	Tcl_DStringFree() when the result is no longer needed. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ -/* Older string based version */ -char * -TclpGetCwd(interp, bufferPtr) -    Tcl_Interp *interp;		/* If non-NULL, used for error reporting. */ -    Tcl_DString *bufferPtr;	/* Uninitialized or free DString filled -				 * with name of current directory. */ +const char * +TclpGetCwd( +    Tcl_Interp *interp,		/* If non-NULL, used for error reporting. */ +    Tcl_DString *bufferPtr)	/* Uninitialized or free DString filled with +				 * name of current directory. */  {      char buffer[MAXPATHLEN+1];  #ifdef USEGETWD -    if (getwd(buffer) == NULL) {			/* INTL: Native. */ +    if (getwd(buffer) == NULL)				/* INTL: Native. */  #else -    if (getcwd(buffer, MAXPATHLEN + 1) == NULL) {	/* INTL: Native. */ -#endif +    if (getcwd(buffer, MAXPATHLEN+1) == NULL)		/* INTL: Native. */ +#endif /* USEGETWD */ +    {  	if (interp != NULL) { -	    Tcl_AppendResult(interp, -		    "error getting working directory name: ", -		    Tcl_PosixError(interp), (char *) NULL); +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "error getting working directory name: %s", +		    Tcl_PosixError(interp)));  	}  	return NULL;      } @@ -630,11 +783,11 @@ TclpGetCwd(interp, bufferPtr)   *	This function replaces the library version of readlink().   *   * Results: - *	The result is a pointer to a string specifying the contents - *	of the symbolic link given by 'path', or NULL if the symbolic - *	link could not be read.  Storage for the result string is - *	allocated in bufferPtr; the caller must call Tcl_DStringFree() - *	when the result is no longer needed. + *	The result is a pointer to a string specifying the contents of the + *	symbolic link given by 'path', or NULL if the symbolic link could not + *	be read. Storage for the result string is allocated in bufferPtr; the + *	caller must call Tcl_DStringFree() when the result is no longer + *	needed.   *   * Side effects:   *	See readlink() documentation. @@ -643,26 +796,30 @@ TclpGetCwd(interp, bufferPtr)   */  char * -TclpReadlink(path, linkPtr) -    CONST char *path;		/* Path of file to readlink (UTF-8). */ -    Tcl_DString *linkPtr;	/* Uninitialized or free DString filled -				 * with contents of link (UTF-8). */ +TclpReadlink( +    const char *path,		/* Path of file to readlink (UTF-8). */ +    Tcl_DString *linkPtr)	/* Uninitialized or free DString filled with +				 * contents of link (UTF-8). */  { +#ifndef DJGPP      char link[MAXPATHLEN];      int length; -    char *native; +    const char *native;      Tcl_DString ds;      native = Tcl_UtfToExternalDString(NULL, path, -1, &ds);      length = readlink(native, link, sizeof(link));	/* INTL: Native. */      Tcl_DStringFree(&ds); -     +      if (length < 0) {  	return NULL;      }      Tcl_ExternalToUtfDString(NULL, link, length, linkPtr);      return Tcl_DStringValue(linkPtr); +#else +    return NULL; +#endif /* !DJGPP */  }  /* @@ -681,59 +838,403 @@ TclpReadlink(path, linkPtr)   *----------------------------------------------------------------------   */ -int  -TclpObjStat(pathPtr, bufPtr) -    Tcl_Obj *pathPtr;		/* Path of file to stat */ -    struct stat *bufPtr;	/* Filled with results of stat call. */ +int +TclpObjStat( +    Tcl_Obj *pathPtr,		/* Path of file to stat */ +    Tcl_StatBuf *bufPtr)	/* Filled with results of stat call. */  { -    char *path = Tcl_FSGetNativePath(pathPtr); +    const char *path = Tcl_FSGetNativePath(pathPtr); +      if (path == NULL) {  	return -1; -    } else { -	return stat(path, bufPtr);      } +    return TclOSstat(path, bufPtr);  } -  #ifdef S_IFLNK -Tcl_Obj*  -TclpObjLink(pathPtr, toPtr) -    Tcl_Obj *pathPtr; -    Tcl_Obj *toPtr; +Tcl_Obj * +TclpObjLink( +    Tcl_Obj *pathPtr, +    Tcl_Obj *toPtr, +    int linkAction)  { -    Tcl_Obj* linkPtr = NULL; -      if (toPtr != NULL) { -        return NULL; +	const char *src = Tcl_FSGetNativePath(pathPtr); +	const char *target = NULL; + +	if (src == NULL) { +	    return NULL; +	} + +	/* +	 * If we're making a symbolic link and the path is relative, then we +	 * must check whether it exists _relative_ to the directory in which +	 * the src is found (not relative to the current cwd which is just not +	 * relevant in this case). +	 * +	 * If we're making a hard link, then a relative path is just converted +	 * to absolute relative to the cwd. +	 */ + +	if ((linkAction & TCL_CREATE_SYMBOLIC_LINK) +		&& (Tcl_FSGetPathType(toPtr) == TCL_PATH_RELATIVE)) { +	    Tcl_Obj *dirPtr, *absPtr; + +	    dirPtr = TclPathPart(NULL, pathPtr, TCL_PATH_DIRNAME); +	    if (dirPtr == NULL) { +		return NULL; +	    } +	    absPtr = Tcl_FSJoinToPath(dirPtr, 1, &toPtr); +	    Tcl_IncrRefCount(absPtr); +	    if (Tcl_FSAccess(absPtr, F_OK) == -1) { +		Tcl_DecrRefCount(absPtr); +		Tcl_DecrRefCount(dirPtr); + +		/* +		 * Target doesn't exist. +		 */ + +		errno = ENOENT; +		return NULL; +	    } + +	    /* +	     * Target exists; we'll construct the relative path we want below. +	     */ + +	    Tcl_DecrRefCount(absPtr); +	    Tcl_DecrRefCount(dirPtr); +	} else { +	    target = Tcl_FSGetNativePath(toPtr); +	    if (target == NULL) { +		return NULL; +	    } +	    if (access(target, F_OK) == -1) { +		/* +		 * Target doesn't exist. +		 */ + +		errno = ENOENT; +		return NULL; +	    } +	} + +	if (access(src, F_OK) != -1) { +	    /* +	     * Src exists. +	     */ + +	    errno = EEXIST; +	    return NULL; +	} + +	/* +	 * Check symbolic link flag first, since we prefer to create these. +	 */ + +	if (linkAction & TCL_CREATE_SYMBOLIC_LINK) { +	    int targetLen; +	    Tcl_DString ds; +	    Tcl_Obj *transPtr; + +	    /* +	     * Now we don't want to link to the absolute, normalized path. +	     * Relative links are quite acceptable (but links to ~user are not +	     * -- these must be expanded first). +	     */ + +	    transPtr = Tcl_FSGetTranslatedPath(NULL, toPtr); +	    if (transPtr == NULL) { +		return NULL; +	    } +	    target = Tcl_GetStringFromObj(transPtr, &targetLen); +	    target = Tcl_UtfToExternalDString(NULL, target, targetLen, &ds); +	    Tcl_DecrRefCount(transPtr); + +	    if (symlink(target, src) != 0) { +		toPtr = NULL; +	    } +	    Tcl_DStringFree(&ds); +	} else if (linkAction & TCL_CREATE_HARD_LINK) { +	    if (link(target, src) != 0) { +		return NULL; +	    } +	} else { +	    errno = ENODEV; +	    return NULL; +	} +	return toPtr;      } else { +	Tcl_Obj *linkPtr = NULL; +  	char link[MAXPATHLEN];  	int length; -	char *native; +	Tcl_DString ds; +	Tcl_Obj *transPtr; -	if (Tcl_FSGetTranslatedPath(NULL, pathPtr) == NULL) { +	transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); +	if (transPtr == NULL) {  	    return NULL;  	} +	Tcl_DecrRefCount(transPtr); +  	length = readlink(Tcl_FSGetNativePath(pathPtr), link, sizeof(link));  	if (length < 0) {  	    return NULL;  	} -	 -	/*  -	 * Allocate and copy the name, taking care since the -	 * name need not be null terminated.  -	 */ -	native = (char*)ckalloc((unsigned)(1+length)); -	strncpy(native, link, (unsigned)length); -	native[length] = '\0'; -	 -	linkPtr = Tcl_FSNewNativePath(pathPtr, native); + +	Tcl_ExternalToUtfDString(NULL, link, length, &ds); +	linkPtr = TclDStringToObj(&ds);  	Tcl_IncrRefCount(linkPtr); +	return linkPtr;      } -    return linkPtr;  } +#endif /* S_IFLNK */ + +/* + *--------------------------------------------------------------------------- + * + * TclpFilesystemPathType -- + * + *	This function is part of the native filesystem support, and returns + *	the path type of the given path. Right now it simply returns NULL. In + *	the future it could return specific path types, like 'nfs', 'samba', + *	'FAT32', etc. + * + * Results: + *	NULL at present. + * + * Side effects: + *	None. + * + *--------------------------------------------------------------------------- + */ + +Tcl_Obj * +TclpFilesystemPathType( +    Tcl_Obj *pathPtr) +{ +    /* +     * All native paths are of the same type. +     */ + +    return NULL; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpNativeToNormalized -- + * + *	Convert native format to a normalized path object, with refCount of + *	zero. + * + *	Currently assumes all native paths are actually normalized already, so + *	if the path given is not normalized this will actually just convert to + *	a valid string path, but not necessarily a normalized one. + * + * Results: + *	A valid normalized path. + * + * Side effects: + *	None. + * + *--------------------------------------------------------------------------- + */ -#endif +Tcl_Obj * +TclpNativeToNormalized( +    ClientData clientData) +{ +    Tcl_DString ds; + +    Tcl_ExternalToUtfDString(NULL, (const char *) clientData, -1, &ds); +    return TclDStringToObj(&ds); +} + +/* + *--------------------------------------------------------------------------- + * + * TclNativeCreateNativeRep -- + * + *	Create a native representation for the given path. + * + * Results: + *	The nativePath representation. + * + * Side effects: + *	Memory will be allocated. The path may need to be normalized. + * + *--------------------------------------------------------------------------- + */ + +ClientData +TclNativeCreateNativeRep( +    Tcl_Obj *pathPtr) +{ +    char *nativePathPtr; +    const char *str; +    Tcl_DString ds; +    Tcl_Obj *validPathPtr; +    int len; + +    if (TclFSCwdIsNative()) { +	/* +	 * The cwd is native, which means we can use the translated path +	 * without worrying about normalization (this will also usually be +	 * shorter so the utf-to-external conversion will be somewhat faster). +	 */ + +	validPathPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); +	if (validPathPtr == NULL) { +	    return NULL; +	} +    } else { +	/* +	 * Make sure the normalized path is set. +	 */ + +	validPathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr); +	if (validPathPtr == NULL) { +	    return NULL; +	} +	Tcl_IncrRefCount(validPathPtr); +    } + +    str = Tcl_GetStringFromObj(validPathPtr, &len); +    Tcl_UtfToExternalDString(NULL, str, len, &ds); +    len = Tcl_DStringLength(&ds) + sizeof(char); +    if (strlen(Tcl_DStringValue(&ds)) < len - sizeof(char)) { +	/* See bug [3118489]: NUL in filenames */ +	Tcl_DecrRefCount(validPathPtr); +	Tcl_DStringFree(&ds); +	return NULL; +    } +    Tcl_DecrRefCount(validPathPtr); +    nativePathPtr = ckalloc(len); +    memcpy(nativePathPtr, Tcl_DStringValue(&ds), (size_t) len); + +    Tcl_DStringFree(&ds); +    return nativePathPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * TclNativeDupInternalRep -- + * + *	Duplicate the native representation. + * + * Results: + *	The copied native representation, or NULL if it is not possible to + *	copy the representation. + * + * Side effects: + *	Memory will be allocated for the copy. + * + *--------------------------------------------------------------------------- + */ + +ClientData +TclNativeDupInternalRep( +    ClientData clientData) +{ +    char *copy; +    size_t len; + +    if (clientData == NULL) { +	return NULL; +    } + +    /* +     * ASCII representation when running on Unix. +     */ + +    len = (strlen((const char*) clientData) + 1) * sizeof(char); + +    copy = ckalloc(len); +    memcpy(copy, clientData, len); +    return copy; +} + +/* + *--------------------------------------------------------------------------- + * + * TclpUtime -- + * + *	Set the modification date for a file. + * + * Results: + *	0 on success, -1 on error. + * + * Side effects: + *	None. + * + *--------------------------------------------------------------------------- + */ +int +TclpUtime( +    Tcl_Obj *pathPtr,		/* File to modify */ +    struct utimbuf *tval)	/* New modification date structure */ +{ +    return utime(Tcl_FSGetNativePath(pathPtr), tval); +} + +#ifdef __CYGWIN__ +int +TclOSstat( +    const char *name, +    void *cygstat) +{ +    struct stat buf; +    Tcl_StatBuf *statBuf = cygstat; +    int result = stat(name, &buf); +    statBuf->st_mode = buf.st_mode; +    statBuf->st_ino = buf.st_ino; +    statBuf->st_dev = buf.st_dev; +    statBuf->st_rdev = buf.st_rdev; +    statBuf->st_nlink = buf.st_nlink; +    statBuf->st_uid = buf.st_uid; +    statBuf->st_gid = buf.st_gid; +    statBuf->st_size = buf.st_size; +    statBuf->st_atime = buf.st_atime; +    statBuf->st_mtime = buf.st_mtime; +    statBuf->st_ctime = buf.st_ctime; +    return result; +} + +int +TclOSlstat( +    const char *name, +    void *cygstat) +{ +    struct stat buf; +    Tcl_StatBuf *statBuf = cygstat; +    int result = lstat(name, &buf); + +    statBuf->st_mode = buf.st_mode; +    statBuf->st_ino = buf.st_ino; +    statBuf->st_dev = buf.st_dev; +    statBuf->st_rdev = buf.st_rdev; +    statBuf->st_nlink = buf.st_nlink; +    statBuf->st_uid = buf.st_uid; +    statBuf->st_gid = buf.st_gid; +    statBuf->st_size = buf.st_size; +    statBuf->st_atime = buf.st_atime; +    statBuf->st_mtime = buf.st_mtime; +    statBuf->st_ctime = buf.st_ctime; +    return result; +} +#endif /* CYGWIN */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
