Renaming file - Permission Denied

B

Bigus

Hi.

I have a script that recurses through a directory tree looking for OTF
fonts. When it finds one it extracts the postscript name and if the filename
is different then it attempts to rename the font file. However, it doesn't
rename the file and returns permission denied.

Strangely though, if I try the rename command stand-alone, ie: outside of
the loop where it runs through the files in the directory, it works. I'm
clutching at straws a bit here but is there some issue with renaming files
in a directory which is currently subject to the opendir command, or is it
something else entirely?

Here's the code:

----------------------------------------------

use strict;
use File::Copy;
use Font::TTF::Font;

recurser();

# -- recurse through 3 levels of folders -- #
sub recurser {

my $fromdir = "e:/software/fonts/";
my $fontext = "otf";

# level 1
opendir D1, $fromdir;
while( my $font1 = readdir(D1) ){
next if $font1 =~ /^\.+$/;
if(-d $fromdir.$font1){
# level 2
opendir D2, $fromdir.$font1;
while( my $font2 = readdir(D2) ){
next if $font2 =~ /^\.+$/;
# level 3
if(-d $fromdir.$font1.'/'.$font2){
opendir D3, $fromdir.$font1.'/'.$font2;
while( my $font3 = readdir(D3) ){
next if $font3 !~ /$fontext$/i;
renamefont
($fromdir.$font1.'/'.$font2.'/',$font3,$fontext);
}
closedir D3;
}
next if $font2 !~ /$fontext$/i;
renamefont ($fromdir.$font1.'/',$font2,$fontext);
}
closedir D2;
}
next if $font1 !~ /$fontext$/i;
renamefont ($fromdir,$font1,$fontext);
}
closedir D1;

}

# -- rename font -- ##

sub renamefont {

my($fontdir,$font,$ext) = @_;

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};

if($psname =~ /\w+/ and $psname !~ /\s+/){
my $newfontname = $psname.'.'.$ext;
if($newfontname ne $font){
rename $fontdir.$font, $fontdir.$newfontname or
print 'rename '.$fontdir.$font.',
'.$fontdir.$newfontname.' failed<br>';
}
}

}

----------------------------------------------

As mentioned above if I just take the output from the failed rename command
in the above loop and run that on it's own, eg:

use strict;
rename "e:/software/fonts/M/MyriadPro-BlackSemiExt_____.otf",
"e:/software/fonts/M/MyriadPro-BlackSemiExt.otf";

it works.

Could someone tell me what's going wrong?

Thanks
Bigus
 
B

Bigus

Thanks for the reply.

I'm going to take a wild guess and assume you're on Win32 :).

Yes indeed :)
You cannot
rename a file while it's open on Win32, so you will need to get the
Font::TTF::Font object to close the file before you do the rename. There
is also sometimes an issue with virus scanners: if the scanner is set to
scan-on-close, you can get the scanner re-opening the file immediately
so you still can't rename it. If you can't kill your virus scanner try
adding a sleep of a few seconds before the rename.

Nice idea. I had a look at the docs again for Font::TTF::Font and can't find
a close function but there is a "release" one which:

Releases ALL of the memory used by the TTF font and all of its component
objects. After calling this method, do NOT expect to have anything left in
the Font::TTF::Font object

So the bit of the code that gets the postscript name is now:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$f->release();

It doesn't work though. I also disabled my virus scanner and put a sleep of
a couple of seconds just before the rename for good measure but it's still
failing to rename the font file :-(
You can use lexical dirhandles, as well.

opendir my $D1, $fromdir or die "can't open '$fromdir': $!";

Variables with numbers in the names are usually an indication you should
be using an array instead.

In any case, you should be using File::Find or File::Find::Rule or
something instead of rolling your own.

Oh yes, thanks, that's a nifty module. Think I used that a few years ago but
had forgotten about it.
This is incorrect. You mean /\.$fontext$/i, otherwise you will catch
files called 'foo.gotf' and such like.

Aye, I realise that but I know there are only font files in the directories
I'm trawling through so didn't bother with the \. :)

Regards
Bigus
 
S

szr

Ben said:
Quoth "Bigus said:
You cannot
rename a file while it's open on Win32, so you will need to get the
Font::TTF::Font object to close the file before you do the rename.
There is also sometimes an issue with virus scanners: if the
scanner is set to scan-on-close, you can get the scanner
re-opening the file immediately so you still can't rename it. If
you can't kill your virus scanner try adding a sleep of a few
seconds before the rename.

Nice idea. I had a look at the docs again for Font::TTF::Font and
can't find a close function but there is a "release" one which:

Releases ALL of the memory used by the TTF font and all of its
component objects. After calling this method, do NOT expect to have
anything left in the Font::TTF::Font object

So the bit of the code that gets the postscript name is now:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$f->release();

It doesn't work though. I also disabled my virus scanner and put a
sleep of a couple of seconds just before the rename for good measure
but it's still failing to rename the font file :-(

Looking through the source (yuck, that's a fairly unpleasant module),
it appears that ->release doesn't actually close the file. It look
like allowing the object to go out of scope should do that, so you
want something like

my $psname;
{
my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
$psname = $t->{strings}[6][1][0]{0};
$f->release;
}
rename ...;

One additional way would be to set the object to undef, which forces it
to destruct itself right then and there.

my $psname;
my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
$psname = $t->{strings}[6][1][0]{0};
$f->release;
$f = undef;

rename ...;

Although it I would agree the separate scope method seems more readable.
 
B

Bigus

my $psname;
{
my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
$psname = $t->{strings}[6][1][0]{0};
$f->release;
}
rename ...;

That works :)

I also tried szr's suggestion of:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$f->release;
$f = undef;

but that didn't work. However, this did:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$f->release;
$t = undef;

So consequently I tried:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$t->release;

which also worked! Bit strange as the Font::TTF::Font clearly shows the
release() being applied to the open() handle.

Anyway, thank you very much for the help :)

Bigus
 
S

szr

Bigus said:
my $psname;
{
my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
$psname = $t->{strings}[6][1][0]{0};
$f->release;
}
rename ...;

That works :)

I also tried szr's suggestion of:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$f->release;
$f = undef;

but that didn't work. However, this did:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$f->release;
$t = undef;

So consequently I tried:

my $f = Font::TTF::Font->open($fontdir.$font);
my $t = $f->{name}->read;
my $psname = $t->{strings}[6][1][0]{0};
$t->release;

which also worked! Bit strange as the Font::TTF::Font clearly shows
the release() being applied to the open() handle.

Anyway, thank you very much for the help :)

Ah, it would seem the file handle is associated with $t, not $f then.
Too bad it's documentation wasn't more clear about this.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,045
Messages
2,570,389
Members
47,052
Latest member
ketan

Latest Threads

Top