os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclUnixFile.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclUnixFile.c --
     3  *
     4  *      This file contains wrappers around UNIX file handling functions.
     5  *      These wrappers mask differences between Windows and UNIX.
     6  *
     7  * Copyright (c) 1995-1998 Sun Microsystems, Inc.
     8  * Portions Copyright (c) 2007 Nokia Corporation and/or its subsidiaries. All rights reserved.   
     9  *
    10  * See the file "license.terms" for information on usage and redistribution
    11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    12  *
    13  * RCS: @(#) $Id: tclUnixFile.c,v 1.32.2.2 2003/10/31 08:46:41 vincentdarley Exp $
    14  */
    15 
    16 #include "tclInt.h"
    17 #include "tclPort.h"
    18 #if defined(__SYMBIAN32__) 
    19 #include "convertPathSlashes.h"
    20 #include "tclSymbianGlobals.h"
    21 #endif 
    22 
    23 static int NativeMatchType(CONST char* nativeName, Tcl_GlobTypeData *types);
    24 
    25 
    26 /*
    27  *---------------------------------------------------------------------------
    28  *
    29  * TclpFindExecutable --
    30  *
    31  *	This procedure computes the absolute path name of the current
    32  *	application, given its argv[0] value.
    33  *
    34  * Results:
    35  *	A dirty UTF string that is the path to the executable.  At this
    36  *	point we may not know the system encoding.  Convert the native
    37  *	string value to UTF using the default encoding.  The assumption
    38  *	is that we will still be able to parse the path given the path
    39  *	name contains ASCII string and '/' chars do not conflict with
    40  *	other UTF chars.
    41  *
    42  * Side effects:
    43  *	The variable tclNativeExecutableName gets filled in with the file
    44  *	name for the application, if we figured it out.  If we couldn't
    45  *	figure it out, tclNativeExecutableName is set to NULL.
    46  *
    47  *---------------------------------------------------------------------------
    48  */
    49 
    50 char *
    51 TclpFindExecutable(argv0)
    52     CONST char *argv0;		/* The value of the application's argv[0]
    53 				 * (native). */
    54 {
    55     CONST char *name, *p;
    56     Tcl_StatBuf statBuf;
    57     int length;
    58     Tcl_DString buffer, nameString;
    59 #ifdef __SYMBIAN32__     
    60     char bufferUsed;
    61 #endif
    62 
    63     if (argv0 == NULL) {
    64 	return NULL;
    65     }
    66     if (tclNativeExecutableName != NULL) {
    67 	return tclNativeExecutableName;
    68     }
    69 
    70 #ifdef __SYMBIAN32__     
    71     // assuming if we're not using eshell that we have to specify the path.
    72     bufferUsed = 0;
    73     if (!strstr(argv0, "Z:\\sys\\bin")) {
    74     	Tcl_DStringInit(&buffer);
    75     	Tcl_DStringSetLength(&buffer, 0);
    76     	Tcl_DStringAppend(&buffer, "Z:\\sys\\bin\\", 11);
    77     	name = Tcl_DStringAppend(&buffer, argv0, -1);
    78         bufferUsed = 1;
    79     }
    80     else    
    81     	name = argv0;  //use if we don't have to specify the path.
    82     
    83     tclNativeExecutableName = ckalloc((unsigned) (strlen(name) + 1));
    84     strcpy(tclNativeExecutableName, name);
    85 
    86     tclCopySymbianPathSlashConversion(TO_TCL, tclNativeExecutableName, tclNativeExecutableName);  
    87  
    88     if (bufferUsed) {
    89     	Tcl_DStringFree(&buffer);
    90     }
    91     
    92     return tclNativeExecutableName;    
    93 #else    
    94     
    95 
    96     Tcl_DStringInit(&buffer);
    97 
    98     name = argv0;
    99     for (p = name; *p != '\0'; p++) {
   100 	if (*p == '/') {
   101 	    /*
   102 	     * The name contains a slash, so use the name directly
   103 	     * without doing a path search.
   104 	     */
   105 
   106 	    goto gotName;
   107 	}
   108     }
   109 
   110     p = getenv("PATH");					/* INTL: Native. */
   111     if (p == NULL) {
   112 	/*
   113 	 * There's no PATH environment variable; use the default that
   114 	 * is used by sh.
   115 	 */
   116 
   117 	p = ":/bin:/usr/bin";
   118     } else if (*p == '\0') {
   119 	/*
   120 	 * An empty path is equivalent to ".".
   121 	 */
   122 
   123 	p = "./";
   124     }
   125 
   126     /*
   127      * Search through all the directories named in the PATH variable
   128      * to see if argv[0] is in one of them.  If so, use that file
   129      * name.
   130      */
   131 
   132     while (1) {
   133 	while (isspace(UCHAR(*p))) {		/* INTL: BUG */
   134 	    p++;
   135 	}
   136 	name = p;
   137 	while ((*p != ':') && (*p != 0)) {
   138 	    p++;
   139 	}
   140 	Tcl_DStringSetLength(&buffer, 0);
   141 	if (p != name) {
   142 	    Tcl_DStringAppend(&buffer, name, p - name);
   143 	    if (p[-1] != '/') {
   144 		Tcl_DStringAppend(&buffer, "/", 1);
   145 	    }
   146 	}
   147 	name = Tcl_DStringAppend(&buffer, argv0, -1);
   148 
   149 	/*
   150 	 * INTL: The following calls to access() and stat() should not be
   151 	 * converted to Tclp routines because they need to operate on native
   152 	 * strings directly.
   153 	 */
   154 
   155 	if ((access(name, X_OK) == 0)			/* INTL: Native. */
   156 		&& (TclOSstat(name, &statBuf) == 0)	/* INTL: Native. */
   157 		&& S_ISREG(statBuf.st_mode)) {
   158 	    goto gotName;
   159 	}
   160 	if (*p == '\0') {
   161 	    break;
   162 	} else if (*(p+1) == 0) {
   163 	    p = "./";
   164 	} else {
   165 	    p++;
   166 	}
   167     }
   168     goto done;
   169 
   170     /*
   171      * If the name starts with "/" then just copy it to tclExecutableName.
   172      */
   173 
   174 gotName:
   175 #ifdef DJGPP
   176     if (name[1] == ':')  {
   177 #else
   178     if (name[0] == '/')  {
   179 #endif
   180 	Tcl_ExternalToUtfDString(NULL, name, -1, &nameString);
   181 	tclNativeExecutableName = (char *)
   182 		ckalloc((unsigned) (Tcl_DStringLength(&nameString) + 1));
   183 	strcpy(tclNativeExecutableName, Tcl_DStringValue(&nameString));
   184 	Tcl_DStringFree(&nameString);
   185 	goto done;
   186     }
   187 
   188     /*
   189      * The name is relative to the current working directory.  First
   190      * strip off a leading "./", if any, then add the full path name of
   191      * the current working directory.
   192      */
   193 
   194     if ((name[0] == '.') && (name[1] == '/')) {
   195 	name += 2;
   196     }
   197 
   198     Tcl_ExternalToUtfDString(NULL, name, -1, &nameString);
   199 
   200     Tcl_DStringFree(&buffer);
   201     TclpGetCwd(NULL, &buffer);
   202 
   203     length = Tcl_DStringLength(&buffer) + Tcl_DStringLength(&nameString) + 2;
   204     tclNativeExecutableName = (char *) ckalloc((unsigned) length);
   205     strcpy(tclNativeExecutableName, Tcl_DStringValue(&buffer));
   206     tclNativeExecutableName[Tcl_DStringLength(&buffer)] = '/';
   207     strcpy(tclNativeExecutableName + Tcl_DStringLength(&buffer) + 1,
   208 	    Tcl_DStringValue(&nameString));
   209     Tcl_DStringFree(&nameString);
   210 
   211 #endif    
   212     
   213 done:
   214     Tcl_DStringFree(&buffer);
   215     return tclNativeExecutableName;
   216 }
   217 
   218 /*
   219  *----------------------------------------------------------------------
   220  *
   221  * TclpMatchInDirectory --
   222  *
   223  *	This routine is used by the globbing code to search a
   224  *	directory for all files which match a given pattern.
   225  *
   226  * Results: 
   227  *	The return value is a standard Tcl result indicating whether an
   228  *	error occurred in globbing.  Errors are left in interp, good
   229  *	results are lappended to resultPtr (which must be a valid object)
   230  *
   231  * Side effects:
   232  *	None.
   233  *
   234  *---------------------------------------------------------------------- */
   235 
   236 int
   237 TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
   238     Tcl_Interp *interp;		/* Interpreter to receive errors. */
   239     Tcl_Obj *resultPtr;		/* List object to lappend results. */
   240     Tcl_Obj *pathPtr;	        /* Contains path to directory to search. */
   241     CONST char *pattern;	/* Pattern to match against. */
   242     Tcl_GlobTypeData *types;	/* Object containing list of acceptable types.
   243 				 * May be NULL. In particular the directory
   244 				 * flag is very important. */
   245 {
   246     CONST char *native;
   247     Tcl_Obj *fileNamePtr;
   248 
   249     fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
   250     if (fileNamePtr == NULL) {
   251 	return TCL_ERROR;
   252     }
   253     
   254     if (pattern == NULL || (*pattern == '\0')) {
   255 	/* Match a file directly */
   256 	native = (CONST char*) Tcl_FSGetNativePath(pathPtr);
   257 	if (NativeMatchType(native, types)) {
   258 	    Tcl_ListObjAppendElement(interp, resultPtr, pathPtr);
   259 	}
   260 	Tcl_DecrRefCount(fileNamePtr);
   261 	return TCL_OK;
   262     } else {
   263 	DIR *d;
   264 	Tcl_DirEntry *entryPtr;
   265 	CONST char *dirName;
   266 	int dirLength;
   267 	int matchHidden;
   268 	int nativeDirLen;
   269 	Tcl_StatBuf statBuf;
   270 	Tcl_DString ds;      /* native encoding of dir */
   271 	Tcl_DString dsOrig;  /* utf-8 encoding of dir */
   272 
   273 	Tcl_DStringInit(&dsOrig);
   274 	dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength);
   275 	Tcl_DStringAppend(&dsOrig, dirName, dirLength);
   276 	
   277 	/*
   278 	 * Make sure that the directory part of the name really is a
   279 	 * directory.  If the directory name is "", use the name "."
   280 	 * instead, because some UNIX systems don't treat "" like "."
   281 	 * automatically.  Keep the "" for use in generating file names,
   282 	 * otherwise "glob foo.c" would return "./foo.c".
   283 	 */
   284 
   285 	if (dirLength == 0) {
   286 	    dirName = ".";
   287 	} else {
   288 	    dirName = Tcl_DStringValue(&dsOrig);
   289 	    /* Make sure we have a trailing directory delimiter */
   290 	    if (dirName[dirLength-1] != '/') {
   291 		dirName = Tcl_DStringAppend(&dsOrig, "/", 1);
   292 		dirLength++;
   293 	    }
   294 	}
   295 	Tcl_DecrRefCount(fileNamePtr);
   296 	
   297 	/*
   298 	 * Now open the directory for reading and iterate over the contents.
   299 	 */
   300 
   301 	native = Tcl_UtfToExternalDString(NULL, dirName, -1, &ds);
   302 
   303 	if ((TclOSstat(native, &statBuf) != 0)		/* INTL: Native. */
   304 		|| !S_ISDIR(statBuf.st_mode)) {
   305 	    Tcl_DStringFree(&dsOrig);
   306 	    Tcl_DStringFree(&ds);
   307 	    return TCL_OK;
   308 	}
   309 
   310 	d = opendir(native);				/* INTL: Native. */
   311 	if (d == NULL) {
   312 	    Tcl_DStringFree(&ds);
   313 	    Tcl_ResetResult(interp);
   314 	    Tcl_AppendResult(interp, "couldn't read directory \"",
   315 		    Tcl_DStringValue(&dsOrig), "\": ",
   316 		    Tcl_PosixError(interp), (char *) NULL);
   317 	    Tcl_DStringFree(&dsOrig);
   318 	    return TCL_ERROR;
   319 	}
   320 
   321 	nativeDirLen = Tcl_DStringLength(&ds);
   322 
   323 	/*
   324 	 * Check to see if -type or the pattern requests hidden files.
   325 	 */
   326 	matchHidden = ((types && (types->perm & TCL_GLOB_PERM_HIDDEN)) ||
   327 		((pattern[0] == '.')
   328 			|| ((pattern[0] == '\\') && (pattern[1] == '.'))));
   329 
   330 	while ((entryPtr = TclOSreaddir(d)) != NULL) { /* INTL: Native. */
   331 	    Tcl_DString utfDs;
   332 	    CONST char *utfname;
   333 
   334 	    /* 
   335 	     * Skip this file if it doesn't agree with the hidden
   336 	     * parameters requested by the user (via -type or pattern).
   337 	     */
   338 	    if (*entryPtr->d_name == '.') {
   339 		if (!matchHidden) continue;
   340 	    } else {
   341 		if (matchHidden) continue;
   342 	    }
   343 
   344 	    /*
   345 	     * Now check to see if the file matches, according to both type
   346 	     * and pattern.  If so, add the file to the result.
   347 	     */
   348 
   349 	    utfname = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name,
   350 		    -1, &utfDs);
   351 	    if (Tcl_StringCaseMatch(utfname, pattern, 0)) {
   352 		int typeOk = 1;
   353 
   354 		if (types != NULL) {
   355 		    Tcl_DStringSetLength(&ds, nativeDirLen);
   356 		    native = Tcl_DStringAppend(&ds, entryPtr->d_name, -1);
   357 		    typeOk = NativeMatchType(native, types);
   358 		}
   359 		if (typeOk) {
   360 		    Tcl_ListObjAppendElement(interp, resultPtr, 
   361 			    TclNewFSPathObj(pathPtr, utfname,
   362 				    Tcl_DStringLength(&utfDs)));
   363 		}
   364 	    }
   365 	    Tcl_DStringFree(&utfDs);
   366 	}
   367 
   368 	closedir(d);
   369 	Tcl_DStringFree(&ds);
   370 	Tcl_DStringFree(&dsOrig);
   371 	return TCL_OK;
   372     }
   373 }
   374 static int 
   375 NativeMatchType(
   376     CONST char* nativeEntry,  /* Native path to check */
   377     Tcl_GlobTypeData *types)  /* Type description to match against */
   378 {
   379     Tcl_StatBuf buf;
   380     if (types == NULL) {
   381 	/* 
   382 	 * Simply check for the file's existence, but do it
   383 	 * with lstat, in case it is a link to a file which
   384 	 * doesn't exist (since that case would not show up
   385 	 * if we used 'access' or 'stat')
   386 	 */
   387 	if (TclOSlstat(nativeEntry, &buf) != 0) {
   388 	    return 0;
   389 	}
   390     } else {
   391 	if (types->perm != 0) {
   392 	    if (TclOSstat(nativeEntry, &buf) != 0) {
   393 		/* 
   394 		 * Either the file has disappeared between the
   395 		 * 'readdir' call and the 'stat' call, or
   396 		 * the file is a link to a file which doesn't
   397 		 * exist (which we could ascertain with
   398 		 * lstat), or there is some other strange
   399 		 * problem.  In all these cases, we define this
   400 		 * to mean the file does not match any defined
   401 		 * permission, and therefore it is not 
   402 		 * added to the list of files to return.
   403 		 */
   404 		return 0;
   405 	    }
   406 	    
   407 	    /* 
   408 	     * readonly means that there are NO write permissions
   409 	     * (even for user), but execute is OK for anybody
   410 	     */
   411 	    if (((types->perm & TCL_GLOB_PERM_RONLY) &&
   412 			(buf.st_mode & (S_IWOTH|S_IWGRP|S_IWUSR))) ||
   413 		((types->perm & TCL_GLOB_PERM_R) &&
   414 			(access(nativeEntry, R_OK) != 0)) ||
   415 		((types->perm & TCL_GLOB_PERM_W) &&
   416 			(access(nativeEntry, W_OK) != 0)) ||
   417 		((types->perm & TCL_GLOB_PERM_X) &&
   418 			(access(nativeEntry, X_OK) != 0))
   419 		) {
   420 		return 0;
   421 	    }
   422 	}
   423 	if (types->type != 0) {
   424 	    if (types->perm == 0) {
   425 		/* We haven't yet done a stat on the file */
   426 		if (TclOSstat(nativeEntry, &buf) != 0) {
   427 		    /* 
   428 		     * Posix error occurred.  The only ok
   429 		     * case is if this is a link to a nonexistent
   430 		     * file, and the user did 'glob -l'. So
   431 		     * we check that here:
   432 		     */
   433 		    if (types->type & TCL_GLOB_TYPE_LINK) {
   434 			if (TclOSlstat(nativeEntry, &buf) == 0) {
   435 			    if (S_ISLNK(buf.st_mode)) {
   436 				return 1;
   437 			    }
   438 			}
   439 		    }
   440 		    return 0;
   441 		}
   442 	    }
   443 	    /*
   444 	     * In order bcdpfls as in 'find -t'
   445 	     */
   446 	    if (
   447 		((types->type & TCL_GLOB_TYPE_BLOCK) &&
   448 			S_ISBLK(buf.st_mode)) ||
   449 		((types->type & TCL_GLOB_TYPE_CHAR) &&
   450 			S_ISCHR(buf.st_mode)) ||
   451 		((types->type & TCL_GLOB_TYPE_DIR) &&
   452 			S_ISDIR(buf.st_mode)) ||
   453 		((types->type & TCL_GLOB_TYPE_PIPE) &&
   454 			S_ISFIFO(buf.st_mode)) ||
   455 		((types->type & TCL_GLOB_TYPE_FILE) &&
   456 			S_ISREG(buf.st_mode))
   457 #ifdef S_ISSOCK
   458 		|| ((types->type & TCL_GLOB_TYPE_SOCK) &&
   459 			S_ISSOCK(buf.st_mode))
   460 #endif /* S_ISSOCK */
   461 		) {
   462 		/* Do nothing -- this file is ok */
   463 	    } else {
   464 #ifdef S_ISLNK
   465 		if (types->type & TCL_GLOB_TYPE_LINK) {
   466 		    if (TclOSlstat(nativeEntry, &buf) == 0) {
   467 			if (S_ISLNK(buf.st_mode)) {
   468 			    return 1;
   469 			}
   470 		    }
   471 		}
   472 #endif /* S_ISLNK */
   473 		return 0;
   474 	    }
   475 	}
   476     }
   477     return 1;
   478 }
   479 
   480 /*
   481  *---------------------------------------------------------------------------
   482  *
   483  * TclpGetUserHome --
   484  *
   485  *	This function takes the specified user name and finds their
   486  *	home directory.
   487  *
   488  * Results:
   489  *	The result is a pointer to a string specifying the user's home
   490  *	directory, or NULL if the user's home directory could not be
   491  *	determined.  Storage for the result string is allocated in
   492  *	bufferPtr; the caller must call Tcl_DStringFree() when the result
   493  *	is no longer needed.
   494  *
   495  * Side effects:
   496  *	None.
   497  *
   498  *----------------------------------------------------------------------
   499  */
   500 
   501 char *
   502 TclpGetUserHome(name, bufferPtr)
   503     CONST char *name;		/* User name for desired home directory. */
   504     Tcl_DString *bufferPtr;	/* Uninitialized or free DString filled
   505 				 * with name of user's home directory. */
   506 {
   507     struct passwd *pwPtr;
   508     Tcl_DString ds;
   509     CONST char *native;
   510 
   511     native = Tcl_UtfToExternalDString(NULL, name, -1, &ds);
   512     pwPtr = getpwnam(native);				/* INTL: Native. */
   513     Tcl_DStringFree(&ds);
   514     
   515     if (pwPtr == NULL) {
   516 	endpwent();
   517 	return NULL;
   518     }
   519     Tcl_ExternalToUtfDString(NULL, pwPtr->pw_dir, -1, bufferPtr);
   520     endpwent();
   521     return Tcl_DStringValue(bufferPtr);
   522 }
   523 
   524 /*
   525  *---------------------------------------------------------------------------
   526  *
   527  * TclpObjAccess --
   528  *
   529  *	This function replaces the library version of access().
   530  *
   531  * Results:
   532  *	See access() documentation.
   533  *
   534  * Side effects:
   535  *	See access() documentation.
   536  *
   537  *---------------------------------------------------------------------------
   538  */
   539 
   540 int 
   541 TclpObjAccess(pathPtr, mode)
   542     Tcl_Obj *pathPtr;        /* Path of file to access */
   543     int mode;                /* Permission setting. */
   544 {
   545     CONST char *path = Tcl_FSGetNativePath(pathPtr);
   546     if (path == NULL) {
   547 	return -1;
   548     } else {
   549 	return access(path, mode);
   550     }
   551 }
   552 
   553 /*
   554  *---------------------------------------------------------------------------
   555  *
   556  * TclpObjChdir --
   557  *
   558  *	This function replaces the library version of chdir().
   559  *
   560  * Results:
   561  *	See chdir() documentation.
   562  *
   563  * Side effects:
   564  *	See chdir() documentation.  
   565  *
   566  *---------------------------------------------------------------------------
   567  */
   568 
   569 int 
   570 TclpObjChdir(pathPtr)
   571     Tcl_Obj *pathPtr;          /* Path to new working directory */
   572 {
   573     CONST char *path = Tcl_FSGetNativePath(pathPtr);
   574     if (path == NULL) {
   575 	return -1;
   576     } else {
   577 	return chdir(path);
   578     }
   579 }
   580 
   581 /*
   582  *----------------------------------------------------------------------
   583  *
   584  * TclpObjLstat --
   585  *
   586  *	This function replaces the library version of lstat().
   587  *
   588  * Results:
   589  *	See lstat() documentation.
   590  *
   591  * Side effects:
   592  *	See lstat() documentation.
   593  *
   594  *----------------------------------------------------------------------
   595  */
   596 
   597 int 
   598 TclpObjLstat(pathPtr, bufPtr)
   599     Tcl_Obj *pathPtr;		/* Path of file to stat */
   600     Tcl_StatBuf *bufPtr;	/* Filled with results of stat call. */
   601 {
   602     return TclOSlstat(Tcl_FSGetNativePath(pathPtr), bufPtr);
   603 }
   604 
   605 /*
   606  *---------------------------------------------------------------------------
   607  *
   608  * TclpObjGetCwd --
   609  *
   610  *	This function replaces the library version of getcwd().
   611  *
   612  * Results:
   613  *	The result is a pointer to a string specifying the current
   614  *	directory, or NULL if the current directory could not be
   615  *	determined.  If NULL is returned, an error message is left in the
   616  *	interp's result.  Storage for the result string is allocated in
   617  *	bufferPtr; the caller must call Tcl_DStringFree() when the result
   618  *	is no longer needed.
   619  *
   620  * Side effects:
   621  *	None.
   622  *
   623  *----------------------------------------------------------------------
   624  */
   625 
   626 Tcl_Obj* 
   627 TclpObjGetCwd(interp)
   628     Tcl_Interp *interp;
   629 {
   630     Tcl_DString ds;
   631     if (TclpGetCwd(interp, &ds) != NULL) {
   632 	Tcl_Obj *cwdPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
   633 	Tcl_IncrRefCount(cwdPtr);
   634 	Tcl_DStringFree(&ds);
   635 	return cwdPtr;
   636     } else {
   637 	return NULL;
   638     }
   639 }
   640 
   641 /* Older string based version */
   642 CONST char *
   643 TclpGetCwd(interp, bufferPtr)
   644     Tcl_Interp *interp;		/* If non-NULL, used for error reporting. */
   645     Tcl_DString *bufferPtr;	/* Uninitialized or free DString filled
   646 				 * with name of current directory. */
   647 {
   648     char buffer[MAXPATHLEN+1];
   649 
   650 #ifdef USEGETWD
   651     if (getwd(buffer) == NULL) {			/* INTL: Native. */
   652 #else
   653     if (getcwd(buffer, MAXPATHLEN + 1) == NULL) {	/* INTL: Native. */
   654 #endif
   655 	if (interp != NULL) {
   656 	    Tcl_AppendResult(interp,
   657 		    "error getting working directory name: ",
   658 		    Tcl_PosixError(interp), (char *) NULL);
   659 	}
   660 	return NULL;
   661     }
   662 	/* TODO - the line bellow is a temporary patch. The defect number is: DEF116621. */
   663 	buffer[0] = 'c';
   664     return Tcl_ExternalToUtfDString(NULL, buffer, -1, bufferPtr);
   665 }
   666 
   667 /*
   668  *---------------------------------------------------------------------------
   669  *
   670  * TclpReadlink --
   671  *
   672  *	This function replaces the library version of readlink().
   673  *
   674  * Results:
   675  *	The result is a pointer to a string specifying the contents
   676  *	of the symbolic link given by 'path', or NULL if the symbolic
   677  *	link could not be read.  Storage for the result string is
   678  *	allocated in bufferPtr; the caller must call Tcl_DStringFree()
   679  *	when the result is no longer needed.
   680  *
   681  * Side effects:
   682  *	See readlink() documentation.
   683  *
   684  *---------------------------------------------------------------------------
   685  */
   686 
   687 char *
   688 TclpReadlink(path, linkPtr)
   689     CONST char *path;		/* Path of file to readlink (UTF-8). */
   690     Tcl_DString *linkPtr;	/* Uninitialized or free DString filled
   691 				 * with contents of link (UTF-8). */
   692 {
   693 #ifndef DJGPP
   694     char link[MAXPATHLEN];
   695     int length;
   696     CONST char *native;
   697     Tcl_DString ds;
   698 
   699     native = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
   700     length = readlink(native, link, sizeof(link));	/* INTL: Native. */
   701     Tcl_DStringFree(&ds);
   702     
   703     if (length < 0) {
   704 	return NULL;
   705     }
   706 
   707     Tcl_ExternalToUtfDString(NULL, link, length, linkPtr);
   708     return Tcl_DStringValue(linkPtr);
   709 #else
   710     return NULL;
   711 #endif
   712 }
   713 
   714 /*
   715  *----------------------------------------------------------------------
   716  *
   717  * TclpObjStat --
   718  *
   719  *	This function replaces the library version of stat().
   720  *
   721  * Results:
   722  *	See stat() documentation.
   723  *
   724  * Side effects:
   725  *	See stat() documentation.
   726  *
   727  *----------------------------------------------------------------------
   728  */
   729 
   730 int 
   731 TclpObjStat(pathPtr, bufPtr)
   732     Tcl_Obj *pathPtr;		/* Path of file to stat */
   733     Tcl_StatBuf *bufPtr;	/* Filled with results of stat call. */
   734 {
   735     CONST char *path = Tcl_FSGetNativePath(pathPtr);
   736     if (path == NULL) {
   737 	return -1;
   738     } else {
   739 	return TclOSstat(path, bufPtr);
   740     }
   741 }
   742 
   743 
   744 #ifdef S_IFLNK
   745 
   746 Tcl_Obj* 
   747 TclpObjLink(pathPtr, toPtr, linkAction)
   748     Tcl_Obj *pathPtr;
   749     Tcl_Obj *toPtr;
   750     int linkAction;
   751 {
   752     if (toPtr != NULL) {
   753 	CONST char *src = Tcl_FSGetNativePath(pathPtr);
   754 	CONST char *target = Tcl_FSGetNativePath(toPtr);
   755 	
   756 	if (src == NULL || target == NULL) {
   757 	    return NULL;
   758 	}
   759 	if (access(src, F_OK) != -1) {
   760 	    /* src exists */
   761 	    errno = EEXIST;
   762 	    return NULL;
   763 	}
   764 	if (access(target, F_OK) == -1) {
   765 	    /* target doesn't exist */
   766 	    errno = ENOENT;
   767 	    return NULL;
   768 	}
   769 	/* 
   770 	 * Check symbolic link flag first, since we prefer to
   771 	 * create these.
   772 	 */
   773 	if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
   774 	    if (symlink(target, src) != 0) return NULL;
   775 	} else if (linkAction & TCL_CREATE_HARD_LINK) {
   776 	    if (link(target, src) != 0) return NULL;
   777 	} else {
   778 	    errno = ENODEV;
   779 	    return NULL;
   780 	}
   781 	return toPtr;
   782     } else {
   783 	Tcl_Obj* linkPtr = NULL;
   784 
   785 	char link[MAXPATHLEN];
   786 	int length;
   787 	Tcl_DString ds;
   788 	Tcl_Obj *transPtr;
   789 	
   790 	transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
   791 	if (transPtr == NULL) {
   792 	    return NULL;
   793 	}
   794 	Tcl_DecrRefCount(transPtr);
   795 	
   796 	length = readlink(Tcl_FSGetNativePath(pathPtr), link, sizeof(link));
   797 	if (length < 0) {
   798 	    return NULL;
   799 	}
   800 
   801 	Tcl_ExternalToUtfDString(NULL, link, length, &ds);
   802 	linkPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), 
   803 				   Tcl_DStringLength(&ds));
   804 	Tcl_DStringFree(&ds);
   805 	if (linkPtr != NULL) {
   806 	    Tcl_IncrRefCount(linkPtr);
   807 	}
   808 	return linkPtr;
   809     }
   810 }
   811 
   812 #endif
   813 
   814 
   815 /*
   816  *---------------------------------------------------------------------------
   817  *
   818  * TclpFilesystemPathType --
   819  *
   820  *      This function is part of the native filesystem support, and
   821  *      returns the path type of the given path.  Right now it simply
   822  *      returns NULL.  In the future it could return specific path
   823  *      types, like 'nfs', 'samba', 'FAT32', etc.
   824  *
   825  * Results:
   826  *      NULL at present.
   827  *
   828  * Side effects:
   829  *	None.
   830  *
   831  *---------------------------------------------------------------------------
   832  */
   833 Tcl_Obj*
   834 TclpFilesystemPathType(pathObjPtr)
   835     Tcl_Obj* pathObjPtr;
   836 {
   837     /* All native paths are of the same type */
   838     return NULL;
   839 }
   840 
   841 /*
   842  *---------------------------------------------------------------------------
   843  *
   844  * TclpUtime --
   845  *
   846  *	Set the modification date for a file.
   847  *
   848  * Results:
   849  *	0 on success, -1 on error.
   850  *
   851  * Side effects:
   852  *	None.
   853  *
   854  *---------------------------------------------------------------------------
   855  */
   856 int 
   857 TclpUtime(pathPtr, tval)
   858     Tcl_Obj *pathPtr;      /* File to modify */
   859     struct utimbuf *tval;  /* New modification date structure */
   860 {
   861     return utime(Tcl_FSGetNativePath(pathPtr),tval);
   862 }