4 * This file contains wrappers around UNIX file handling functions.
5 * These wrappers mask differences between Windows and UNIX.
7 * Copyright (c) 1995-1998 Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 static int NativeMatchType(CONST char* nativeName, Tcl_GlobTypeData *types);
22 *---------------------------------------------------------------------------
24 * TclpFindExecutable --
26 * This procedure computes the absolute path name of the current
27 * application, given its argv[0] value.
30 * A dirty UTF string that is the path to the executable. At this
31 * point we may not know the system encoding. Convert the native
32 * string value to UTF using the default encoding. The assumption
33 * is that we will still be able to parse the path given the path
34 * name contains ASCII string and '/' chars do not conflict with
38 * The variable tclNativeExecutableName gets filled in with the file
39 * name for the application, if we figured it out. If we couldn't
40 * figure it out, tclNativeExecutableName is set to NULL.
42 *---------------------------------------------------------------------------
46 TclpFindExecutable(argv0)
47 CONST char *argv0; /* The value of the application's argv[0]
53 Tcl_DString buffer, nameString;
58 if (tclNativeExecutableName != NULL) {
59 return tclNativeExecutableName;
62 Tcl_DStringInit(&buffer);
65 for (p = name; *p != '\0'; p++) {
68 * The name contains a slash, so use the name directly
69 * without doing a path search.
76 p = getenv("PATH"); /* INTL: Native. */
79 * There's no PATH environment variable; use the default that
84 } else if (*p == '\0') {
86 * An empty path is equivalent to ".".
93 * Search through all the directories named in the PATH variable
94 * to see if argv[0] is in one of them. If so, use that file
99 while (isspace(UCHAR(*p))) { /* INTL: BUG */
103 while ((*p != ':') && (*p != 0)) {
106 Tcl_DStringSetLength(&buffer, 0);
108 Tcl_DStringAppend(&buffer, name, p - name);
110 Tcl_DStringAppend(&buffer, "/", 1);
113 name = Tcl_DStringAppend(&buffer, argv0, -1);
116 * INTL: The following calls to access() and stat() should not be
117 * converted to Tclp routines because they need to operate on native
121 if ((access(name, X_OK) == 0) /* INTL: Native. */
122 && (TclOSstat(name, &statBuf) == 0) /* INTL: Native. */
123 && S_ISREG(statBuf.st_mode)) {
128 } else if (*(p+1) == 0) {
137 * If the name starts with "/" then just copy it to tclExecutableName.
142 if (name[1] == ':') {
144 if (name[0] == '/') {
146 Tcl_ExternalToUtfDString(NULL, name, -1, &nameString);
147 tclNativeExecutableName = (char *)
148 ckalloc((unsigned) (Tcl_DStringLength(&nameString) + 1));
149 strcpy(tclNativeExecutableName, Tcl_DStringValue(&nameString));
150 Tcl_DStringFree(&nameString);
155 * The name is relative to the current working directory. First
156 * strip off a leading "./", if any, then add the full path name of
157 * the current working directory.
160 if ((name[0] == '.') && (name[1] == '/')) {
164 Tcl_ExternalToUtfDString(NULL, name, -1, &nameString);
166 Tcl_DStringFree(&buffer);
167 TclpGetCwd(NULL, &buffer);
169 length = Tcl_DStringLength(&buffer) + Tcl_DStringLength(&nameString) + 2;
170 tclNativeExecutableName = (char *) ckalloc((unsigned) length);
171 strcpy(tclNativeExecutableName, Tcl_DStringValue(&buffer));
172 tclNativeExecutableName[Tcl_DStringLength(&buffer)] = '/';
173 strcpy(tclNativeExecutableName + Tcl_DStringLength(&buffer) + 1,
174 Tcl_DStringValue(&nameString));
175 Tcl_DStringFree(&nameString);
178 Tcl_DStringFree(&buffer);
179 return tclNativeExecutableName;
183 *----------------------------------------------------------------------
185 * TclpMatchInDirectory --
187 * This routine is used by the globbing code to search a
188 * directory for all files which match a given pattern.
191 * The return value is a standard Tcl result indicating whether an
192 * error occurred in globbing. Errors are left in interp, good
193 * results are lappended to resultPtr (which must be a valid object)
198 *---------------------------------------------------------------------- */
201 TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
202 Tcl_Interp *interp; /* Interpreter to receive errors. */
203 Tcl_Obj *resultPtr; /* List object to lappend results. */
204 Tcl_Obj *pathPtr; /* Contains path to directory to search. */
205 CONST char *pattern; /* Pattern to match against. */
206 Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
207 * May be NULL. In particular the directory
208 * flag is very important. */
211 Tcl_Obj *fileNamePtr;
213 fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
214 if (fileNamePtr == NULL) {
218 if (pattern == NULL || (*pattern == '\0')) {
219 /* Match a file directly */
220 CONST char *native = (CONST char*) Tcl_FSGetNativePath(pathPtr);
221 if (NativeMatchType(native, types)) {
222 Tcl_ListObjAppendElement(interp, resultPtr, pathPtr);
226 CONST char *fname, *dirName;
236 Tcl_DStringInit(&dsOrig);
237 Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1);
238 baseLength = Tcl_DStringLength(&dsOrig);
241 * Make sure that the directory part of the name really is a
242 * directory. If the directory name is "", use the name "."
243 * instead, because some UNIX systems don't treat "" like "."
244 * automatically. Keep the "" for use in generating file names,
245 * otherwise "glob foo.c" would return "./foo.c".
248 if (baseLength == 0) {
251 dirName = Tcl_DStringValue(&dsOrig);
252 /* Make sure we have a trailing directory delimiter */
253 if (dirName[baseLength-1] != '/') {
254 dirName = Tcl_DStringAppend(&dsOrig, "/", 1);
260 * Check to see if the pattern needs to compare with hidden files.
263 if ((pattern[0] == '.')
264 || ((pattern[0] == '\\') && (pattern[1] == '.'))) {
271 * Now open the directory for reading and iterate over the contents.
274 native = Tcl_UtfToExternalDString(NULL, dirName, -1, &ds);
276 if ((TclOSstat(native, &statBuf) != 0) /* INTL: Native. */
277 || !S_ISDIR(statBuf.st_mode)) {
278 Tcl_DStringFree(&dsOrig);
279 Tcl_DStringFree(&ds);
283 d = opendir(native); /* INTL: Native. */
285 char savedChar = '\0';
286 Tcl_ResetResult(interp);
287 Tcl_DStringFree(&ds);
290 * Strip off a trailing '/' if necessary, before reporting the error.
293 if (baseLength > 0) {
294 savedChar = (Tcl_DStringValue(&dsOrig))[baseLength-1];
295 if (savedChar == '/') {
296 (Tcl_DStringValue(&dsOrig))[baseLength-1] = '\0';
299 Tcl_AppendResult(interp, "couldn't read directory \"",
300 Tcl_DStringValue(&dsOrig), "\": ",
301 Tcl_PosixError(interp), (char *) NULL);
302 if (baseLength > 0) {
303 (Tcl_DStringValue(&dsOrig))[baseLength-1] = savedChar;
305 Tcl_DStringFree(&dsOrig);
309 nativeDirLen = Tcl_DStringLength(&ds);
314 Tcl_DirEntry *entryPtr;
316 entryPtr = TclOSreaddir(d); /* INTL: Native. */
317 if (entryPtr == NULL) {
320 if (types != NULL && (types->perm & TCL_GLOB_PERM_HIDDEN)) {
322 * We explicitly asked for hidden files, so turn around
323 * and ignore any file which isn't hidden.
325 if (*entryPtr->d_name != '.') {
328 } else if (!matchHidden && (*entryPtr->d_name == '.')) {
330 * Don't match names starting with "." unless the "." is
331 * present in the pattern.
337 * Now check to see if the file matches, according to both type
338 * and pattern. If so, add the file to the result.
341 utf = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name, -1, &utfDs);
342 if (Tcl_StringMatch(utf, pattern) != 0) {
345 Tcl_DStringSetLength(&dsOrig, baseLength);
346 Tcl_DStringAppend(&dsOrig, utf, -1);
347 fname = Tcl_DStringValue(&dsOrig);
350 Tcl_DStringSetLength(&ds, nativeDirLen);
351 nativeEntry = Tcl_DStringAppend(&ds, entryPtr->d_name, -1);
352 typeOk = NativeMatchType(nativeEntry, types);
355 Tcl_ListObjAppendElement(interp, resultPtr,
356 Tcl_NewStringObj(fname, Tcl_DStringLength(&dsOrig)));
359 Tcl_DStringFree(&utfDs);
363 Tcl_DStringFree(&ds);
364 Tcl_DStringFree(&dsOrig);
370 CONST char* nativeEntry, /* Native path to check */
371 Tcl_GlobTypeData *types) /* Type description to match against */
376 * Simply check for the file's existence, but do it
377 * with lstat, in case it is a link to a file which
378 * doesn't exist (since that case would not show up
379 * if we used 'access' or 'stat')
381 if (TclOSlstat(nativeEntry, &buf) != 0) {
385 if (types->perm != 0) {
386 if (TclOSstat(nativeEntry, &buf) != 0) {
388 * Either the file has disappeared between the
389 * 'readdir' call and the 'stat' call, or
390 * the file is a link to a file which doesn't
391 * exist (which we could ascertain with
392 * lstat), or there is some other strange
393 * problem. In all these cases, we define this
394 * to mean the file does not match any defined
395 * permission, and therefore it is not
396 * added to the list of files to return.
402 * readonly means that there are NO write permissions
403 * (even for user), but execute is OK for anybody
405 if (((types->perm & TCL_GLOB_PERM_RONLY) &&
406 (buf.st_mode & (S_IWOTH|S_IWGRP|S_IWUSR))) ||
407 ((types->perm & TCL_GLOB_PERM_R) &&
408 (access(nativeEntry, R_OK) != 0)) ||
409 ((types->perm & TCL_GLOB_PERM_W) &&
410 (access(nativeEntry, W_OK) != 0)) ||
411 ((types->perm & TCL_GLOB_PERM_X) &&
412 (access(nativeEntry, X_OK) != 0))
417 if (types->type != 0) {
418 if (types->perm == 0) {
419 /* We haven't yet done a stat on the file */
420 if (TclOSstat(nativeEntry, &buf) != 0) {
421 /* Posix error occurred */
426 * In order bcdpfls as in 'find -t'
429 ((types->type & TCL_GLOB_TYPE_BLOCK) &&
430 S_ISBLK(buf.st_mode)) ||
431 ((types->type & TCL_GLOB_TYPE_CHAR) &&
432 S_ISCHR(buf.st_mode)) ||
433 ((types->type & TCL_GLOB_TYPE_DIR) &&
434 S_ISDIR(buf.st_mode)) ||
435 ((types->type & TCL_GLOB_TYPE_PIPE) &&
436 S_ISFIFO(buf.st_mode)) ||
437 ((types->type & TCL_GLOB_TYPE_FILE) &&
438 S_ISREG(buf.st_mode))
440 || ((types->type & TCL_GLOB_TYPE_SOCK) &&
441 S_ISSOCK(buf.st_mode))
442 #endif /* S_ISSOCK */
444 /* Do nothing -- this file is ok */
447 if (types->type & TCL_GLOB_TYPE_LINK) {
448 if (TclOSlstat(nativeEntry, &buf) == 0) {
449 if (S_ISLNK(buf.st_mode)) {
463 *---------------------------------------------------------------------------
467 * This function takes the specified user name and finds their
471 * The result is a pointer to a string specifying the user's home
472 * directory, or NULL if the user's home directory could not be
473 * determined. Storage for the result string is allocated in
474 * bufferPtr; the caller must call Tcl_DStringFree() when the result
475 * is no longer needed.
480 *----------------------------------------------------------------------
484 TclpGetUserHome(name, bufferPtr)
485 CONST char *name; /* User name for desired home directory. */
486 Tcl_DString *bufferPtr; /* Uninitialized or free DString filled
487 * with name of user's home directory. */
489 struct passwd *pwPtr;
493 native = Tcl_UtfToExternalDString(NULL, name, -1, &ds);
494 pwPtr = getpwnam(native); /* INTL: Native. */
495 Tcl_DStringFree(&ds);
501 Tcl_ExternalToUtfDString(NULL, pwPtr->pw_dir, -1, bufferPtr);
503 return Tcl_DStringValue(bufferPtr);
507 *---------------------------------------------------------------------------
511 * This function replaces the library version of access().
514 * See access() documentation.
517 * See access() documentation.
519 *---------------------------------------------------------------------------
523 TclpObjAccess(pathPtr, mode)
524 Tcl_Obj *pathPtr; /* Path of file to access */
525 int mode; /* Permission setting. */
527 CONST char *path = Tcl_FSGetNativePath(pathPtr);
531 return access(path, mode);
536 *---------------------------------------------------------------------------
540 * This function replaces the library version of chdir().
543 * See chdir() documentation.
546 * See chdir() documentation.
548 *---------------------------------------------------------------------------
552 TclpObjChdir(pathPtr)
553 Tcl_Obj *pathPtr; /* Path to new working directory */
555 CONST char *path = Tcl_FSGetNativePath(pathPtr);
564 *----------------------------------------------------------------------
568 * This function replaces the library version of lstat().
571 * See lstat() documentation.
574 * See lstat() documentation.
576 *----------------------------------------------------------------------
580 TclpObjLstat(pathPtr, bufPtr)
581 Tcl_Obj *pathPtr; /* Path of file to stat */
582 Tcl_StatBuf *bufPtr; /* Filled with results of stat call. */
584 return TclOSlstat(Tcl_FSGetNativePath(pathPtr), bufPtr);
588 *---------------------------------------------------------------------------
592 * This function replaces the library version of getcwd().
595 * The result is a pointer to a string specifying the current
596 * directory, or NULL if the current directory could not be
597 * determined. If NULL is returned, an error message is left in the
598 * interp's result. Storage for the result string is allocated in
599 * bufferPtr; the caller must call Tcl_DStringFree() when the result
600 * is no longer needed.
605 *----------------------------------------------------------------------
609 TclpObjGetCwd(interp)
613 if (TclpGetCwd(interp, &ds) != NULL) {
614 Tcl_Obj *cwdPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
615 Tcl_IncrRefCount(cwdPtr);
616 Tcl_DStringFree(&ds);
623 /* Older string based version */
625 TclpGetCwd(interp, bufferPtr)
626 Tcl_Interp *interp; /* If non-NULL, used for error reporting. */
627 Tcl_DString *bufferPtr; /* Uninitialized or free DString filled
628 * with name of current directory. */
630 char buffer[MAXPATHLEN+1];
633 if (getwd(buffer) == NULL) { /* INTL: Native. */
635 if (getcwd(buffer, MAXPATHLEN + 1) == NULL) { /* INTL: Native. */
637 if (interp != NULL) {
638 Tcl_AppendResult(interp,
639 "error getting working directory name: ",
640 Tcl_PosixError(interp), (char *) NULL);
644 return Tcl_ExternalToUtfDString(NULL, buffer, -1, bufferPtr);
648 *---------------------------------------------------------------------------
652 * This function replaces the library version of readlink().
655 * The result is a pointer to a string specifying the contents
656 * of the symbolic link given by 'path', or NULL if the symbolic
657 * link could not be read. Storage for the result string is
658 * allocated in bufferPtr; the caller must call Tcl_DStringFree()
659 * when the result is no longer needed.
662 * See readlink() documentation.
664 *---------------------------------------------------------------------------
668 TclpReadlink(path, linkPtr)
669 CONST char *path; /* Path of file to readlink (UTF-8). */
670 Tcl_DString *linkPtr; /* Uninitialized or free DString filled
671 * with contents of link (UTF-8). */
674 char link[MAXPATHLEN];
679 native = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
680 length = readlink(native, link, sizeof(link)); /* INTL: Native. */
681 Tcl_DStringFree(&ds);
687 Tcl_ExternalToUtfDString(NULL, link, length, linkPtr);
688 return Tcl_DStringValue(linkPtr);
695 *----------------------------------------------------------------------
699 * This function replaces the library version of stat().
702 * See stat() documentation.
705 * See stat() documentation.
707 *----------------------------------------------------------------------
711 TclpObjStat(pathPtr, bufPtr)
712 Tcl_Obj *pathPtr; /* Path of file to stat */
713 Tcl_StatBuf *bufPtr; /* Filled with results of stat call. */
715 CONST char *path = Tcl_FSGetNativePath(pathPtr);
719 return TclOSstat(path, bufPtr);
727 TclpObjLink(pathPtr, toPtr, linkAction)
733 CONST char *src = Tcl_FSGetNativePath(pathPtr);
734 CONST char *target = Tcl_FSGetNativePath(toPtr);
736 if (src == NULL || target == NULL) {
739 if (access(src, F_OK) != -1) {
744 if (access(target, F_OK) == -1) {
745 /* target doesn't exist */
750 * Check symbolic link flag first, since we prefer to
753 if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
754 if (symlink(target, src) != 0) return NULL;
755 } else if (linkAction & TCL_CREATE_HARD_LINK) {
756 if (link(target, src) != 0) return NULL;
763 Tcl_Obj* linkPtr = NULL;
765 char link[MAXPATHLEN];
769 if (Tcl_FSGetTranslatedPath(NULL, pathPtr) == NULL) {
772 length = readlink(Tcl_FSGetNativePath(pathPtr), link, sizeof(link));
777 Tcl_ExternalToUtfDString(NULL, link, length, &ds);
778 linkPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds),
779 Tcl_DStringLength(&ds));
780 Tcl_DStringFree(&ds);
781 if (linkPtr != NULL) {
782 Tcl_IncrRefCount(linkPtr);
792 *---------------------------------------------------------------------------
794 * TclpFilesystemPathType --
796 * This function is part of the native filesystem support, and
797 * returns the path type of the given path. Right now it simply
798 * returns NULL. In the future it could return specific path
799 * types, like 'nfs', 'samba', 'FAT32', etc.
807 *---------------------------------------------------------------------------
810 TclpFilesystemPathType(pathObjPtr)
813 /* All native paths are of the same type */