Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/unix/tclUnixFile.c @ 52

Last change on this file since 52 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 28.1 KB
Line 
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 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclUnixFile.c,v 1.52 2007/12/13 15:28:42 dgp Exp $
13 */
14
15#include "tclInt.h"
16#include "tclFileSystem.h"
17
18static int NativeMatchType(Tcl_Interp *interp, CONST char* nativeEntry,
19        CONST char* nativeName, Tcl_GlobTypeData *types);
20
21/*
22 *---------------------------------------------------------------------------
23 *
24 * TclpFindExecutable --
25 *
26 *      This function computes the absolute path name of the current
27 *      application, given its argv[0] value.
28 *
29 * Results:
30 *      None.
31 *
32 * Side effects:
33 *      The computed path name is stored as a ProcessGlobalValue.
34 *
35 *---------------------------------------------------------------------------
36 */
37
38void
39TclpFindExecutable(
40    CONST char *argv0)          /* The value of the application's argv[0]
41                                 * (native). */
42{
43    CONST char *name, *p;
44    Tcl_StatBuf statBuf;
45    Tcl_DString buffer, nameString, cwd, utfName;
46    Tcl_Encoding encoding;
47
48    if (argv0 == NULL) {
49        return;
50    }
51    Tcl_DStringInit(&buffer);
52
53    name = argv0;
54    for (p = name; *p != '\0'; p++) {
55        if (*p == '/') {
56            /*
57             * The name contains a slash, so use the name directly without
58             * doing a path search.
59             */
60
61            goto gotName;
62        }
63    }
64
65    p = getenv("PATH");                                 /* INTL: Native. */
66    if (p == NULL) {
67        /*
68         * There's no PATH environment variable; use the default that is used
69         * by sh.
70         */
71
72        p = ":/bin:/usr/bin";
73    } else if (*p == '\0') {
74        /*
75         * An empty path is equivalent to ".".
76         */
77
78        p = "./";
79    }
80
81    /*
82     * Search through all the directories named in the PATH variable to see if
83     * argv[0] is in one of them. If so, use that file name.
84     */
85
86    while (1) {
87        while (isspace(UCHAR(*p))) {                    /* INTL: BUG */
88            p++;
89        }
90        name = p;
91        while ((*p != ':') && (*p != 0)) {
92            p++;
93        }
94        Tcl_DStringSetLength(&buffer, 0);
95        if (p != name) {
96            Tcl_DStringAppend(&buffer, name, p - name);
97            if (p[-1] != '/') {
98                Tcl_DStringAppend(&buffer, "/", 1);
99            }
100        }
101        name = Tcl_DStringAppend(&buffer, argv0, -1);
102
103        /*
104         * INTL: The following calls to access() and stat() should not be
105         * converted to Tclp routines because they need to operate on native
106         * strings directly.
107         */
108
109        if ((access(name, X_OK) == 0)                   /* INTL: Native. */
110                && (TclOSstat(name, &statBuf) == 0)     /* INTL: Native. */
111                && S_ISREG(statBuf.st_mode)) {
112            goto gotName;
113        }
114        if (*p == '\0') {
115            break;
116        } else if (*(p+1) == 0) {
117            p = "./";
118        } else {
119            p++;
120        }
121    }
122    TclSetObjNameOfExecutable(Tcl_NewObj(), NULL);
123    goto done;
124
125    /*
126     * If the name starts with "/" then just store it
127     */
128
129  gotName:
130#ifdef DJGPP
131    if (name[1] == ':')
132#else
133    if (name[0] == '/')
134#endif
135    {
136        encoding = Tcl_GetEncoding(NULL, NULL);
137        Tcl_ExternalToUtfDString(encoding, name, -1, &utfName);
138        TclSetObjNameOfExecutable(
139                Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding);
140        Tcl_DStringFree(&utfName);
141        goto done;
142    }
143
144    /*
145     * The name is relative to the current working directory. First strip off
146     * a leading "./", if any, then add the full path name of the current
147     * working directory.
148     */
149
150    if ((name[0] == '.') && (name[1] == '/')) {
151        name += 2;
152    }
153
154    Tcl_DStringInit(&nameString);
155    Tcl_DStringAppend(&nameString, name, -1);
156
157    TclpGetCwd(NULL, &cwd);
158
159    Tcl_DStringFree(&buffer);
160    Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&cwd),
161            Tcl_DStringLength(&cwd), &buffer);
162    if (Tcl_DStringValue(&cwd)[Tcl_DStringLength(&cwd) -1] != '/') {
163        Tcl_DStringAppend(&buffer, "/", 1);
164    }
165    Tcl_DStringFree(&cwd);
166    Tcl_DStringAppend(&buffer, Tcl_DStringValue(&nameString),
167            Tcl_DStringLength(&nameString));
168    Tcl_DStringFree(&nameString);
169
170    encoding = Tcl_GetEncoding(NULL, NULL);
171    Tcl_ExternalToUtfDString(encoding, Tcl_DStringValue(&buffer), -1,
172            &utfName);
173    TclSetObjNameOfExecutable(
174            Tcl_NewStringObj(Tcl_DStringValue(&utfName), -1), encoding);
175    Tcl_DStringFree(&utfName);
176
177  done:
178    Tcl_DStringFree(&buffer);
179}
180
181/*
182 *----------------------------------------------------------------------
183 *
184 * TclpMatchInDirectory --
185 *
186 *      This routine is used by the globbing code to search a directory for
187 *      all files which match a given pattern.
188 *
189 * Results:
190 *      The return value is a standard Tcl result indicating whether an error
191 *      occurred in globbing. Errors are left in interp, good results are
192 *      [lappend]ed to resultPtr (which must be a valid object).
193 *
194 * Side effects:
195 *      None.
196 *
197 *----------------------------------------------------------------------
198 */
199
200int
201TclpMatchInDirectory(
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. */
209{
210    CONST char *native;
211    Tcl_Obj *fileNamePtr;
212    int matchResult = 0;
213
214    if (types != NULL && types->type == TCL_GLOB_TYPE_MOUNT) {
215        /*
216         * The native filesystem never adds mounts.
217         */
218
219        return TCL_OK;
220    }
221
222    fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
223    if (fileNamePtr == NULL) {
224        return TCL_ERROR;
225    }
226
227    if (pattern == NULL || (*pattern == '\0')) {
228        /*
229         * Match a file directly.
230         */
231        Tcl_Obj *tailPtr;
232        CONST char *nativeTail;
233
234        native = (CONST char*) Tcl_FSGetNativePath(pathPtr);
235        tailPtr = TclPathPart(interp, pathPtr, TCL_PATH_TAIL);
236        nativeTail = (CONST char*) Tcl_FSGetNativePath(tailPtr);
237        matchResult = NativeMatchType(interp, native, nativeTail, types);
238        if (matchResult == 1) {
239            Tcl_ListObjAppendElement(interp, resultPtr, pathPtr);
240        }
241        Tcl_DecrRefCount(tailPtr);
242        Tcl_DecrRefCount(fileNamePtr);
243    } else {
244        DIR *d;
245        Tcl_DirEntry *entryPtr;
246        CONST char *dirName;
247        int dirLength;
248        int matchHidden, matchHiddenPat;
249        int nativeDirLen;
250        Tcl_StatBuf statBuf;
251        Tcl_DString ds;         /* native encoding of dir */
252        Tcl_DString dsOrig;     /* utf-8 encoding of dir */
253
254        Tcl_DStringInit(&dsOrig);
255        dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength);
256        Tcl_DStringAppend(&dsOrig, dirName, dirLength);
257
258        /*
259         * Make sure that the directory part of the name really is a
260         * directory.  If the directory name is "", use the name "." instead,
261         * because some UNIX systems don't treat "" like "." automatically.
262         * Keep the "" for use in generating file names, otherwise "glob
263         * foo.c" would return "./foo.c".
264         */
265
266        if (dirLength == 0) {
267            dirName = ".";
268        } else {
269            dirName = Tcl_DStringValue(&dsOrig);
270
271            /*
272             * Make sure we have a trailing directory delimiter.
273             */
274
275            if (dirName[dirLength-1] != '/') {
276                dirName = Tcl_DStringAppend(&dsOrig, "/", 1);
277                dirLength++;
278            }
279        }
280
281        /*
282         * Now open the directory for reading and iterate over the contents.
283         */
284
285        native = Tcl_UtfToExternalDString(NULL, dirName, -1, &ds);
286
287        if ((TclOSstat(native, &statBuf) != 0)          /* INTL: Native. */
288                || !S_ISDIR(statBuf.st_mode)) {
289            Tcl_DStringFree(&dsOrig);
290            Tcl_DStringFree(&ds);
291            Tcl_DecrRefCount(fileNamePtr);
292            return TCL_OK;
293        }
294
295        d = opendir(native);                            /* INTL: Native. */
296        if (d == NULL) {
297            Tcl_DStringFree(&ds);
298            if (interp != NULL) {
299                Tcl_ResetResult(interp);
300                Tcl_AppendResult(interp, "couldn't read directory \"",
301                        Tcl_DStringValue(&dsOrig), "\": ",
302                        Tcl_PosixError(interp), (char *) NULL);
303            }
304            Tcl_DStringFree(&dsOrig);
305            Tcl_DecrRefCount(fileNamePtr);
306            return TCL_ERROR;
307        }
308
309        nativeDirLen = Tcl_DStringLength(&ds);
310
311        /*
312         * Check to see if -type or the pattern requests hidden files.
313         */
314
315        matchHiddenPat = (pattern[0] == '.')
316                || ((pattern[0] == '\\') && (pattern[1] == '.'));
317        matchHidden = matchHiddenPat
318                || (types && (types->perm & TCL_GLOB_PERM_HIDDEN));
319        while ((entryPtr = TclOSreaddir(d)) != NULL) {  /* INTL: Native. */
320            Tcl_DString utfDs;
321            CONST char *utfname;
322
323            /*
324             * Skip this file if it doesn't agree with the hidden parameters
325             * requested by the user (via -type or pattern).
326             */
327
328            if (*entryPtr->d_name == '.') {
329                if (!matchHidden) continue;
330            } else {
331#ifdef MAC_OSX_TCL
332                if (matchHiddenPat) continue;
333                /* Also need to check HFS hidden flag in TclMacOSXMatchType. */
334#else
335                if (matchHidden) continue;
336#endif
337            }
338
339            /*
340             * Now check to see if the file matches, according to both type
341             * and pattern. If so, add the file to the result.
342             */
343
344            utfname = Tcl_ExternalToUtfDString(NULL, entryPtr->d_name, -1,
345                    &utfDs);
346            if (Tcl_StringCaseMatch(utfname, pattern, 0)) {
347                int typeOk = 1;
348
349                if (types != NULL) {
350                    Tcl_DStringSetLength(&ds, nativeDirLen);
351                    native = Tcl_DStringAppend(&ds, entryPtr->d_name, -1);
352                    matchResult = NativeMatchType(interp, native,
353                            entryPtr->d_name, types);
354                    typeOk = (matchResult == 1);
355                }
356                if (typeOk) {
357                    Tcl_ListObjAppendElement(interp, resultPtr,
358                            TclNewFSPathObj(pathPtr, utfname,
359                            Tcl_DStringLength(&utfDs)));
360                }
361            }
362            Tcl_DStringFree(&utfDs);
363            if (matchResult < 0) {
364                break;
365            }
366        }
367
368        closedir(d);
369        Tcl_DStringFree(&ds);
370        Tcl_DStringFree(&dsOrig);
371        Tcl_DecrRefCount(fileNamePtr);
372    }
373    if (matchResult < 0) {
374        return TCL_ERROR;
375    } else {
376        return TCL_OK;
377    }
378}
379
380/*
381 *----------------------------------------------------------------------
382 *
383 * NativeMatchType --
384 *
385 *      This routine is used by the globbing code to check if a file
386 *      matches a given type description.
387 *
388 * Results:
389 *      The return value is 1, 0 or -1 indicating whether the file
390 *      matches the given criteria, does not match them, or an error
391 *      occurred (in wich case an error is left in interp).
392 *
393 * Side effects:
394 *      None.
395 *
396 *----------------------------------------------------------------------
397 */
398
399static int
400NativeMatchType(
401    Tcl_Interp *interp,       /* Interpreter to receive errors. */
402    CONST char *nativeEntry,  /* Native path to check. */
403    CONST char *nativeName,   /* Native filename to check. */
404    Tcl_GlobTypeData *types)  /* Type description to match against. */
405{
406    Tcl_StatBuf buf;
407    if (types == NULL) {
408        /*
409         * Simply check for the file's existence, but do it with lstat, in
410         * case it is a link to a file which doesn't exist (since that case
411         * would not show up if we used 'access' or 'stat')
412         */
413
414        if (TclOSlstat(nativeEntry, &buf) != 0) {
415            return 0;
416        }
417    } else {
418        if (types->perm != 0) {
419            if (TclOSstat(nativeEntry, &buf) != 0) {
420                /*
421                 * Either the file has disappeared between the 'readdir' call
422                 * and the 'stat' call, or the file is a link to a file which
423                 * doesn't exist (which we could ascertain with lstat), or
424                 * there is some other strange problem. In all these cases, we
425                 * define this to mean the file does not match any defined
426                 * permission, and therefore it is not added to the list of
427                 * files to return.
428                 */
429
430                return 0;
431            }
432
433            /*
434             * readonly means that there are NO write permissions (even for
435             * user), but execute is OK for anybody OR that the user immutable
436             * flag is set (where supported).
437             */
438
439            if (((types->perm & TCL_GLOB_PERM_RONLY) &&
440#if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
441                        !(buf.st_flags & UF_IMMUTABLE) &&
442#endif
443                        (buf.st_mode & (S_IWOTH|S_IWGRP|S_IWUSR))) ||
444                ((types->perm & TCL_GLOB_PERM_R) &&
445                        (access(nativeEntry, R_OK) != 0)) ||
446                ((types->perm & TCL_GLOB_PERM_W) &&
447                        (access(nativeEntry, W_OK) != 0)) ||
448                ((types->perm & TCL_GLOB_PERM_X) &&
449                        (access(nativeEntry, X_OK) != 0))
450#ifndef MAC_OSX_TCL
451                || ((types->perm & TCL_GLOB_PERM_HIDDEN) &&
452                        (*nativeName != '.'))
453#endif
454                ) {
455                return 0;
456            }
457        }
458        if (types->type != 0) {
459            if (types->perm == 0) {
460                /*
461                 * We haven't yet done a stat on the file.
462                 */
463
464                if (TclOSstat(nativeEntry, &buf) != 0) {
465                    /*
466                     * Posix error occurred. The only ok case is if this is a
467                     * link to a nonexistent file, and the user did 'glob -l'.
468                     * So we check that here:
469                     */
470
471                    if (types->type & TCL_GLOB_TYPE_LINK) {
472                        if (TclOSlstat(nativeEntry, &buf) == 0) {
473                            if (S_ISLNK(buf.st_mode)) {
474                                return 1;
475                            }
476                        }
477                    }
478                    return 0;
479                }
480            }
481
482            /*
483             * In order bcdpfls as in 'find -t'
484             */
485
486            if (((types->type & TCL_GLOB_TYPE_BLOCK)&& S_ISBLK(buf.st_mode)) ||
487                ((types->type & TCL_GLOB_TYPE_CHAR) && S_ISCHR(buf.st_mode)) ||
488                ((types->type & TCL_GLOB_TYPE_DIR)  && S_ISDIR(buf.st_mode)) ||
489                ((types->type & TCL_GLOB_TYPE_PIPE) && S_ISFIFO(buf.st_mode))||
490                ((types->type & TCL_GLOB_TYPE_FILE) && S_ISREG(buf.st_mode))
491#ifdef S_ISSOCK
492                ||((types->type & TCL_GLOB_TYPE_SOCK) && S_ISSOCK(buf.st_mode))
493#endif /* S_ISSOCK */
494                ) {
495                /*
496                 * Do nothing - this file is ok.
497                 */
498            } else {
499#ifdef S_ISLNK
500                if (types->type & TCL_GLOB_TYPE_LINK) {
501                    if (TclOSlstat(nativeEntry, &buf) == 0) {
502                        if (S_ISLNK(buf.st_mode)) {
503                            goto filetypeOK;
504                        }
505                    }
506                }
507#endif /* S_ISLNK */
508                return 0;
509            }
510        }
511    filetypeOK: ;
512#ifdef MAC_OSX_TCL
513        if (types->macType != NULL || types->macCreator != NULL ||
514                (types->perm & TCL_GLOB_PERM_HIDDEN)) {
515            int matchResult;
516
517            if (types->perm == 0 && types->type == 0) {
518                /*
519                 * We haven't yet done a stat on the file.
520                 */
521
522                if (TclOSstat(nativeEntry, &buf) != 0) {
523                    return 0;
524                }
525            }
526
527            matchResult = TclMacOSXMatchType(interp, nativeEntry, nativeName,
528                    &buf, types);
529            if (matchResult != 1) {
530                return matchResult;
531            }
532        }
533#endif
534    }
535    return 1;
536}
537
538/*
539 *---------------------------------------------------------------------------
540 *
541 * TclpGetUserHome --
542 *
543 *      This function takes the specified user name and finds their home
544 *      directory.
545 *
546 * Results:
547 *      The result is a pointer to a string specifying the user's home
548 *      directory, or NULL if the user's home directory could not be
549 *      determined. Storage for the result string is allocated in bufferPtr;
550 *      the caller must call Tcl_DStringFree() when the result is no longer
551 *      needed.
552 *
553 * Side effects:
554 *      None.
555 *
556 *----------------------------------------------------------------------
557 */
558
559char *
560TclpGetUserHome(
561    CONST char *name,           /* User name for desired home directory. */
562    Tcl_DString *bufferPtr)     /* Uninitialized or free DString filled with
563                                 * name of user's home directory. */
564{
565    struct passwd *pwPtr;
566    Tcl_DString ds;
567    CONST char *native;
568
569    native = Tcl_UtfToExternalDString(NULL, name, -1, &ds);
570    pwPtr = getpwnam(native);                           /* INTL: Native. */
571    Tcl_DStringFree(&ds);
572
573    if (pwPtr == NULL) {
574        endpwent();
575        return NULL;
576    }
577    Tcl_ExternalToUtfDString(NULL, pwPtr->pw_dir, -1, bufferPtr);
578    endpwent();
579    return Tcl_DStringValue(bufferPtr);
580}
581
582/*
583 *---------------------------------------------------------------------------
584 *
585 * TclpObjAccess --
586 *
587 *      This function replaces the library version of access().
588 *
589 * Results:
590 *      See access() documentation.
591 *
592 * Side effects:
593 *      See access() documentation.
594 *
595 *---------------------------------------------------------------------------
596 */
597
598int
599TclpObjAccess(
600    Tcl_Obj *pathPtr,           /* Path of file to access */
601    int mode)                   /* Permission setting. */
602{
603    CONST char *path = Tcl_FSGetNativePath(pathPtr);
604    if (path == NULL) {
605        return -1;
606    } else {
607        return access(path, mode);
608    }
609}
610
611/*
612 *---------------------------------------------------------------------------
613 *
614 * TclpObjChdir --
615 *
616 *      This function replaces the library version of chdir().
617 *
618 * Results:
619 *      See chdir() documentation.
620 *
621 * Side effects:
622 *      See chdir() documentation.
623 *
624 *---------------------------------------------------------------------------
625 */
626
627int
628TclpObjChdir(
629    Tcl_Obj *pathPtr)           /* Path to new working directory */
630{
631    CONST char *path = Tcl_FSGetNativePath(pathPtr);
632    if (path == NULL) {
633        return -1;
634    } else {
635        return chdir(path);
636    }
637}
638
639/*
640 *----------------------------------------------------------------------
641 *
642 * TclpObjLstat --
643 *
644 *      This function replaces the library version of lstat().
645 *
646 * Results:
647 *      See lstat() documentation.
648 *
649 * Side effects:
650 *      See lstat() documentation.
651 *
652 *----------------------------------------------------------------------
653 */
654
655int
656TclpObjLstat(
657    Tcl_Obj *pathPtr,           /* Path of file to stat */
658    Tcl_StatBuf *bufPtr)        /* Filled with results of stat call. */
659{
660    return TclOSlstat(Tcl_FSGetNativePath(pathPtr), bufPtr);
661}
662
663/*
664 *---------------------------------------------------------------------------
665 *
666 * TclpGetNativeCwd --
667 *
668 *      This function replaces the library version of getcwd().
669 *
670 * Results:
671 *      The input and output are filesystem paths in native form. The result
672 *      is either the given clientData, if the working directory hasn't
673 *      changed, or a new clientData (owned by our caller), giving the new
674 *      native path, or NULL if the current directory could not be determined.
675 *      If NULL is returned, the caller can examine the standard posix error
676 *      codes to determine the cause of the problem.
677 *
678 * Side effects:
679 *      None.
680 *
681 *----------------------------------------------------------------------
682 */
683
684ClientData
685TclpGetNativeCwd(
686    ClientData clientData)
687{
688    char buffer[MAXPATHLEN+1];
689
690#ifdef USEGETWD
691    if (getwd(buffer) == NULL)                          /* INTL: Native. */
692#else
693    if (getcwd(buffer, MAXPATHLEN+1) == NULL)           /* INTL: Native. */
694#endif
695    {
696        return NULL;
697    }
698    if ((clientData != NULL) && strcmp(buffer, (CONST char*)clientData) == 0) {
699        /*
700         * No change to pwd.
701         */
702
703        return clientData;
704    } else {
705        char *newCd = (char *) ckalloc((unsigned) (strlen(buffer) + 1));
706        strcpy(newCd, buffer);
707        return (ClientData) newCd;
708    }
709}
710
711/*
712 *---------------------------------------------------------------------------
713 *
714 * TclpGetCwd --
715 *
716 *      This function replaces the library version of getcwd(). (Obsolete
717 *      function, only retained for old extensions which may call it
718 *      directly).
719 *
720 * Results:
721 *      The result is a pointer to a string specifying the current directory,
722 *      or NULL if the current directory could not be determined. If NULL is
723 *      returned, an error message is left in the interp's result. Storage for
724 *      the result string is allocated in bufferPtr; the caller must call
725 *      Tcl_DStringFree() when the result is no longer needed.
726 *
727 * Side effects:
728 *      None.
729 *
730 *----------------------------------------------------------------------
731 */
732
733CONST char *
734TclpGetCwd(
735    Tcl_Interp *interp,         /* If non-NULL, used for error reporting. */
736    Tcl_DString *bufferPtr)     /* Uninitialized or free DString filled with
737                                 * name of current directory. */
738{
739    char buffer[MAXPATHLEN+1];
740
741#ifdef USEGETWD
742    if (getwd(buffer) == NULL)                          /* INTL: Native. */
743#else
744    if (getcwd(buffer, MAXPATHLEN+1) == NULL)           /* INTL: Native. */
745#endif
746    {
747        if (interp != NULL) {
748            Tcl_AppendResult(interp,
749                    "error getting working directory name: ",
750                    Tcl_PosixError(interp), NULL);
751        }
752        return NULL;
753    }
754    return Tcl_ExternalToUtfDString(NULL, buffer, -1, bufferPtr);
755}
756
757/*
758 *---------------------------------------------------------------------------
759 *
760 * TclpReadlink --
761 *
762 *      This function replaces the library version of readlink().
763 *
764 * Results:
765 *      The result is a pointer to a string specifying the contents of the
766 *      symbolic link given by 'path', or NULL if the symbolic link could not
767 *      be read. Storage for the result string is allocated in bufferPtr; the
768 *      caller must call Tcl_DStringFree() when the result is no longer
769 *      needed.
770 *
771 * Side effects:
772 *      See readlink() documentation.
773 *
774 *---------------------------------------------------------------------------
775 */
776
777char *
778TclpReadlink(
779    CONST char *path,           /* Path of file to readlink (UTF-8). */
780    Tcl_DString *linkPtr)       /* Uninitialized or free DString filled with
781                                 * contents of link (UTF-8). */
782{
783#ifndef DJGPP
784    char link[MAXPATHLEN];
785    int length;
786    CONST char *native;
787    Tcl_DString ds;
788
789    native = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
790    length = readlink(native, link, sizeof(link));      /* INTL: Native. */
791    Tcl_DStringFree(&ds);
792
793    if (length < 0) {
794        return NULL;
795    }
796
797    Tcl_ExternalToUtfDString(NULL, link, length, linkPtr);
798    return Tcl_DStringValue(linkPtr);
799#else
800    return NULL;
801#endif
802}
803
804/*
805 *----------------------------------------------------------------------
806 *
807 * TclpObjStat --
808 *
809 *      This function replaces the library version of stat().
810 *
811 * Results:
812 *      See stat() documentation.
813 *
814 * Side effects:
815 *      See stat() documentation.
816 *
817 *----------------------------------------------------------------------
818 */
819
820int
821TclpObjStat(
822    Tcl_Obj *pathPtr,           /* Path of file to stat */
823    Tcl_StatBuf *bufPtr)        /* Filled with results of stat call. */
824{
825    CONST char *path = Tcl_FSGetNativePath(pathPtr);
826    if (path == NULL) {
827        return -1;
828    } else {
829        return TclOSstat(path, bufPtr);
830    }
831}
832
833#ifdef S_IFLNK
834
835Tcl_Obj*
836TclpObjLink(
837    Tcl_Obj *pathPtr,
838    Tcl_Obj *toPtr,
839    int linkAction)
840{
841    if (toPtr != NULL) {
842        CONST char *src = Tcl_FSGetNativePath(pathPtr);
843        CONST char *target = NULL;
844
845        if (src == NULL) {
846            return NULL;
847        }
848
849        /*
850         * If we're making a symbolic link and the path is relative, then we
851         * must check whether it exists _relative_ to the directory in which
852         * the src is found (not relative to the current cwd which is just not
853         * relevant in this case).
854         *
855         * If we're making a hard link, then a relative path is just converted
856         * to absolute relative to the cwd.
857         */
858
859        if ((linkAction & TCL_CREATE_SYMBOLIC_LINK)
860                && (Tcl_FSGetPathType(toPtr) == TCL_PATH_RELATIVE)) {
861            Tcl_Obj *dirPtr, *absPtr;
862
863            dirPtr = TclPathPart(NULL, pathPtr, TCL_PATH_DIRNAME);
864            if (dirPtr == NULL) {
865                return NULL;
866            }
867            absPtr = Tcl_FSJoinToPath(dirPtr, 1, &toPtr);
868            Tcl_IncrRefCount(absPtr);
869            if (Tcl_FSAccess(absPtr, F_OK) == -1) {
870                Tcl_DecrRefCount(absPtr);
871                Tcl_DecrRefCount(dirPtr);
872
873                /*
874                 * Target doesn't exist.
875                 */
876
877                errno = ENOENT;
878                return NULL;
879            }
880
881            /*
882             * Target exists; we'll construct the relative path we want below.
883             */
884
885            Tcl_DecrRefCount(absPtr);
886            Tcl_DecrRefCount(dirPtr);
887        } else {
888            target = Tcl_FSGetNativePath(toPtr);
889            if (target == NULL) {
890                return NULL;
891            }
892            if (access(target, F_OK) == -1) {
893                /*
894                 * Target doesn't exist.
895                 */
896
897                errno = ENOENT;
898                return NULL;
899            }
900        }
901
902        if (access(src, F_OK) != -1) {
903            /*
904             * Src exists.
905             */
906
907            errno = EEXIST;
908            return NULL;
909        }
910
911        /*
912         * Check symbolic link flag first, since we prefer to create these.
913         */
914
915        if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
916            int targetLen;
917            Tcl_DString ds;
918            Tcl_Obj *transPtr;
919
920            /*
921             * Now we don't want to link to the absolute, normalized path.
922             * Relative links are quite acceptable (but links to ~user are not
923             * -- these must be expanded first).
924             */
925
926            transPtr = Tcl_FSGetTranslatedPath(NULL, toPtr);
927            if (transPtr == NULL) {
928                return NULL;
929            }
930            target = Tcl_GetStringFromObj(transPtr, &targetLen);
931            target = Tcl_UtfToExternalDString(NULL, target, targetLen, &ds);
932            Tcl_DecrRefCount(transPtr);
933
934            if (symlink(target, src) != 0) {
935                toPtr = NULL;
936            }
937            Tcl_DStringFree(&ds);
938        } else if (linkAction & TCL_CREATE_HARD_LINK) {
939            if (link(target, src) != 0) {
940                return NULL;
941            }
942        } else {
943            errno = ENODEV;
944            return NULL;
945        }
946        return toPtr;
947    } else {
948        Tcl_Obj *linkPtr = NULL;
949
950        char link[MAXPATHLEN];
951        int length;
952        Tcl_DString ds;
953        Tcl_Obj *transPtr;
954
955        transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
956        if (transPtr == NULL) {
957            return NULL;
958        }
959        Tcl_DecrRefCount(transPtr);
960
961        length = readlink(Tcl_FSGetNativePath(pathPtr), link, sizeof(link));
962        if (length < 0) {
963            return NULL;
964        }
965
966        Tcl_ExternalToUtfDString(NULL, link, length, &ds);
967        linkPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds),
968                Tcl_DStringLength(&ds));
969        Tcl_DStringFree(&ds);
970        if (linkPtr != NULL) {
971            Tcl_IncrRefCount(linkPtr);
972        }
973        return linkPtr;
974    }
975}
976#endif /* S_IFLNK */
977
978/*
979 *---------------------------------------------------------------------------
980 *
981 * TclpFilesystemPathType --
982 *
983 *      This function is part of the native filesystem support, and returns
984 *      the path type of the given path. Right now it simply returns NULL. In
985 *      the future it could return specific path types, like 'nfs', 'samba',
986 *      'FAT32', etc.
987 *
988 * Results:
989 *      NULL at present.
990 *
991 * Side effects:
992 *      None.
993 *
994 *---------------------------------------------------------------------------
995 */
996
997Tcl_Obj *
998TclpFilesystemPathType(
999    Tcl_Obj *pathPtr)
1000{
1001    /*
1002     * All native paths are of the same type.
1003     */
1004
1005    return NULL;
1006}
1007
1008/*
1009 *---------------------------------------------------------------------------
1010 *
1011 * TclpNativeToNormalized --
1012 *
1013 *      Convert native format to a normalized path object, with refCount of
1014 *      zero.
1015 *
1016 *      Currently assumes all native paths are actually normalized already, so
1017 *      if the path given is not normalized this will actually just convert to
1018 *      a valid string path, but not necessarily a normalized one.
1019 *
1020 * Results:
1021 *      A valid normalized path.
1022 *
1023 * Side effects:
1024 *      None.
1025 *
1026 *---------------------------------------------------------------------------
1027 */
1028
1029Tcl_Obj *
1030TclpNativeToNormalized(
1031    ClientData clientData)
1032{
1033    Tcl_DString ds;
1034    Tcl_Obj *objPtr;
1035    int len;
1036
1037    CONST char *copy;
1038    Tcl_ExternalToUtfDString(NULL, (CONST char*)clientData, -1, &ds);
1039
1040    copy = Tcl_DStringValue(&ds);
1041    len = Tcl_DStringLength(&ds);
1042
1043    objPtr = Tcl_NewStringObj(copy,len);
1044    Tcl_DStringFree(&ds);
1045
1046    return objPtr;
1047}
1048
1049/*
1050 *---------------------------------------------------------------------------
1051 *
1052 * TclNativeCreateNativeRep --
1053 *
1054 *      Create a native representation for the given path.
1055 *
1056 * Results:
1057 *      The nativePath representation.
1058 *
1059 * Side effects:
1060 *      Memory will be allocated. The path may need to be normalized.
1061 *
1062 *---------------------------------------------------------------------------
1063 */
1064
1065ClientData
1066TclNativeCreateNativeRep(
1067    Tcl_Obj *pathPtr)
1068{
1069    char *nativePathPtr;
1070    Tcl_DString ds;
1071    Tcl_Obj *validPathPtr;
1072    int len;
1073    char *str;
1074
1075    if (TclFSCwdIsNative()) {
1076        /*
1077         * The cwd is native, which means we can use the translated path
1078         * without worrying about normalization (this will also usually be
1079         * shorter so the utf-to-external conversion will be somewhat faster).
1080         */
1081
1082        validPathPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
1083        if (validPathPtr == NULL) {
1084            return NULL;
1085        }
1086    } else {
1087        /*
1088         * Make sure the normalized path is set.
1089         */
1090
1091        validPathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
1092        if (validPathPtr == NULL) {
1093            return NULL;
1094        }
1095        Tcl_IncrRefCount(validPathPtr);
1096    }
1097
1098    str = Tcl_GetStringFromObj(validPathPtr, &len);
1099    Tcl_UtfToExternalDString(NULL, str, len, &ds);
1100    len = Tcl_DStringLength(&ds) + sizeof(char);
1101    Tcl_DecrRefCount(validPathPtr);
1102    nativePathPtr = ckalloc((unsigned) len);
1103    memcpy((void*)nativePathPtr, (void*)Tcl_DStringValue(&ds), (size_t) len);
1104
1105    Tcl_DStringFree(&ds);
1106    return (ClientData)nativePathPtr;
1107}
1108
1109/*
1110 *---------------------------------------------------------------------------
1111 *
1112 * TclNativeDupInternalRep --
1113 *
1114 *      Duplicate the native representation.
1115 *
1116 * Results:
1117 *      The copied native representation, or NULL if it is not possible to
1118 *      copy the representation.
1119 *
1120 * Side effects:
1121 *      Memory will be allocated for the copy.
1122 *
1123 *---------------------------------------------------------------------------
1124 */
1125
1126ClientData
1127TclNativeDupInternalRep(
1128    ClientData clientData)
1129{
1130    char *copy;
1131    size_t len;
1132
1133    if (clientData == NULL) {
1134        return NULL;
1135    }
1136
1137    /*
1138     * ASCII representation when running on Unix.
1139     */
1140
1141    len = sizeof(char) + (strlen((CONST char*) clientData) * sizeof(char));
1142
1143    copy = (char *) ckalloc(len);
1144    memcpy((void *) copy, (void *) clientData, len);
1145    return (ClientData)copy;
1146}
1147
1148/*
1149 *---------------------------------------------------------------------------
1150 *
1151 * TclpUtime --
1152 *
1153 *      Set the modification date for a file.
1154 *
1155 * Results:
1156 *      0 on success, -1 on error.
1157 *
1158 * Side effects:
1159 *      None.
1160 *
1161 *---------------------------------------------------------------------------
1162 */
1163
1164int
1165TclpUtime(
1166    Tcl_Obj *pathPtr,           /* File to modify */
1167    struct utimbuf *tval)       /* New modification date structure */
1168{
1169    return utime(Tcl_FSGetNativePath(pathPtr), tval);
1170}
1171
1172/*
1173 * Local Variables:
1174 * mode: c
1175 * c-basic-offset: 4
1176 * fill-column: 78
1177 * End:
1178 */
Note: See TracBrowser for help on using the repository browser.