$ perl -v | head -2
$Header: perly.c,v 3.0.1.5 90/03/27 16:20:57 lwall Locked $
Patch level: 18
$ ls
$ echo >file1
$ ls
file1
$ perl -e 'rename ("file1", "file2")'
$ ls
file2
$ perl -e 'rename ("file2", "file2")'
$ ls
$
The rename emulation first unlinks the destination, but this is a Bad Thing
if the source and dest are the same.
--
Brian L. Matthews b...@6sceng.UUCP
Naaah.
In Perl:
sub safe_rename {
local($f1,$f2) = @_;
@f1 = stat($f1);
return undef unless @f1; # $! is set already, I hope
@f2 = stat($f2);
return rename($f1,$f2) unless @f2;
# both files exist... lessee if they are the same:
if ((shift(@f1) == shift(@f2)) && (shift(@f1) == shift(@f2))) {
# success... same device/inode
$! = 0;
return 1;
}
rename($f1,$f2);
}
If you want it in C, implement it. But, if you really need a safe
rename, try this.
$_="Just_another_Perl_hacker,";open(_,">$_");print<$_*>;unlink;
--
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III |
| mer...@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/
Ouch. And it's rather difficult to do it right, since two differing strings
may in fact refer to the same directory entry. Sigh...
I'm gonna have to break apart each name into directory and filename,
stat the two directories to see if they're the same directory, and then
compare the filenames. Well, maybe the opposite order would be more
efficient, since if the filenames differ there's no need to do the
directory comparison.
Larry
Braakkkk. Next contestant...
You've just prevented me from saying
link('foo', 'bar');
...
&safe_rename('foo', 'bar');
At least, you've prevented that part of rename's semantics that says that
the name 'foo' won't exist any more.
You want to test whether the two names are the same directory entry, not
whether they're the same file.
Of course, if they are the same file already, and not the same directory
entry, you can do the rename merely by unlinking the first name. But you'd
still have to test to see if it's a different directory entry, or unlinking
the first name unlinks the second too.
I've already got the C routine written. It goes like this:
#ifndef RENAME
int
same_dirent(a,b)
char *a;
char *b;
{
char *fa = rindex(a,'/');
char *fb = rindex(b,'/');
struct stat tmpstatbuf1;
struct stat tmpstatbuf2;
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
char tmpbuf[MAXPATHLEN+1];
if (fa)
fa++;
else
fa = a;
if (fb)
fb++;
else
fb = b;
if (strNE(a,b))
return FALSE;
if (fa == a)
strcpy(tmpbuf,".")
else
strncpy(tmpbuf, a, fa - a);
if (stat(tmpbuf, &tmpstatbuf1) < 0)
return FALSE;
if (fb == b)
strcpy(tmpbuf,".")
else
strncpy(tmpbuf, b, fb - b);
if (stat(tmpbuf, &tmpstatbuf2) < 0)
return FALSE;
return tmpstatbuf1.st_dev == tmpstatbuf2.st_dev &&
tmpstatbuf1.st_ino == tmpstatbuf2.st_ino;
}
#endif /* !RENAME */
It has a little problem in that
/some/path/name/
doesn't compare to
/some/path/name/.
but I consider that a minor problem, on the GIGO principle.
Besides, REAL computers have a rename() system call. :-)
Larry
Let the kernel do the hard stuff :-) Just stat the two files, and do
the obvious. In fact, it's probably fairly simple to do in perl
itself, but it still needs to be done in C someday.
Yes, he will, Randal. There's no way I'm going to put "sub
safe_rename" in each and every Perl script. A Perl implementation in
which "rename($from, $to)" sometimes clobbers $from is buggy.
We should all be thankful (we are thankful, right?) that Larry is so
quick to provide bug fixes instead of workarounds.
--
Chip Salzenberg at ComDev/TCT <chip%t...@ateng.com>, <uunet!ateng!tct!chip>