diff options
author | hobbs <hobbs> | 2002-08-08 08:56:18 (GMT) |
---|---|---|
committer | hobbs <hobbs> | 2002-08-08 08:56:18 (GMT) |
commit | 1d73c07b16f3304b87070edf4a7aee24a263402f (patch) | |
tree | aeaa59f60f701e1b111d28a3ca390205142aa573 /generic | |
parent | 1e8d54aee72071673f69d387e8130f02b46934a7 (diff) | |
download | tcl-1d73c07b16f3304b87070edf4a7aee24a263402f.zip tcl-1d73c07b16f3304b87070edf4a7aee24a263402f.tar.gz tcl-1d73c07b16f3304b87070edf4a7aee24a263402f.tar.bz2 |
* tests/fCmd.test:
* tests/unixFCmd.test: updated tests for new link copy behavior.
* generic/tclFCmd.c (CopyRenameOneFile): changed the behavior to
follow links to endpoints and copy that file/directory instead of
just copying the surface link. This means that trying to copy a
link that has no endpoint (danling link) is an error.
[Patch #591647] (darley)
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tclFCmd.c | 56 |
1 files changed, 53 insertions, 3 deletions
diff --git a/generic/tclFCmd.c b/generic/tclFCmd.c index e027977..743fe14 100644 --- a/generic/tclFCmd.c +++ b/generic/tclFCmd.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclFCmd.c,v 1.18 2002/07/18 16:17:48 vincentdarley Exp $ + * RCS: @(#) $Id: tclFCmd.c,v 1.19 2002/08/08 08:56:21 hobbs Exp $ */ #include "tclInt.h" @@ -448,6 +448,8 @@ CopyRenameOneFile(interp, source, target, copyFlag, force) { int result; Tcl_Obj *errfile, *errorBuffer; + /* If source is a link, then this is the real file/directory */ + Tcl_Obj *actualSource = NULL; Tcl_StatBuf sourceStatBuf, targetStatBuf; if (Tcl_FSConvertToPathType(interp, source) != TCL_OK) { @@ -549,8 +551,53 @@ CopyRenameOneFile(interp, source, target, copyFlag, force) */ } + actualSource = source; + Tcl_IncrRefCount(actualSource); +#ifdef S_ISLNK + /* + * To add a flag to make 'copy' copy links instead of files, we could + * add a condition to ignore this 'if' here. + */ + if (copyFlag && S_ISLNK(sourceStatBuf.st_mode)) { + /* + * We want to copy files not links. Therefore we must follow the + * link. There are two purposes to this 'stat' call here. First + * we want to know if the linked-file/dir actually exists, and + * second, in the block of code which follows, some 20 lines + * down, we want to check if the thing is a file or directory. + */ + if (Tcl_FSStat(source, &sourceStatBuf) != 0) { + /* Actual file doesn't exist */ + Tcl_AppendResult(interp, + "error copying \"", Tcl_GetString(source), + "\": the target of this link doesn't exist", + (char *) NULL); + goto done; + } else { + int counter = 0; + while (1) { + Tcl_Obj *path = Tcl_FSLink(actualSource,NULL,0); + if (path == NULL) { + break; + } + Tcl_DecrRefCount(actualSource); + actualSource = path; + counter++; + /* Arbitrary limit of 20 links to follow */ + if (counter > 20) { + /* Too many links */ + Tcl_SetErrno(EMLINK); + errfile = source; + goto done; + } + } + /* Now 'actualSource' is the correct file */ + } + } +#endif + if (S_ISDIR(sourceStatBuf.st_mode)) { - result = Tcl_FSCopyDirectory(source, target, &errorBuffer); + result = Tcl_FSCopyDirectory(actualSource, target, &errorBuffer); if (result != TCL_OK) { if (errno == EXDEV) { /* @@ -598,7 +645,7 @@ CopyRenameOneFile(interp, source, target, copyFlag, force) } } } else { - result = Tcl_FSCopyFile(source, target); + result = Tcl_FSCopyFile(actualSource, target); if ((result != TCL_OK) && (errno == EXDEV)) { result = TclCrossFilesystemCopy(interp, source, target); } @@ -652,6 +699,9 @@ CopyRenameOneFile(interp, source, target, copyFlag, force) if (errorBuffer != NULL) { Tcl_DecrRefCount(errorBuffer); } + if (actualSource != NULL) { + Tcl_DecrRefCount(actualSource); + } return result; } |