tclUnixFile.c

Go to the documentation of this file.
00001 /*
00002  * tclUnixFile.c --
00003  *
00004  *      This file contains wrappers around UNIX file handling functions.
00005  *      These wrappers mask differences between Windows and UNIX.
00006  *
00007  * Copyright (c) 1995-1998 Sun Microsystems, Inc.
00008  *
00009  * See the file "license.terms" for information on usage and redistribution of
00010  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00011  *
00012  * RCS: @(#) $Id: tclUnixFile.c,v 1.52 2007/12/13 15:28:42 dgp Exp $
00013  */
00014 
00015 #include "tclInt.h"
00016 #include "tclFileSystem.h"
00017 
00018 static int NativeMatchType(Tcl_Interp *interp, CONST char* nativeEntry,
00019         CONST char* nativeName, Tcl_GlobTypeData *types);
00020 
00021 /*
00022  *---------------------------------------------------------------------------
00023  *
00024  * TclpFindExecutable --
00025  *
00026  *      This function computes the absolute path name of the current
00027  *      application, given its argv[0] value.
00028  *
00029  * Results:
00030  *      None.
00031  *
00032  * Side effects:
00033  *      The computed path name is stored as a ProcessGlobalValue.
00034  *
00035  *---------------------------------------------------------------------------
00036  */
00037 
00038 void
00039 TclpFindExecutable(
00040     CONST char *argv0)          /* The value of the application's argv[0]
00041                                  * (native). */
00042 {
00043     CONST char *name, *p;
00044     Tcl_StatBuf statBuf;
00045     Tcl_DString buffer, nameString, cwd, utfName;
00046     Tcl_Encoding encoding;
00047 
00048     if (argv0 == NULL) {
00049         return;
00050     }
00051     Tcl_DStringInit(&buffer);
00052 
00053     name = argv0;
00054     for (p = name; *p != '\0'; p++) {
00055         if (*p == '/') {
00056             /*
00057              * The name contains a slash, so use the name directly without
00058              * doing a path search.
00059              */
00060 
00061             goto gotName;
00062         }
00063     }
00064 
00065     p = getenv("PATH");                                 /* INTL: Native. */
00066     if (p == NULL) {
00067         /*
00068          * There's no PATH environment variable; use the default that is used
00069          * by sh.
00070          */
00071 
00072         p = ":/bin:/usr/bin";
00073     } else if (*p == '\0') {
00074         /*
00075          * An empty path is equivalent to ".".
00076          */
00077 
00078         p = "./";
00079     }
00080 
00081     /*
00082      * Search through all the directories named in the PATH variable to see if
00083      * argv[0] is in one of them. If so, use that file name.
00084      */
00085 
00086     while (1) {
00087         while (isspace(UCHAR(*p))) {                    /* INTL: BUG */
00088             p++;
00089         }
00090         name = p;
00091         while ((*p != ':') && (*p != 0)) {
00092             p++;
00093         }
00094         Tcl_DStringSetLength(&buffer, 0);
00095         if (p != name) {
00096             Tcl_DStringAppend(&buffer, name, p - name);
00097             if (p[-1] != '/') {
00098                 Tcl_DStringAppend(&buffer, "/", 1);
00099             }
00100         }
00101         name = Tcl_DStringAppend(&buffer, argv0, -1);
00102 
00103         /*
00104          * INTL: The following calls to access() and stat() should not be
00105          * converted to Tclp routines because they need to operate on native
00106          * strings directly.
00107          */
00108 
00109         if ((access(name, X_OK) == 0)                   /* INTL: Native. */
00110                 && (TclOSstat(name, &statBuf) == 0)     /* INTL: Native. */
00111                 && S_ISREG(statBuf.st_mode)) {
00112             goto gotName;
00113         }
00114         if (*p == '\0') {
00115             break;
00116         } else if (*(p+1) == 0) {
00117             p = "./";
00118         } else {
00119             p++;
00120         }
00121     }
00122     TclSetObjNameOfExecutable(Tcl_NewObj(), NULL);
00123     goto done;
00124 
00125     /*
00126      * If the name starts with "/" then just store it
00127      */
00128 
00129   gotName:
00130 #ifdef DJGPP
00131     if (name[1] == ':')
00132 #else
00133     if (name[0] == '/')
00134 #endif
00135     {
00136         encoding = Tcl_GetEncoding(NULL, NULL);
00137         Tcl_ExternalToUtfDString(encoding, name, -1, &utfName);
00138         TclSetObjNameOfExecutable(
00139                 Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding);
00140         Tcl_DStringFree(&utfName);
00141         goto done;
00142     }
00143 
00144     /*
00145      * The name is relative to the current working directory. First strip off
00146      * a leading "./", if any, then add the full path name of the current
00147      * working directory.
00148      */
00149 
00150     if ((name[0] == '.') && (name[1] == '/')) {
00151         name += 2;
00152     }
00153 
00154     Tcl_DStringInit(&nameString);
00155     Tcl_DStringAppend(&nameString, name, -1);
00156 
00157     TclpGetCwd(NULL, &cwd);
00158 
00159     Tcl_DStringFree(&buffer);
00160     Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&cwd),
00161             Tcl_DStringLength(&cwd), &buffer);
00162     if (Tcl_DStringValue(&cwd)[Tcl_DStringLength(&cwd) -1] != '/') {
00163         Tcl_DStringAppend(&buffer, "/", 1);
00164     }
00165     Tcl_DStringFree(&cwd);
00166     Tcl_DStringAppend(&buffer, Tcl_DStringValue(&nameString),
00167             Tcl_DStringLength(&nameString));
00168     Tcl_DStringFree(&nameString);
00169 
00170     encoding = Tcl_GetEncoding(NULL, NULL);
00171     Tcl_ExternalToUtfDString(encoding, Tcl_DStringValue(&buffer), -1,
00172             &utfName);
00173     TclSetObjNameOfExecutable(
00174             Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding);
00175     Tcl_DStringFree(&utfName);
00176 
00177   done:
00178     Tcl_DStringFree(&buffer);
00179 }
00180 
00181 /*
00182  *----------------------------------------------------------------------
00183  *
00184  * TclpMatchInDirectory --
00185  *
00186  *      This routine is used by the globbing code to search a directory for
00187  *      all files which match a given pattern.
00188  *
00189  * Results:
00190  *      The return value is a standard Tcl result indicating whether an error
00191  *      occurred in globbing. Errors are left in interp, good results are
00192  *      [lappend]ed to resultPtr (which must be a valid object).
00193  *
00194  * Side effects:
00195  *      None.
00196  *
00197  *----------------------------------------------------------------------
00198  */
00199 
00200 int
00201 TclpMatchInDirectory(
00202     Tcl_Interp *interp,         /* Interpreter to receive errors. */
00203     Tcl_Obj *resultPtr,         /* List object to lappend results. */
00204     Tcl_Obj *pathPtr,           /* Contains path to directory to search. */
00205     CONST char *pattern,        /* Pattern to match against. */
00206     Tcl_GlobTypeData *types)    /* Object containing list of acceptable types.
00207                                  * May be NULL. In particular the directory
00208                                  * flag is very important. */
00209 {
00210     CONST char *native;
00211     Tcl_Obj *fileNamePtr;
00212     int matchResult = 0;
00213 
00214     if (types != NULL && types->type == TCL_GLOB_TYPE_MOUNT) {
00215         /*
00216          * The native filesystem never adds mounts.
00217          */
00218 
00219         return TCL_OK;
00220     }
00221 
00222     fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
00223     if (fileNamePtr == NULL) {
00224         return TCL_ERROR;
00225     }
00226 
00227     if (pattern == NULL || (*pattern == '\0')) {
00228         /*
00229          * Match a file directly.
00230          */
00231         Tcl_Obj *tailPtr;
00232         CONST char *nativeTail;
00233 
00234         native = (CONST char*) Tcl_FSGetNativePath(pathPtr);
00235         tailPtr = TclPathPart(interp, pathPtr, TCL_PATH_TAIL);
00236         nativeTail = (CONST char*) Tcl_FSGetNativePath(tailPtr);
00237         matchResult = NativeMatchType(interp, native, nativeTail, types);
00238         if (matchResult == 1) {
00239             Tcl_ListObjAppendElement(interp, resultPtr, pathPtr);
00240         }
00241         Tcl_DecrRefCount(tailPtr);
00242         Tcl_DecrRefCount(fileNamePtr);
00243     } else {
00244         DIR *d;
00245         Tcl_DirEntry *entryPtr;
00246         CONST char *dirName;
00247         int dirLength;
00248         int matchHidden, matchHiddenPat;
00249         int nativeDirLen;
00250         Tcl_StatBuf statBuf;
00251         Tcl_DString ds;         /* native encoding of dir */
00252         Tcl_DString dsOrig;     /* utf-8 encoding of dir */
00253 
00254         Tcl_DStringInit(&dsOrig);
00255         dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength);
00256         Tcl_DStringAppend(&dsOrig, dirName, dirLength);
00257 
00258         /*
00259          * Make sure that the directory part of the name really is a
00260          * directory.  If the directory name is "", use the name "." instead,
00261          * because some UNIX systems don't treat "" like "." automatically.
00262          * Keep the "" for use in generating file names, otherwise "glob
00263          * foo.c" would return "./foo.c".
00264          */
00265 
00266         if (dirLength == 0) {
00267             dirName = ".";
00268         } else {
00269             dirName = Tcl_DStringValue(&dsOrig);
00270 
00271             /*
00272              * Make sure we have a trailing directory delimiter.
00273              */
00274 
00275             if (dirName[dirLength-1] != '/') {
00276                 dirName = Tcl_DStringAppend(&dsOrig, "/", 1);
00277                 dirLength++;
00278             }
00279         }
00280 
00281         /*
00282          * Now open the directory for reading and iterate over the contents.
00283          */
00284 
00285         native = Tcl_UtfToExternalDString(NULL, dirName, -1, &ds);
00286 
00287         if ((TclOSstat(native, &statBuf) != 0)          /* INTL: Native. */
00288                 || !S_ISDIR(statBuf.st_mode)) {
00289             Tcl_DStringFree(&dsOrig);
00290             Tcl_DStringFree(&ds);
00291             Tcl_DecrRefCount(fileNamePtr);
00292             return TCL_OK;
00293         }
00294 
00295         d = opendir(native);                            /* INTL: Native. */
00296         if (d == NULL) {
00297             Tcl_DStringFree(&ds);
00298             if (interp != NULL) {
00299                 Tcl_ResetResult(interp);
00300                 Tcl_AppendResult(interp, "couldn't read directory \"",
00301                         Tcl_DStringValue(&dsOrig), "\": ",
00302                         Tcl_PosixError(interp), (char *) NULL);
00303             }
00304             Tcl_DStringFree(&dsOrig);
00305             Tcl_DecrRefCount(fileNamePtr);
00306             return TCL_ERROR;
00307         }
00308 
00309         nativeDirLen = Tcl_DStringLength(&ds);
00310 
00311         /*
00312          * Check to see if -type or the pattern requests hidden files.
00313          */
00314 
00315         matchHiddenPat = (pattern[0] == '.')
00316                 || ((pattern[0] == '\\') && (pattern[1] == '.'));
00317         matchHidden = matchHiddenPat 
00318                 || (types && (types->perm & TCL_GLOB_PERM_HIDDEN));
00319         while ((entryPtr = TclOSreaddir(d)) != NULL) {  /* INTL: Native. */
00320             Tcl_DString utfDs;
00321             CONST char *utfname;
00322 
00323             /*
00324              * Skip this file if it doesn't agree with the hidden parameters
00325              * requested by the user (via -type or pattern).
00326              */
00327 
00328             if (*entryPtr->d_name == '.') {
00329                 if (!matchHidden) continue;
00330             } else {
00331 #ifdef MAC_OSX_TCL
00332                 if (matchHiddenPat) continue;
00333                 /* Also need to check HFS hidden flag in TclMacOSXMatchType. */
00334 #else
00335                 if (matchHidden) continue;
00336 #endif
00337             }
00338 
00339             /*
00340              * Now check to see if the file matches, according to both type
00341              * and pattern. If so, add the file to the result.
00342              */
00343 
00344             utfname = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name, -1,
00345                     &utfDs);
00346             if (Tcl_StringCaseMatch(utfname, pattern, 0)) {
00347                 int typeOk = 1;
00348 
00349                 if (types != NULL) {
00350                     Tcl_DStringSetLength(&ds, nativeDirLen);
00351                     native = Tcl_DStringAppend(&ds, entryPtr->d_name, -1);
00352                     matchResult = NativeMatchType(interp, native,
00353                             entryPtr->d_name, types);
00354                     typeOk = (matchResult == 1);
00355                 }
00356                 if (typeOk) {
00357                     Tcl_ListObjAppendElement(interp, resultPtr,
00358                             TclNewFSPathObj(pathPtr, utfname,
00359                             Tcl_DStringLength(&utfDs)));
00360                 }
00361             }
00362             Tcl_DStringFree(&utfDs);
00363             if (matchResult < 0) {
00364                 break;
00365             }
00366         }
00367 
00368         closedir(d);
00369         Tcl_DStringFree(&ds);
00370         Tcl_DStringFree(&dsOrig);
00371         Tcl_DecrRefCount(fileNamePtr);
00372     }
00373     if (matchResult < 0) {
00374         return TCL_ERROR;
00375     } else {
00376         return TCL_OK;
00377     }
00378 }
00379 
00380 /*
00381  *----------------------------------------------------------------------
00382  *
00383  * NativeMatchType --
00384  *
00385  *      This routine is used by the globbing code to check if a file
00386  *      matches a given type description.
00387  *
00388  * Results:
00389  *      The return value is 1, 0 or -1 indicating whether the file
00390  *      matches the given criteria, does not match them, or an error
00391  *      occurred (in wich case an error is left in interp).
00392  *
00393  * Side effects:
00394  *      None.
00395  *
00396  *----------------------------------------------------------------------
00397  */
00398 
00399 static int
00400 NativeMatchType(
00401     Tcl_Interp *interp,       /* Interpreter to receive errors. */
00402     CONST char *nativeEntry,  /* Native path to check. */
00403     CONST char *nativeName,   /* Native filename to check. */
00404     Tcl_GlobTypeData *types)  /* Type description to match against. */
00405 {
00406     Tcl_StatBuf buf;
00407     if (types == NULL) {
00408         /*
00409          * Simply check for the file's existence, but do it with lstat, in
00410          * case it is a link to a file which doesn't exist (since that case
00411          * would not show up if we used 'access' or 'stat')
00412          */
00413 
00414         if (TclOSlstat(nativeEntry, &buf) != 0) {
00415             return 0;
00416         }
00417     } else {
00418         if (types->perm != 0) {
00419             if (TclOSstat(nativeEntry, &buf) != 0) {
00420                 /*
00421                  * Either the file has disappeared between the 'readdir' call
00422                  * and the 'stat' call, or the file is a link to a file which
00423                  * doesn't exist (which we could ascertain with lstat), or
00424                  * there is some other strange problem. In all these cases, we
00425                  * define this to mean the file does not match any defined
00426                  * permission, and therefore it is not added to the list of
00427                  * files to return.
00428                  */
00429 
00430                 return 0;
00431             }
00432 
00433             /*
00434              * readonly means that there are NO write permissions (even for
00435              * user), but execute is OK for anybody OR that the user immutable
00436              * flag is set (where supported).
00437              */
00438 
00439             if (((types->perm & TCL_GLOB_PERM_RONLY) &&
00440 #if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
00441                         !(buf.st_flags & UF_IMMUTABLE) &&
00442 #endif
00443                         (buf.st_mode & (S_IWOTH|S_IWGRP|S_IWUSR))) ||
00444                 ((types->perm & TCL_GLOB_PERM_R) &&
00445                         (access(nativeEntry, R_OK) != 0)) ||
00446                 ((types->perm & TCL_GLOB_PERM_W) &&
00447                         (access(nativeEntry, W_OK) != 0)) ||
00448                 ((types->perm & TCL_GLOB_PERM_X) &&
00449                         (access(nativeEntry, X_OK) != 0))
00450 #ifndef MAC_OSX_TCL
00451                 || ((types->perm & TCL_GLOB_PERM_HIDDEN) &&
00452                         (*nativeName != '.'))
00453 #endif
00454                 ) {
00455                 return 0;
00456             }
00457         }
00458         if (types->type != 0) {
00459             if (types->perm == 0) {
00460                 /*
00461                  * We haven't yet done a stat on the file.
00462                  */
00463 
00464                 if (TclOSstat(nativeEntry, &buf) != 0) {
00465                     /*
00466                      * Posix error occurred. The only ok case is if this is a
00467                      * link to a nonexistent file, and the user did 'glob -l'.
00468                      * So we check that here:
00469                      */
00470 
00471                     if (types->type & TCL_GLOB_TYPE_LINK) {
00472                         if (TclOSlstat(nativeEntry, &buf) == 0) {
00473                             if (S_ISLNK(buf.st_mode)) {
00474                                 return 1;
00475                             }
00476                         }
00477                     }
00478                     return 0;
00479                 }
00480             }
00481 
00482             /*
00483              * In order bcdpfls as in 'find -t'
00484              */
00485 
00486             if (((types->type & TCL_GLOB_TYPE_BLOCK)&& S_ISBLK(buf.st_mode)) ||
00487                 ((types->type & TCL_GLOB_TYPE_CHAR) && S_ISCHR(buf.st_mode)) ||
00488                 ((types->type & TCL_GLOB_TYPE_DIR)  && S_ISDIR(buf.st_mode)) ||
00489                 ((types->type & TCL_GLOB_TYPE_PIPE) && S_ISFIFO(buf.st_mode))||
00490                 ((types->type & TCL_GLOB_TYPE_FILE) && S_ISREG(buf.st_mode))
00491 #ifdef S_ISSOCK
00492                 ||((types->type & TCL_GLOB_TYPE_SOCK) && S_ISSOCK(buf.st_mode))
00493 #endif /* S_ISSOCK */
00494                 ) {
00495                 /*
00496                  * Do nothing - this file is ok.
00497                  */
00498             } else {
00499 #ifdef S_ISLNK
00500                 if (types->type & TCL_GLOB_TYPE_LINK) {
00501                     if (TclOSlstat(nativeEntry, &buf) == 0) {
00502                         if (S_ISLNK(buf.st_mode)) {
00503                             goto filetypeOK;
00504                         }
00505                     }
00506                 }
00507 #endif /* S_ISLNK */
00508                 return 0;
00509             }
00510         }
00511     filetypeOK: ;
00512 #ifdef MAC_OSX_TCL
00513         if (types->macType != NULL || types->macCreator != NULL ||
00514                 (types->perm & TCL_GLOB_PERM_HIDDEN)) {
00515             int matchResult;
00516 
00517             if (types->perm == 0 && types->type == 0) {
00518                 /*
00519                  * We haven't yet done a stat on the file.
00520                  */
00521 
00522                 if (TclOSstat(nativeEntry, &buf) != 0) {
00523                     return 0;
00524                 }
00525             }
00526 
00527             matchResult = TclMacOSXMatchType(interp, nativeEntry, nativeName,
00528                     &buf, types);
00529             if (matchResult != 1) {
00530                 return matchResult;
00531             }
00532         }
00533 #endif
00534     }
00535     return 1;
00536 }
00537 
00538 /*
00539  *---------------------------------------------------------------------------
00540  *
00541  * TclpGetUserHome --
00542  *
00543  *      This function takes the specified user name and finds their home
00544  *      directory.
00545  *
00546  * Results:
00547  *      The result is a pointer to a string specifying the user's home
00548  *      directory, or NULL if the user's home directory could not be
00549  *      determined. Storage for the result string is allocated in bufferPtr;
00550  *      the caller must call Tcl_DStringFree() when the result is no longer
00551  *      needed.
00552  *
00553  * Side effects:
00554  *      None.
00555  *
00556  *----------------------------------------------------------------------
00557  */
00558 
00559 char *
00560 TclpGetUserHome(
00561     CONST char *name,           /* User name for desired home directory. */
00562     Tcl_DString *bufferPtr)     /* Uninitialized or free DString filled with
00563                                  * name of user's home directory. */
00564 {
00565     struct passwd *pwPtr;
00566     Tcl_DString ds;
00567     CONST char *native;
00568 
00569     native = Tcl_UtfToExternalDString(NULL, name, -1, &ds);
00570     pwPtr = getpwnam(native);                           /* INTL: Native. */
00571     Tcl_DStringFree(&ds);
00572 
00573     if (pwPtr == NULL) {
00574         endpwent();
00575         return NULL;
00576     }
00577     Tcl_ExternalToUtfDString(NULL, pwPtr->pw_dir, -1, bufferPtr);
00578     endpwent();
00579     return Tcl_DStringValue(bufferPtr);
00580 }
00581 
00582 /*
00583  *---------------------------------------------------------------------------
00584  *
00585  * TclpObjAccess --
00586  *
00587  *      This function replaces the library version of access().
00588  *
00589  * Results:
00590  *      See access() documentation.
00591  *
00592  * Side effects:
00593  *      See access() documentation.
00594  *
00595  *---------------------------------------------------------------------------
00596  */
00597 
00598 int
00599 TclpObjAccess(
00600     Tcl_Obj *pathPtr,           /* Path of file to access */
00601     int mode)                   /* Permission setting. */
00602 {
00603     CONST char *path = Tcl_FSGetNativePath(pathPtr);
00604     if (path == NULL) {
00605         return -1;
00606     } else {
00607         return access(path, mode);
00608     }
00609 }
00610 
00611 /*
00612  *---------------------------------------------------------------------------
00613  *
00614  * TclpObjChdir --
00615  *
00616  *      This function replaces the library version of chdir().
00617  *
00618  * Results:
00619  *      See chdir() documentation.
00620  *
00621  * Side effects:
00622  *      See chdir() documentation.
00623  *
00624  *---------------------------------------------------------------------------
00625  */
00626 
00627 int
00628 TclpObjChdir(
00629     Tcl_Obj *pathPtr)           /* Path to new working directory */
00630 {
00631     CONST char *path = Tcl_FSGetNativePath(pathPtr);
00632     if (path == NULL) {
00633         return -1;
00634     } else {
00635         return chdir(path);
00636     }
00637 }
00638 
00639 /*
00640  *----------------------------------------------------------------------
00641  *
00642  * TclpObjLstat --
00643  *
00644  *      This function replaces the library version of lstat().
00645  *
00646  * Results:
00647  *      See lstat() documentation.
00648  *
00649  * Side effects:
00650  *      See lstat() documentation.
00651  *
00652  *----------------------------------------------------------------------
00653  */
00654 
00655 int
00656 TclpObjLstat(
00657     Tcl_Obj *pathPtr,           /* Path of file to stat */
00658     Tcl_StatBuf *bufPtr)        /* Filled with results of stat call. */
00659 {
00660     return TclOSlstat(Tcl_FSGetNativePath(pathPtr), bufPtr);
00661 }
00662 
00663 /*
00664  *---------------------------------------------------------------------------
00665  *
00666  * TclpGetNativeCwd --
00667  *
00668  *      This function replaces the library version of getcwd().
00669  *
00670  * Results:
00671  *      The input and output are filesystem paths in native form. The result
00672  *      is either the given clientData, if the working directory hasn't
00673  *      changed, or a new clientData (owned by our caller), giving the new
00674  *      native path, or NULL if the current directory could not be determined.
00675  *      If NULL is returned, the caller can examine the standard posix error
00676  *      codes to determine the cause of the problem.
00677  *
00678  * Side effects:
00679  *      None.
00680  *
00681  *----------------------------------------------------------------------
00682  */
00683 
00684 ClientData
00685 TclpGetNativeCwd(
00686     ClientData clientData)
00687 {
00688     char buffer[MAXPATHLEN+1];
00689 
00690 #ifdef USEGETWD
00691     if (getwd(buffer) == NULL)                          /* INTL: Native. */
00692 #else
00693     if (getcwd(buffer, MAXPATHLEN+1) == NULL)           /* INTL: Native. */
00694 #endif
00695     {
00696         return NULL;
00697     }
00698     if ((clientData != NULL) && strcmp(buffer, (CONST char*)clientData) == 0) {
00699         /*
00700          * No change to pwd.
00701          */
00702 
00703         return clientData;
00704     } else {
00705         char *newCd = (char *) ckalloc((unsigned) (strlen(buffer) + 1));
00706         strcpy(newCd, buffer);
00707         return (ClientData) newCd;
00708     }
00709 }
00710 
00711 /*
00712  *---------------------------------------------------------------------------
00713  *
00714  * TclpGetCwd --
00715  *
00716  *      This function replaces the library version of getcwd(). (Obsolete
00717  *      function, only retained for old extensions which may call it
00718  *      directly).
00719  *
00720  * Results:
00721  *      The result is a pointer to a string specifying the current directory,
00722  *      or NULL if the current directory could not be determined. If NULL is
00723  *      returned, an error message is left in the interp's result. Storage for
00724  *      the result string is allocated in bufferPtr; the caller must call
00725  *      Tcl_DStringFree() when the result is no longer needed.
00726  *
00727  * Side effects:
00728  *      None.
00729  *
00730  *----------------------------------------------------------------------
00731  */
00732 
00733 CONST char *
00734 TclpGetCwd(
00735     Tcl_Interp *interp,         /* If non-NULL, used for error reporting. */
00736     Tcl_DString *bufferPtr)     /* Uninitialized or free DString filled with
00737                                  * name of current directory. */
00738 {
00739     char buffer[MAXPATHLEN+1];
00740 
00741 #ifdef USEGETWD
00742     if (getwd(buffer) == NULL)                          /* INTL: Native. */
00743 #else
00744     if (getcwd(buffer, MAXPATHLEN+1) == NULL)           /* INTL: Native. */
00745 #endif
00746     {
00747         if (interp != NULL) {
00748             Tcl_AppendResult(interp,
00749                     "error getting working directory name: ",
00750                     Tcl_PosixError(interp), NULL);
00751         }
00752         return NULL;
00753     }
00754     return Tcl_ExternalToUtfDString(NULL, buffer, -1, bufferPtr);
00755 }
00756 
00757 /*
00758  *---------------------------------------------------------------------------
00759  *
00760  * TclpReadlink --
00761  *
00762  *      This function replaces the library version of readlink().
00763  *
00764  * Results:
00765  *      The result is a pointer to a string specifying the contents of the
00766  *      symbolic link given by 'path', or NULL if the symbolic link could not
00767  *      be read. Storage for the result string is allocated in bufferPtr; the
00768  *      caller must call Tcl_DStringFree() when the result is no longer
00769  *      needed.
00770  *
00771  * Side effects:
00772  *      See readlink() documentation.
00773  *
00774  *---------------------------------------------------------------------------
00775  */
00776 
00777 char *
00778 TclpReadlink(
00779     CONST char *path,           /* Path of file to readlink (UTF-8). */
00780     Tcl_DString *linkPtr)       /* Uninitialized or free DString filled with
00781                                  * contents of link (UTF-8). */
00782 {
00783 #ifndef DJGPP
00784     char link[MAXPATHLEN];
00785     int length;
00786     CONST char *native;
00787     Tcl_DString ds;
00788 
00789     native = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
00790     length = readlink(native, link, sizeof(link));      /* INTL: Native. */
00791     Tcl_DStringFree(&ds);
00792 
00793     if (length < 0) {
00794         return NULL;
00795     }
00796 
00797     Tcl_ExternalToUtfDString(NULL, link, length, linkPtr);
00798     return Tcl_DStringValue(linkPtr);
00799 #else
00800     return NULL;
00801 #endif
00802 }
00803 
00804 /*
00805  *----------------------------------------------------------------------
00806  *
00807  * TclpObjStat --
00808  *
00809  *      This function replaces the library version of stat().
00810  *
00811  * Results:
00812  *      See stat() documentation.
00813  *
00814  * Side effects:
00815  *      See stat() documentation.
00816  *
00817  *----------------------------------------------------------------------
00818  */
00819 
00820 int
00821 TclpObjStat(
00822     Tcl_Obj *pathPtr,           /* Path of file to stat */
00823     Tcl_StatBuf *bufPtr)        /* Filled with results of stat call. */
00824 {
00825     CONST char *path = Tcl_FSGetNativePath(pathPtr);
00826     if (path == NULL) {
00827         return -1;
00828     } else {
00829         return TclOSstat(path, bufPtr);
00830     }
00831 }
00832 
00833 #ifdef S_IFLNK
00834 
00835 Tcl_Obj*
00836 TclpObjLink(
00837     Tcl_Obj *pathPtr,
00838     Tcl_Obj *toPtr,
00839     int linkAction)
00840 {
00841     if (toPtr != NULL) {
00842         CONST char *src = Tcl_FSGetNativePath(pathPtr);
00843         CONST char *target = NULL;
00844 
00845         if (src == NULL) {
00846             return NULL;
00847         }
00848 
00849         /*
00850          * If we're making a symbolic link and the path is relative, then we
00851          * must check whether it exists _relative_ to the directory in which
00852          * the src is found (not relative to the current cwd which is just not
00853          * relevant in this case).
00854          *
00855          * If we're making a hard link, then a relative path is just converted
00856          * to absolute relative to the cwd.
00857          */
00858 
00859         if ((linkAction & TCL_CREATE_SYMBOLIC_LINK)
00860                 && (Tcl_FSGetPathType(toPtr) == TCL_PATH_RELATIVE)) {
00861             Tcl_Obj *dirPtr, *absPtr;
00862 
00863             dirPtr = TclPathPart(NULL, pathPtr, TCL_PATH_DIRNAME);
00864             if (dirPtr == NULL) {
00865                 return NULL;
00866             }
00867             absPtr = Tcl_FSJoinToPath(dirPtr, 1, &toPtr);
00868             Tcl_IncrRefCount(absPtr);
00869             if (Tcl_FSAccess(absPtr, F_OK) == -1) {
00870                 Tcl_DecrRefCount(absPtr);
00871                 Tcl_DecrRefCount(dirPtr);
00872 
00873                 /*
00874                  * Target doesn't exist.
00875                  */
00876 
00877                 errno = ENOENT;
00878                 return NULL;
00879             }
00880 
00881             /*
00882              * Target exists; we'll construct the relative path we want below.
00883              */
00884 
00885             Tcl_DecrRefCount(absPtr);
00886             Tcl_DecrRefCount(dirPtr);
00887         } else {
00888             target = Tcl_FSGetNativePath(toPtr);
00889             if (target == NULL) {
00890                 return NULL;
00891             }
00892             if (access(target, F_OK) == -1) {
00893                 /*
00894                  * Target doesn't exist.
00895                  */
00896 
00897                 errno = ENOENT;
00898                 return NULL;
00899             }
00900         }
00901 
00902         if (access(src, F_OK) != -1) {
00903             /*
00904              * Src exists.
00905              */
00906 
00907             errno = EEXIST;
00908             return NULL;
00909         }
00910 
00911         /*
00912          * Check symbolic link flag first, since we prefer to create these.
00913          */
00914 
00915         if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
00916             int targetLen;
00917             Tcl_DString ds;
00918             Tcl_Obj *transPtr;
00919 
00920             /*
00921              * Now we don't want to link to the absolute, normalized path.
00922              * Relative links are quite acceptable (but links to ~user are not
00923              * -- these must be expanded first).
00924              */
00925 
00926             transPtr = Tcl_FSGetTranslatedPath(NULL, toPtr);
00927             if (transPtr == NULL) {
00928                 return NULL;
00929             }
00930             target = Tcl_GetStringFromObj(transPtr, &targetLen);
00931             target = Tcl_UtfToExternalDString(NULL, target, targetLen, &ds);
00932             Tcl_DecrRefCount(transPtr);
00933 
00934             if (symlink(target, src) != 0) {
00935                 toPtr = NULL;
00936             }
00937             Tcl_DStringFree(&ds);
00938         } else if (linkAction & TCL_CREATE_HARD_LINK) {
00939             if (link(target, src) != 0) {
00940                 return NULL;
00941             }
00942         } else {
00943             errno = ENODEV;
00944             return NULL;
00945         }
00946         return toPtr;
00947     } else {
00948         Tcl_Obj *linkPtr = NULL;
00949 
00950         char link[MAXPATHLEN];
00951         int length;
00952         Tcl_DString ds;
00953         Tcl_Obj *transPtr;
00954 
00955         transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
00956         if (transPtr == NULL) {
00957             return NULL;
00958         }
00959         Tcl_DecrRefCount(transPtr);
00960 
00961         length = readlink(Tcl_FSGetNativePath(pathPtr), link, sizeof(link));
00962         if (length < 0) {
00963             return NULL;
00964         }
00965 
00966         Tcl_ExternalToUtfDString(NULL, link, length, &ds);
00967         linkPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds),
00968                 Tcl_DStringLength(&ds));
00969         Tcl_DStringFree(&ds);
00970         if (linkPtr != NULL) {
00971             Tcl_IncrRefCount(linkPtr);
00972         }
00973         return linkPtr;
00974     }
00975 }
00976 #endif /* S_IFLNK */
00977 
00978 /*
00979  *---------------------------------------------------------------------------
00980  *
00981  * TclpFilesystemPathType --
00982  *
00983  *      This function is part of the native filesystem support, and returns
00984  *      the path type of the given path. Right now it simply returns NULL. In
00985  *      the future it could return specific path types, like 'nfs', 'samba',
00986  *      'FAT32', etc.
00987  *
00988  * Results:
00989  *      NULL at present.
00990  *
00991  * Side effects:
00992  *      None.
00993  *
00994  *---------------------------------------------------------------------------
00995  */
00996 
00997 Tcl_Obj *
00998 TclpFilesystemPathType(
00999     Tcl_Obj *pathPtr)
01000 {
01001     /*
01002      * All native paths are of the same type.
01003      */
01004 
01005     return NULL;
01006 }
01007 
01008 /*
01009  *---------------------------------------------------------------------------
01010  *
01011  * TclpNativeToNormalized --
01012  *
01013  *      Convert native format to a normalized path object, with refCount of
01014  *      zero.
01015  *
01016  *      Currently assumes all native paths are actually normalized already, so
01017  *      if the path given is not normalized this will actually just convert to
01018  *      a valid string path, but not necessarily a normalized one.
01019  *
01020  * Results:
01021  *      A valid normalized path.
01022  *
01023  * Side effects:
01024  *      None.
01025  *
01026  *---------------------------------------------------------------------------
01027  */
01028 
01029 Tcl_Obj *
01030 TclpNativeToNormalized(
01031     ClientData clientData)
01032 {
01033     Tcl_DString ds;
01034     Tcl_Obj *objPtr;
01035     int len;
01036 
01037     CONST char *copy;
01038     Tcl_ExternalToUtfDString(NULL, (CONST char*)clientData, -1, &ds);
01039 
01040     copy = Tcl_DStringValue(&ds);
01041     len = Tcl_DStringLength(&ds);
01042 
01043     objPtr = Tcl_NewStringObj(copy,len);
01044     Tcl_DStringFree(&ds);
01045 
01046     return objPtr;
01047 }
01048 
01049 /*
01050  *---------------------------------------------------------------------------
01051  *
01052  * TclNativeCreateNativeRep --
01053  *
01054  *      Create a native representation for the given path.
01055  *
01056  * Results:
01057  *      The nativePath representation.
01058  *
01059  * Side effects:
01060  *      Memory will be allocated. The path may need to be normalized.
01061  *
01062  *---------------------------------------------------------------------------
01063  */
01064 
01065 ClientData
01066 TclNativeCreateNativeRep(
01067     Tcl_Obj *pathPtr)
01068 {
01069     char *nativePathPtr;
01070     Tcl_DString ds;
01071     Tcl_Obj *validPathPtr;
01072     int len;
01073     char *str;
01074 
01075     if (TclFSCwdIsNative()) {
01076         /*
01077          * The cwd is native, which means we can use the translated path
01078          * without worrying about normalization (this will also usually be
01079          * shorter so the utf-to-external conversion will be somewhat faster).
01080          */
01081 
01082         validPathPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
01083         if (validPathPtr == NULL) {
01084             return NULL;
01085         }
01086     } else {
01087         /*
01088          * Make sure the normalized path is set.
01089          */
01090 
01091         validPathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
01092         if (validPathPtr == NULL) {
01093             return NULL;
01094         }
01095         Tcl_IncrRefCount(validPathPtr);
01096     }
01097 
01098     str = Tcl_GetStringFromObj(validPathPtr, &len);
01099     Tcl_UtfToExternalDString(NULL, str, len, &ds);
01100     len = Tcl_DStringLength(&ds) + sizeof(char);
01101     Tcl_DecrRefCount(validPathPtr);
01102     nativePathPtr = ckalloc((unsigned) len);
01103     memcpy((void*)nativePathPtr, (void*)Tcl_DStringValue(&ds), (size_t) len);
01104 
01105     Tcl_DStringFree(&ds);
01106     return (ClientData)nativePathPtr;
01107 }
01108 
01109 /*
01110  *---------------------------------------------------------------------------
01111  *
01112  * TclNativeDupInternalRep --
01113  *
01114  *      Duplicate the native representation.
01115  *
01116  * Results:
01117  *      The copied native representation, or NULL if it is not possible to
01118  *      copy the representation.
01119  *
01120  * Side effects:
01121  *      Memory will be allocated for the copy.
01122  *
01123  *---------------------------------------------------------------------------
01124  */
01125 
01126 ClientData
01127 TclNativeDupInternalRep(
01128     ClientData clientData)
01129 {
01130     char *copy;
01131     size_t len;
01132 
01133     if (clientData == NULL) {
01134         return NULL;
01135     }
01136 
01137     /*
01138      * ASCII representation when running on Unix.
01139      */
01140 
01141     len = sizeof(char) + (strlen((CONST char*) clientData) * sizeof(char));
01142 
01143     copy = (char *) ckalloc(len);
01144     memcpy((void *) copy, (void *) clientData, len);
01145     return (ClientData)copy;
01146 }
01147 
01148 /*
01149  *---------------------------------------------------------------------------
01150  *
01151  * TclpUtime --
01152  *
01153  *      Set the modification date for a file.
01154  *
01155  * Results:
01156  *      0 on success, -1 on error.
01157  *
01158  * Side effects:
01159  *      None.
01160  *
01161  *---------------------------------------------------------------------------
01162  */
01163 
01164 int
01165 TclpUtime(
01166     Tcl_Obj *pathPtr,           /* File to modify */
01167     struct utimbuf *tval)       /* New modification date structure */
01168 {
01169     return utime(Tcl_FSGetNativePath(pathPtr), tval);
01170 }
01171 
01172 /*
01173  * Local Variables:
01174  * mode: c
01175  * c-basic-offset: 4
01176  * fill-column: 78
01177  * End:
01178  */



Generated on Wed Mar 12 12:18:26 2008 by  doxygen 1.5.1