diff --git a/bin/latexml b/bin/latexml index 9c5d457e58..a6d564cd08 100755 --- a/bin/latexml +++ b/bin/latexml @@ -133,7 +133,7 @@ UseLog(undef); if (!$serialized) { } elsif ($destination) { my $OUTPUT; - open($OUTPUT, ">", $destination) or die "Couldn't open output file $destination: $!"; + pathname_openfile($OUTPUT, ">", $destination) or die "Couldn't open output file $destination: $!"; print $OUTPUT $serialized; close($OUTPUT); } else { @@ -163,7 +163,7 @@ sub validate_args { # Check search paths @paths = map { pathname_canonical($_) } reverse(@paths); - if (my @baddirs = grep { !-d $_ } @paths) { + if (my @baddirs = grep { !pathname_test_d($_) } @paths) { push(@fails, "These search path do not exist: " . join(', ', @baddirs)); } # Find the requested source @@ -178,7 +178,7 @@ sub validate_args { } elsif (!($pathname = pathname_find($source, types => ['tex', 'bib', ''], paths => ['.', @paths]) || ($initfile && pathname_kpsewhich($source, types => ['tex', 'ltx'], paths => ['.', @paths]))) - || !-r $pathname) { + || !pathname_test_r($pathname)) { push(@fails, "Input file '$source' not readable"); } else { $mode = 'BibTeX' if !defined $mode && ($pathname =~ /\.bib$/); @@ -197,7 +197,7 @@ sub validate_args { $destination = pathname_canonical($destination); if (my $dir = pathname_directory($destination)) { # -w is not about writeability on Windows, see https://perldoc.perl.org/perlport#-X - if (!(pathname_mkdir($dir) && ($^O eq 'MSWin32' || -w $dir))) { + if (!(pathname_mkdir($dir) && ($^O eq 'MSWin32' || pathname_test_w($dir)))) { push(@fails, "Couldn't create writable destination directory $dir: $!"); } } } if($initfile && !$destination){ push(@fails, "No destination for dump of $initfile"); } diff --git a/bin/latexmlc b/bin/latexmlc index 402fa95a01..a9bb3bc924 100755 --- a/bin/latexmlc +++ b/bin/latexmlc @@ -43,7 +43,7 @@ use JSON::XS qw(decode_json); # Determine if a socket server is installed locally and obtain its pathname: my $latexmls; -$latexmls = catfile($RealBin_safe, 'latexmls') if (-e catfile($RealBin_safe, 'latexmls')); +$latexmls = catfile($RealBin_safe, 'latexmls') if pathname_test_e(catfile($RealBin_safe, 'latexmls')); $latexmls = which('latexmls') unless defined $latexmls; # Some defaults: @@ -84,7 +84,7 @@ if (!$opts->get('base')) { # Record if destination exists, for summary my $deststat; -$deststat = (stat($opts->get('destination')))[9] if $opts->get('destination'); +$deststat = (pathname_stat($opts->get('destination')))[9] if $opts->get('destination'); $deststat = 0 unless defined $deststat; push @$keyvals, ['path', $cdir]; #add current path, to ensure never empty @@ -117,7 +117,7 @@ if (!$logfile) { $opts->set('log', $logfile); } # A bit special: Since LaTeXML.pm will always try to append to the log file # make sure the file is new before we start. -unlink($logfile) if (-f $logfile); +unlink($logfile) if pathname_test_f($logfile); #*************************************************************************** # Prepare output variables: @@ -185,7 +185,7 @@ if ($result) { # has ALREADY encoded (in this case to utf8), so NO encode is needed! if ($destination) { my $output_handle; - open($output_handle, ">", $destination) or die("Fatal:I/O:forbidden Couldn't open output file " . $destination . ": $!"); + pathname_openfile($output_handle, ">", $destination) or die("Fatal:I/O:forbidden Couldn't open output file " . $destination . ": $!"); if ($whatsout eq 'archive') { binmode($output_handle, ':raw'); } @@ -206,7 +206,7 @@ if ($status_code == 3 || ($status =~ /fatal error/)) { # non-zero exit code f # == Helpers == sub summary { my ($destination, $prior_stat) = @_; - my $new_stat = (stat($destination))[9] || 0; + my $new_stat = (pathname_stat($destination))[9] || 0; return ($new_stat && ($prior_stat != $new_stat)) ? "\nWrote $destination\n" : "\nError! Did not write file $destination\n"; } diff --git a/bin/latexmlmath b/bin/latexmlmath index 4d58f76fd1..c76b363bf4 100755 --- a/bin/latexmlmath +++ b/bin/latexmlmath @@ -130,7 +130,7 @@ EODoc # Digest the TeX #====================================================================== @paths = map { pathname_canonical($_) } @paths; -if (my @baddirs = grep { !-d $_ } @paths) { +if (my @baddirs = grep { !pathname_test_d($_) } @paths) { warn "$LaTeXML::IDENTITY: these path directories do not exist: " . join(', ', @baddirs) . "\n"; } my $latexml = LaTeXML::Core->new(preload => ['LaTeX.pool', @preload], searchpaths => [@paths], @@ -190,7 +190,7 @@ if ($mathimage) { DirectMathImages->new(magnification => $mag, imagename => $mathimage, imagetype => $imagetype, %OPTIONS)); - unlink('LaTeXML.cache'); } + pathname_unlink('LaTeXML.cache'); } if ($mathsvg) { my $imagetype = 'svg'; @@ -201,18 +201,18 @@ if ($mathsvg) { imagename => $tmpfile, imagetype => $imagetype, %OPTIONS)); my $SVG; - open($SVG, '<', "$tmpfile.svg") or die "Couldn't read temporary svg output '$tmpfile': $!"; + pathname_openfile($SVG, '<', "$tmpfile.svg") or die "Couldn't read temporary svg output '$tmpfile': $!"; while (<$SVG>) { print $_; } close($SVG); - unlink "$tmpfile.svg"; } + pathname_unlink("$tmpfile.svg"); } else { $mathsvg =~ s/\.svg$//; my ($result) = $post->ProcessChain(cloneDoc($document), DirectMathImages->new(magnification => $mag, imagename => $mathsvg, imagetype => $imagetype, %OPTIONS)); } - unlink('LaTeXML.cache'); } + pathname_unlink('LaTeXML.cache'); } if ($pmml) { require LaTeXML::Post::MathML::Presentation; @@ -293,7 +293,7 @@ sub outputText { if (my $dir = pathname_directory($destination)) { pathname_mkdir($dir) or die "Couldn't create destination directory $dir: $!"; } my $OUT; - open($OUT, '>', $destination) or die "Couldn't open output file $destination: $!"; + pathname_openfile($OUT, '>', $destination) or die "Couldn't open output file $destination: $!"; binmode($OUT, ":encoding(UTF-8)"); print $OUT $serialized; close($OUT); } diff --git a/bin/latexmlpost b/bin/latexmlpost index 52b5f7e67a..5efbfdbbcd 100755 --- a/bin/latexmlpost +++ b/bin/latexmlpost @@ -190,7 +190,7 @@ eval { # Catch errors our %OPTIONS = ( searchpaths => ['.', @paths, $DOCUMENT->getSearchPaths], ); - if (defined $dbfile && !-f $dbfile) { + if (defined $dbfile && !pathname_test_f($dbfile)) { if (my $dbdir = pathname_directory($dbfile)) { pathname_mkdir($dbdir); } } my $DB = LaTeXML::Util::ObjectDB->new(dbfile => $dbfile, %OPTIONS); @@ -228,7 +228,7 @@ sub validate_args { # Check search paths @paths = map { pathname_canonical($_) } reverse(@paths); - if (my @baddirs = grep { !-d $_ } @paths) { + if (my @baddirs = grep { !pathname_test_d($_) } @paths) { push(@fails, "These search paths do not exist: " . join(', ', @baddirs)); } # Find the requested XML file @@ -238,7 +238,7 @@ sub validate_args { elsif ($xmlfile eq '-') { # Any sense in pre-reading it here?? } elsif (!($pathname = pathname_find($xmlfile, types => ['xml', ''], paths => ['.', @paths])) - || !-r $pathname) { + || !pathname_test_r($pathname)) { push(@fails, "Input file '$xmlfile' not readable"); } else { $xmlfile = $pathname; } diff --git a/lib/LaTeXML.pm b/lib/LaTeXML.pm index 1fe43bf0e2..0bd7d9dcc0 100644 --- a/lib/LaTeXML.pm +++ b/lib/LaTeXML.pm @@ -446,7 +446,7 @@ sub convert_post { my @procs = (); #TODO: Add support for the following: my $dbfile = $$opts{dbfile}; - if (defined $dbfile && !-f $dbfile) { + if (defined $dbfile && !pathname_test_f($dbfile)) { if (my $dbdir = pathname_directory($dbfile)) { pathname_mkdir($dbdir); } } my $DB = LaTeXML::Util::ObjectDB->new(dbfile => $dbfile, %PostOPS); @@ -632,7 +632,7 @@ sub convert_post { NoteLog(($$opts{recursive} ? "recursive " : "") . "processing finished " . localtime()); my $archive_log_status_code = max($$runtime{status_code}, $latexmlpost->getStatusCode); Note("Status:conversion:" . $archive_log_status_code); - open my $log_fh, '>', $log_file; + pathname_openfile(my $log_fh, '>', $log_file); print $log_fh $self->flush_log; close $log_fh; $self->bind_log; } @@ -702,7 +702,7 @@ sub new_latexml { nomathparse => $$opts{nomathparse}, # Backwards compatibility mathparse => $$opts{mathparse}); - if (my @baddirs = grep { !-d $_ } @{ $$opts{paths} }) { + if (my @baddirs = grep { !pathname_test_d($_) } @{ $$opts{paths} }) { warn "\n$LaTeXML::IDENTITY : these path directories do not exist: " . join(', ', @baddirs) . "\n"; } $latexml->withState(sub { diff --git a/lib/LaTeXML/Common/Config.pm b/lib/LaTeXML/Common/Config.pm index f6c4d34cbf..98af566686 100644 --- a/lib/LaTeXML/Common/Config.pm +++ b/lib/LaTeXML/Common/Config.pm @@ -633,7 +633,7 @@ sub _read_options_file { my $OPT; #### Now can we report status to right places before we've gotten configuration??? (verbosity, logfile...) #### ProgressSpinup("Loading profile $file"); - unless (open($OPT, "<", $file)) { + unless (pathname_openfile($OPT, "<", $file)) { Error('expected', $file, "Could not open options file '$file'"); return; } while (my $line = <$OPT>) { @@ -662,7 +662,7 @@ sub _read_options_file { # But also the standard behaviour, where the $env is an array of paths $env_value = $ENV{$env_name}; next unless $env_value; - @values = grep { -d $_ } reverse(split(':', $env_value)); + @values = grep { pathname_test_d($_) } reverse(split(':', $env_value)); next unless @values; } CORE::push(@$opts, "--$key=$_") foreach (@values); } else { diff --git a/lib/LaTeXML/Common/Error.pm b/lib/LaTeXML/Common/Error.pm index 71194c88d9..451d4c654c 100644 --- a/lib/LaTeXML/Common/Error.pm +++ b/lib/LaTeXML/Common/Error.pm @@ -134,7 +134,7 @@ sub UseLog { $log_count++; return if $LOG or not($path); # already opened? pathname_mkdir(pathname_directory($path)); # and hopefully no errors! :> - open($LOG, ($append ? '>>' : '>'), $path) or die "Cannot open log file $path for writing: $!"; + pathname_openfile($LOG, ($append ? '>>' : '>'), $path) or die "Cannot open log file $path for writing: $!"; $LOG_PATH = $path; binmode($LOG, ":encoding(UTF-8)"); } return; } diff --git a/lib/LaTeXML/Common/Font/Metric.pm b/lib/LaTeXML/Common/Font/Metric.pm index b7b78698cf..bd4df6ec2a 100644 --- a/lib/LaTeXML/Common/Font/Metric.pm +++ b/lib/LaTeXML/Common/Font/Metric.pm @@ -47,7 +47,7 @@ sub read_tfm { my $pathname = $$self{file}; # Read the TFM raw data. my $TFM; - if (!open($TFM, '<', $pathname)) { + if (!pathname_openfile($TFM, '<', $pathname)) { Error('expected', $pathname, undef, "Couldn't open TFM files '$pathname': $!"); return; } diff --git a/lib/LaTeXML/Common/Model.pm b/lib/LaTeXML/Common/Model.pm index 89724e8144..091e8f61f8 100644 --- a/lib/LaTeXML/Common/Model.pm +++ b/lib/LaTeXML/Common/Model.pm @@ -138,7 +138,7 @@ sub loadCompiledSchema { my ($self, $file) = @_; ProgressSpinup("Loading compiled schema $file"); my $MODEL; - open($MODEL, '<', $file) or Fatal('I/O', $file, undef, "Cannot open Compiled Model $file for reading", $!); + pathname_openfile($MODEL, '<', $file) or Fatal('I/O', $file, undef, "Cannot open Compiled Model $file for reading", $!); my $line; while ($line = <$MODEL>) { if ($line =~ /^([^\{]+)\{(.*?)\}\((.*?)\)$/) { diff --git a/lib/LaTeXML/Common/XML/Parser.pm b/lib/LaTeXML/Common/XML/Parser.pm index a7388fb105..7ce6249cd2 100644 --- a/lib/LaTeXML/Common/XML/Parser.pm +++ b/lib/LaTeXML/Common/XML/Parser.pm @@ -12,6 +12,7 @@ package LaTeXML::Common::XML::Parser; use strict; use warnings; +use LaTeXML::Util::Pathname; use XML::LibXML; sub new { @@ -24,7 +25,7 @@ sub parseFile { my ($self, $file) = @_; LaTeXML::Common::XML::initialize_catalogs(); # LaTeXML::Common::XML::initialize_input_callbacks($$self{parser}); - if (((-s $file) || 0) > 20_000_000) { + if ((pathname_test_s($file) || 0) > 20_000_000) { $$self{parser}->set_option('huge', 1); } return $$self{parser}->parse_file($file); } diff --git a/lib/LaTeXML/Core/Mouth/file.pm b/lib/LaTeXML/Core/Mouth/file.pm index 3e09a83123..26ed569df5 100644 --- a/lib/LaTeXML/Core/Mouth/file.pm +++ b/lib/LaTeXML/Core/Mouth/file.pm @@ -32,11 +32,11 @@ sub new { sub openFile { my ($self, $pathname) = @_; my $IN; - if (!-r $pathname) { + if (!pathname_test_r($pathname)) { Error('I/O', 'unreadable', $self, "File $pathname is not readable. Ignoring."); } - elsif ((!-z $pathname) && (-B $pathname)) { + elsif (!pathname_test_z($pathname) && pathname_test_B($pathname)) { Error('invalid', 'binary', $self, "Input file $pathname appears to be binary. Ignoring."); } - elsif (open($IN, '<:raw', $pathname)) { } + elsif (pathname_openfile($IN, '<:raw', $pathname)) { } else { Error('I/O', 'open', $self, "Can't open $pathname for reading", $!); } $$self{IN} = $IN; diff --git a/lib/LaTeXML/Engine/pdfTeX.pool.ltxml b/lib/LaTeXML/Engine/pdfTeX.pool.ltxml index 2d074996ab..188d6e2fad 100644 --- a/lib/LaTeXML/Engine/pdfTeX.pool.ltxml +++ b/lib/LaTeXML/Engine/pdfTeX.pool.ltxml @@ -116,7 +116,7 @@ DefMacro('\pdffilesize{}', sub { # used in expl3's \__file_full_name:n , among others my ($gullet, $file) = @_; if (my $path = FindFile(ToString(Expand($file)))) { - my @stat = stat $path; + my @stat = pathname_stat($path); return (defined $stat[7]) ? Explode($stat[7]) : (); } else { return (); } }); diff --git a/lib/LaTeXML/Package.pm b/lib/LaTeXML/Package.pm index 963f64aeb2..aee95d44b6 100644 --- a/lib/LaTeXML/Package.pm +++ b/lib/LaTeXML/Package.pm @@ -2087,8 +2087,8 @@ sub FindFile_aux { return $file; } if (pathname_is_absolute($file)) { # And if we've got an absolute path, if (!$options{noltxml}) { - return $file . '.ltxml' if -f ($file . '.ltxml'); } # No need to search, just check if it exists. - return $file if -f $file; # No need to search, just check if it exists. + return $file . '.ltxml' if pathname_test_f($file . '.ltxml'); } # No need to search, just check if it exists. + return $file if pathname_test_f($file); # No need to search, just check if it exists. return; } # otherwise we're never going to find it. # Note that the strategy is complicated by the fact that @@ -2132,7 +2132,7 @@ sub FindFile_aux { local $ENV{TEXINPUTS} = join($Config::Config{'path_sep'}, @$paths, $ENV{TEXINPUTS} || $Config::Config{'path_sep'}); if (my $result = (!$options{searchpaths_only}) && pathname_kpsewhich(@candidates)) { - return (-f $result ? $result : undef); } + return pathname_test_f($result) ? $result : undef; } if ($urlbase && ($path = url_find($file, urlbase => $urlbase))) { return $path; } return; } @@ -2760,7 +2760,7 @@ sub maybeRequireDependencies { if (my $path = FindFile($file, type => $type, noltxml => 1)) { local $/ = undef; my $IN; - if (open($IN, '<', $path)) { + if (pathname_openfile($IN, '<', $path)) { my $code = <$IN>; close($IN); my @classes = (); diff --git a/lib/LaTeXML/Package/listings.sty.ltxml b/lib/LaTeXML/Package/listings.sty.ltxml index 857129d147..9e9f4eb0b7 100644 --- a/lib/LaTeXML/Package/listings.sty.ltxml +++ b/lib/LaTeXML/Package/listings.sty.ltxml @@ -323,7 +323,7 @@ sub listingsReadRawFile { my $path = FindFile($filename); my $text; my $LST_FH; - if ($path && open($LST_FH, '<', $path)) { + if ($path && pathname_openfile($LST_FH, '<', $path)) { { local $/ = undef; $text = <$LST_FH>; close($LST_FH); } } diff --git a/lib/LaTeXML/Post.pm b/lib/LaTeXML/Post.pm index 8859e5d726..0483d851e0 100644 --- a/lib/LaTeXML/Post.pm +++ b/lib/LaTeXML/Post.pm @@ -1544,7 +1544,7 @@ sub openCache { or return Fatal('internal', 'db', undef, "Couldn't create DB cache for " . $self->getDestination, "Message was: " . $!, - (-f $dbfile ? "\n(possibly incompatible db format?)" : '')); + (pathname_test_f($dbfile) ? "\n(possibly incompatible db format?)" : '')); } return; } diff --git a/lib/LaTeXML/Post/Graphics.pm b/lib/LaTeXML/Post/Graphics.pm index ff9aa82d0e..d168e862a0 100644 --- a/lib/LaTeXML/Post/Graphics.pm +++ b/lib/LaTeXML/Post/Graphics.pm @@ -135,7 +135,7 @@ sub findGraphicFile { my ($self, $doc, $node) = @_; if (my $source = $node->getAttribute('graphic')) { # if we already have a usable candidate, save ourselves the work - my @paths = grep { -e $_ } split(',', $node->getAttribute('candidates') || ''); + my @paths = grep { pathname_test_e($_) } split(',', $node->getAttribute('candidates') || ''); if (!scalar(@paths)) { # Find all acceptable image files, in order of search paths my ($dir, $name, $reqtype) = pathname_split($source); diff --git a/lib/LaTeXML/Post/LaTeXImages.pm b/lib/LaTeXML/Post/LaTeXImages.pm index 9c6e632044..ee8ccf5863 100644 --- a/lib/LaTeXML/Post/LaTeXImages.pm +++ b/lib/LaTeXML/Post/LaTeXImages.pm @@ -241,7 +241,7 @@ sub generateImages { foreach my $key (sort keys %table) { my $store = $doc->cacheLookup($key); if ($store && ($store =~ /^(.*);(\d+);(\d+);(\d+)$/)) { - next if -f pathname_absolute($1, $destdir); } + next if pathname_test_f(pathname_absolute($1, $destdir)); } push(@pending, $table{$key}); } Debug("LaTeXImages: $nuniq unique; " . scalar(@pending) . " new") if $LaTeXML::DEBUG{images}; @@ -255,7 +255,7 @@ sub generateImages { # === Generate the LaTeX file. my $texfile = pathname_make(dir => $workdir, name => $jobname, type => 'tex'); my $TEX; - if (!open($TEX, '>', $texfile)) { + if (!pathname_openfile($TEX, '>', $texfile)) { Error('I/O', $texfile, undef, "Cant write to '$texfile'", "Response was: $!"); return $doc; } my ($pre_preamble, $add_to_body) = $self->pre_preamble($doc); @@ -288,7 +288,7 @@ sub generateImages { # Sometimes latex returns non-zero code, even though it apparently succeeded. # And sometimes it doesn't produce a dvi, even with 0 return code? - if (($ltxerr != 0) || (!-f "$workdir/$jobname.dvi")) { + if ($ltxerr != 0 || !pathname_test_f("$workdir/$jobname.dvi")) { $workdir->unlink_on_destroy(0) if $LaTeXML::DEBUG{images}; # Preserve junk Error('shell', $LATEXCMD, undef, "LaTeX command '$ltxcommand' failed", @@ -302,7 +302,7 @@ sub generateImages { # Extract dimensions (width x height+depth) from each image from log file. my @dimensions = (); my $LOG; - if (open($LOG, '<', "$workdir/$jobname.log")) { + if (pathname_openfile($LOG, '<', "$workdir/$jobname.log")) { while (<$LOG>) { if (/^\s*LXIMAGE\s*(\d+)\s*=\s*([\+\-\d\.]+)pt\s*x\s*([\+\-\d\.]+)pt\s*\+\s*([\+\-\d\.]+)pt\s*$/) { $dimensions[$1] = [$2, $3, $4]; } } @@ -334,7 +334,7 @@ sub generateImages { my ($index, $ndigits) = (0, 1 + int(log($doc->cacheLookup((ref $self) . ':_max_image_') || 1) / log(10))); foreach my $entry (@pending) { my $src = "$workdir/" . sprintf($$self{dvicmd_output_name}, ++$index); - if (-f $src) { + if (pathname_test_f($src)) { my @dests = @{ $$entry{dest} }; push(@dests, $self->generateResourcePathname($doc, $$entry{nodes}[0], undef, $$self{imagetype})) unless @dests; @@ -528,10 +528,10 @@ sub convert_image { sub DESTROY { if (my $tmpdir = File::Spec->tmpdir()) { - if (-d $tmpdir && opendir(my $tmpdir_fh, $tmpdir)) { - my @empty_magick = grep { -z $_ } map { "$tmpdir/$_" } readdir($tmpdir_fh); + if (pathname_test_d($tmpdir) && pathname_opendir(my $tmpdir_fh, $tmpdir)) { + my @empty_magick = grep { pathname_test_z($_) } map { "$tmpdir/$_" } readdir($tmpdir_fh); closedir($tmpdir_fh); - unlink $_ foreach @empty_magick; + pathname_unlink($_) foreach @empty_magick; } } return; } diff --git a/lib/LaTeXML/Post/MakeBibliography.pm b/lib/LaTeXML/Post/MakeBibliography.pm index e01b19bc05..c978a4b145 100644 --- a/lib/LaTeXML/Post/MakeBibliography.pm +++ b/lib/LaTeXML/Post/MakeBibliography.pm @@ -155,7 +155,7 @@ sub getBibliographies { $raw .= $rawbib; } else { # TODO: Is this a memory concern for large bib files? - if (open(my $bibfh, '<', $rawbib)) { + if (pathname_openfile(my $bibfh, '<', $rawbib)) { $raw .= join("", <$bibfh>); close $bibfh; } else { @@ -217,7 +217,7 @@ sub convertBibliography { # ->bind_log analog: my $biblog = ''; ### my $biblog_handle; -### open($biblog_handle, ">>", \$biblog) or Error("Can't redirect STDERR to log for inner bibliography converter!"); +### pathname_openfile($biblog_handle, ">>", \$biblog) or Error("Can't redirect STDERR to log for inner bibliography converter!"); ### *BIB_STDERR_SAVED = *STDERR; ### *STDERR = *$biblog_handle; # end ->bind_log diff --git a/lib/LaTeXML/Post/Manifest/Epub.pm b/lib/LaTeXML/Post/Manifest/Epub.pm index 42f7d3245a..760b0e0a9c 100644 --- a/lib/LaTeXML/Post/Manifest/Epub.pm +++ b/lib/LaTeXML/Post/Manifest/Epub.pm @@ -72,17 +72,17 @@ sub initialize { # 1. Create mimetype declaration my $EPUB_FH; my $mime_path = pathname_concat($directory, 'mimetype'); - open($EPUB_FH, ">", $mime_path) + pathname_openfile($EPUB_FH, ">", $mime_path) or Fatal('I/O', 'mimetype', $doc, "Couldn't open '$mime_path' for writing: $!"); print $EPUB_FH 'application/epub+zip'; close $EPUB_FH; # 2. Create META-INF metadata directory my $meta_inf_dir = catdir($directory, 'META-INF'); - mkdir $meta_inf_dir; + pathname_mkdir($meta_inf_dir); # 2.1. Add the container.xml description my $CONTAINER_FH; my $container_path = pathname_concat($meta_inf_dir, 'container.xml'); - open($CONTAINER_FH, ">", $container_path) + pathname_openfile($CONTAINER_FH, ">", $container_path) or Fatal('I/O', 'container.xml', $doc, "Couldn't open '$container_path' for writing: $!"); print $CONTAINER_FH $container_content; close $CONTAINER_FH; @@ -202,7 +202,7 @@ sub finalize { my $OPS_abspath = $_; my $OPS_pathname = pathname_relative($OPS_abspath, $OPS_directory); my (undef, $name, $ext) = pathname_split($OPS_pathname); - if (-f $OPS_abspath && $ext ne 'xhtml' && "$name.$ext" ne 'LaTeXML.cache' && $OPS_abspath ne 'content.opf') { + if (pathname_test_f($OPS_abspath) && $ext ne 'xhtml' && "$name.$ext" ne 'LaTeXML.cache' && $OPS_abspath ne 'content.opf') { push(@content, $OPS_pathname); } } }, $OPS_directory); @@ -223,11 +223,11 @@ sub finalize { # Write the content.opf file to disk my $directory = $$self{siteDirectory}; my $content_path = pathname_concat($OPS_directory, 'content.opf'); - if (-f $content_path) { + if (pathname_test_f($content_path)) { Info('note', 'content.opf', undef, 'using the manifest supplied by the user'); } else { my $OPF_FH; - open($OPF_FH, ">", $content_path) + pathname_openfile($OPF_FH, ">", $content_path) or Fatal('I/O', 'content.opf', undef, "Couldn't open '$content_path' for writing: $_"); print $OPF_FH $$self{opf}->toString(1); close $OPF_FH; } diff --git a/lib/LaTeXML/Post/Writer.pm b/lib/LaTeXML/Post/Writer.pm index da29b7ee8d..b938878564 100644 --- a/lib/LaTeXML/Post/Writer.pm +++ b/lib/LaTeXML/Post/Writer.pm @@ -54,7 +54,7 @@ sub process { or return Fatal('I/O', $destdir, undef, "Couldn't create directory '$destdir'", "Response was: $!"); my $OUT; - open($OUT, '>', $destination) + pathname_openfile($OUT, '>', $destination) or return Fatal('I/O', $destdir, undef, "Couldn't write '$destination'", "Response was: $!"); print $OUT $serialized; close($OUT); } diff --git a/lib/LaTeXML/Post/XSLT.pm b/lib/LaTeXML/Post/XSLT.pm index 385269cd88..b158cae7a4 100644 --- a/lib/LaTeXML/Post/XSLT.pm +++ b/lib/LaTeXML/Post/XSLT.pm @@ -40,7 +40,7 @@ sub new { types => ['xsl'], installation_subdir => 'resources/XSLT', paths => $$self{searchpaths} || ['.']); Error('missing-file', $stylesheet, undef, "No stylesheet '$stylesheet' found!") - unless $pathname && -f $pathname; + unless $pathname && pathname_test_f($pathname); $stylesheet = $pathname; } $stylesheet = $stylesheet && LaTeXML::Common::XML::XSLT->new($stylesheet); if ((!ref $stylesheet) || !($stylesheet->can('transform'))) { diff --git a/lib/LaTeXML/Pre/BibTeX.pm b/lib/LaTeXML/Pre/BibTeX.pm index b604cf82f6..d171f751ff 100644 --- a/lib/LaTeXML/Pre/BibTeX.pm +++ b/lib/LaTeXML/Pre/BibTeX.pm @@ -67,7 +67,7 @@ sub newFromFile { "SEACHPATHS is " . join(', ', @$paths)) unless $file; my $BIB; $$self{file} = $bibname; - open($BIB, '<', $file) or Fatal('I/O', $file, undef, "Can't open BibTeX $file for reading", $!); + pathname_openfile($BIB, '<', $file) or Fatal('I/O', $file, undef, "Can't open BibTeX $file for reading", $!); $$self{lines} = [<$BIB>]; close($BIB); $$self{line} = shift(@{ $$self{lines} }) || ''; diff --git a/lib/LaTeXML/Util/ObjectDB.pm b/lib/LaTeXML/Util/ObjectDB.pm index 09a4e9743e..1f6dba3b33 100644 --- a/lib/LaTeXML/Util/ObjectDB.pm +++ b/lib/LaTeXML/Util/ObjectDB.pm @@ -45,7 +45,7 @@ sub new { my $dbfile = $options{dbfile}; if ($dbfile && $options{clean}) { warn "\nWARN: Removing Object database file $dbfile!!!\n"; - unlink($dbfile); } + pathname_unlink($dbfile); } my $self = bless { dbfile => $dbfile, objects => {}, externaldb => {}, diff --git a/lib/LaTeXML/Util/Pack.pm b/lib/LaTeXML/Util/Pack.pm index 59ed0e8b1c..a6d6c786fa 100644 --- a/lib/LaTeXML/Util/Pack.pm +++ b/lib/LaTeXML/Util/Pack.pm @@ -16,6 +16,7 @@ use warnings; use IO::String; use JSON::XS qw(decode_json); use File::Find; +use LaTeXML::Util::Pathname; use base qw(Exporter); our @EXPORT = qw(&pack_collection); @@ -68,7 +69,7 @@ sub detect_source { if ($readme_file = $self->find_file('00README.json')) { my $json_str = do { local $/ = undef; - open(my $README_FH, '<', $readme_file) or + pathname_openfile(my $README_FH, '<', $readme_file) or (print STDERR "failed to open '$readme_file' for use as input directives: $!. Continuing.\n"); <$README_FH>; } || ''; @@ -80,7 +81,7 @@ sub detect_source { } } # I.1.2. Legacy arXiv 00README.XXX elsif ($readme_file = $self->find_file('00README.XXX')) { - open(my $README_FH, '<', $readme_file) or + pathname_openfile(my $README_FH, '<', $readme_file) or (print STDERR "failed to open '$readme_file' for use as input directives: $!. Continuing.\n"); local $/ = "\n"; my $toplevelfile; @@ -93,7 +94,7 @@ sub detect_source { $toplevelfile = $self->full_filename($name); } elsif ($directive eq 'ignore') { my $ignored_filepath = $self->full_filename($name); - unlink($ignored_filepath) if -e $ignored_filepath; } } } + pathname_unlink($ignored_filepath) if pathname_test_e($ignored_filepath); } } } return $toplevelfile if $toplevelfile; } # I.2. Without an explicit directive, @@ -107,8 +108,8 @@ sub detect_source { $tex_file = $self->full_filename($tex_file); # Open file and read first few bytes to do magic sequence identification # note that file will be auto-closed when $FILE_TO_GUESS goes out of scope - next unless -e $tex_file; # skip deleted "ignored" files. - open(my $FILE_TO_GUESS, '<', $tex_file) or + next unless pathname_test_e($tex_file); # skip deleted "ignored" files. + pathname_openfile(my $FILE_TO_GUESS, '<', $tex_file) or (print STDERR "failed to open '$tex_file' to guess its format: $!. Continuing.\n"); local $/ = "\n"; my ($maybe_tex, $maybe_tex_priority, $maybe_tex_priority2); @@ -200,7 +201,7 @@ sub detect_source { @files_by_likelihood = @pdf_includes if @pdf_includes; } # Special heuristic 3: prefer "best" candidates with a .bbl file if (scalar(@files_by_likelihood) > 1) { - my @with_bbl = grep { my $base = $_; $base =~ s/\.tex$//; -e "$base.bbl"; } @files_by_likelihood; + my @with_bbl = grep { my $base = $_; $base =~ s/\.tex$//; pathname_test_e("$base.bbl"); } @files_by_likelihood; @files_by_likelihood = @with_bbl if @with_bbl; } # Special heuristic 4 ?!: Sometimes in arXiv the decision is made in an unclear manner # (example: see 2112.08935 v1, which has equally good main.tex and bare_adv.tex) @@ -223,7 +224,7 @@ sub heuristic_check_for_pdftex { my @filenames = @_; my @pdf_includes = (); for my $tex_file (@filenames) { - my $is_open = open(my $TEX_FH, '<', $tex_file); + my $is_open = pathname_openfile(my $TEX_FH, '<', $tex_file); if (!$is_open) { print STDERR "failed to open '$tex_file' to guess its format: $!. Continuing.\n"; next; } @@ -340,7 +341,7 @@ sub pack_collection { elsif ($whatsout eq 'math') { # Math output - least common ancestor of all math in the document push @packed_docs, get_math($doc); - unlink('LaTeXML.cache'); } + pathname_unlink('LaTeXML.cache'); } else { push @packed_docs, $doc; } } return @packed_docs; } diff --git a/lib/LaTeXML/Util/Pack/Dir.pm b/lib/LaTeXML/Util/Pack/Dir.pm index ab8869380b..f127fc922d 100644 --- a/lib/LaTeXML/Util/Pack/Dir.pm +++ b/lib/LaTeXML/Util/Pack/Dir.pm @@ -15,6 +15,7 @@ use strict; use warnings; use File::Find; use File::Spec::Functions qw(catfile); +use LaTeXML::Util::Pathname; use base qw(LaTeXML::Util::Pack); @@ -29,7 +30,7 @@ sub unpack_source { sub find_file { my ($self, $name) = @_; my $filename = $$self{directory} . "/$name"; - my $found = -e $filename; + my $found = pathname_test_e($filename); return $found && $filename; } sub full_filename { diff --git a/lib/LaTeXML/Util/Pack/Zip.pm b/lib/LaTeXML/Util/Pack/Zip.pm index c15c187983..1f9a938037 100644 --- a/lib/LaTeXML/Util/Pack/Zip.pm +++ b/lib/LaTeXML/Util/Pack/Zip.pm @@ -75,7 +75,7 @@ sub get_archive { my ($directory, $whatsout) = @_; # Zip and send back my $archive = Archive::Zip->new(); - opendir(my $dirhandle, $directory) + pathname_opendir(my $dirhandle, $directory) # TODO: Switch to Error API # or Fatal('expected', 'directory', undef, # "Expected a directory to archive '$directory':", $@); @@ -83,8 +83,8 @@ sub get_archive { my @entries = grep { /^[^.]/ } readdir($dirhandle); closedir $dirhandle; my $ext_exclude = $LaTeXML::Util::Pack::ARCHIVE_EXT_EXCLUDE; - my @files = grep { !/$ext_exclude/ && -f pathname_concat($directory, $_) } @entries; - my @subdirs = grep { -d File::Spec->catdir($directory, $_) } @entries; + my @files = grep { !/$ext_exclude/ && pathname_test_f(pathname_concat($directory, $_)) } @entries; + my @subdirs = grep { pathname_test_d(File::Spec->catdir($directory, $_)) } @entries; # We want to first add the files instead of simply invoking ->addTree on the top level # without ANY file attributes at all, # since EPUB is VERY picky about the first entry in the archive starting at byte 38 (file 'mimetype') @@ -96,7 +96,7 @@ sub get_archive { local $/ = undef; my $FH; my $pathname = pathname_concat($directory, $file); - open $FH, "<", $pathname + pathname_openfile($FH, "<", $pathname) # TODO: Switch to Error API #or Fatal('I/O', $pathname, undef, "File $pathname is not readable."); or (print STDERR "Fatal:I/O:$pathname File $pathname is not readable."); diff --git a/lib/LaTeXML/Util/Pathname.pm b/lib/LaTeXML/Util/Pathname.pm index 4d857546e7..159243783d 100644 --- a/lib/LaTeXML/Util/Pathname.pm +++ b/lib/LaTeXML/Util/Pathname.pm @@ -36,6 +36,14 @@ use base qw(Exporter); our @EXPORT = qw( &pathname_find &pathname_findall &pathname_kpsewhich &pathname_make &pathname_canonical &pathname_split &pathname_directory &pathname_name &pathname_type + &pathname_test_r &pathname_test_w &pathname_test_x &pathname_test_o + &pathname_test_R &pathname_test_W &pathname_test_X &pathname_test_O + &pathname_test_e &pathname_test_z &pathname_test_s + &pathname_test_f &pathname_test_d &pathname_test_l &pathname_test_p &pathname_test_S &pathname_test_b &pathname_test_c + &pathname_test_u &pathname_test_g &pathname_test_k + &pathname_test_T &pathname_test_B + &pathname_test_M &pathname_test_A &pathname_test_C + &pathname_open &pathname_stat &pathname_unlink &pathname_openfile &pathname_opendir &pathname_timestamp &pathname_concat &pathname_relative &pathname_absolute &pathname_to_url @@ -222,9 +230,53 @@ sub pathname_to_url { #====================================================================== # Actual file system operations. + +sub pathname_test_r { return -r $_[0]; } +sub pathname_test_w { return -w $_[0]; } +sub pathname_test_x { return -x $_[0]; } +sub pathname_test_o { return -o $_[0]; } + +sub pathname_test_R { return -R $_[0]; } +sub pathname_test_W { return -W $_[0]; } +sub pathname_test_X { return -X $_[0]; } +sub pathname_test_O { return -O $_[0]; } + +sub pathname_test_e { return -e $_[0]; } +sub pathname_test_z { return -z $_[0]; } +sub pathname_test_s { return -s $_[0]; } + +sub pathname_test_f { return -f $_[0]; } +sub pathname_test_d { return -d $_[0]; } +sub pathname_test_l { return -l $_[0]; } +sub pathname_test_p { return -p $_[0]; } +sub pathname_test_S { return -S $_[0]; } +sub pathname_test_b { return -b $_[0]; } +sub pathname_test_c { return -c $_[0]; } + +sub pathname_test_u { return -u $_[0]; } +sub pathname_test_g { return -g $_[0]; } +sub pathname_test_k { return -k $_[0]; } + +sub pathname_test_T { return -T $_[0]; } +sub pathname_test_B { return -B $_[0]; } + +sub pathname_test_M { return -M $_[0]; } +sub pathname_test_A { return -A $_[0]; } +sub pathname_test_C { return -C $_[0]; } + +sub pathname_stat { return stat $_[0]; } + +sub pathname_unlink { return unlink(@_); } + +sub pathname_openfile { + return open($_[0], $_[1], $_[2]); } + +sub pathname_opendir { + return opendir($_[0], $_[1]); } + sub pathname_timestamp { my ($pathname) = @_; - return -f $pathname ? (stat($pathname))[9] : 0; } + return pathname_test_f($pathname) ? (pathname_stat($pathname))[9] : 0; } our $CWD = undef; # DO NOT use pathname_cwd, unless you also use pathname_chdir to change dirs!!! @@ -252,7 +304,7 @@ sub pathname_mkdir { my (@dirs) = (File::Spec->splitdir($dirs), $last); for (my $i = 0 ; $i <= $#dirs ; $i++) { my $dir = File::Spec->catpath($volume, File::Spec->catdir(@dirs[0 .. $i]), ''); - if (!-d $dir) { + if (!pathname_test_d($dir)) { mkdir($dir) or return; } } return $directory; } @@ -263,7 +315,7 @@ sub pathname_copy { # If it _needs_ to be copied: $source = pathname_canonical($source); $destination = pathname_canonical($destination); - if ((!-f $destination) || (pathname_timestamp($source) > pathname_timestamp($destination))) { + if (!pathname_test_f($destination) || (pathname_timestamp($source) > pathname_timestamp($destination))) { if (my $destdir = pathname_directory($destination)) { pathname_mkdir($destdir) or return; } ### if($^O =~ /^(MSWin32|NetWare)$/){ # Windows @@ -273,7 +325,7 @@ sub pathname_copy { ### system("cp --preserve=timestamps $source $destination")==0 or return; } # Hopefully this portably copies, preserving timestamp. copy($source, $destination) or return; - my ($atime, $mtime) = (stat($source))[8, 9]; + my ($atime, $mtime) = (pathname_stat($source))[8, 9]; utime $atime, $mtime, $destination; # And set the modification time } return $destination; } @@ -296,7 +348,7 @@ sub pathname_copy { # was installed, by appending it to the paths. # This is presumably daemon safe... -my @INSTALLDIRS = grep { (-f "$_.pm") && (-d $_) } +my @INSTALLDIRS = grep { pathname_test_f("$_.pm") && pathname_test_d($_) } map { pathname_canonical($_ . $SEP . 'LaTeXML') } @INC; # [CONSTANT] sub pathname_installation { @@ -374,7 +426,7 @@ sub candidate_pathnames { qr/^\Q$name\E\Q$ext\E$/]); } } # Now, combine; precedence to leading directories. foreach my $dir (@dirs) { - opendir(my $dir_handle, $dir) or next; + pathname_opendir(my $dir_handle, $dir) or next; my @dir_files = readdir($dir_handle); closedir($dir_handle); for my $local_file (@dir_files) { @@ -434,7 +486,7 @@ sub build_kpse_cache { my @filters = (); # Really shouldn't end up empty. foreach my $path (split(/$KPATHSEP/, $texpaths)) { $path =~ s/^!!//; $path =~ s|//+$|/|; - push(@filters, $path) if -d $path; } + push(@filters, $path) if pathname_test_d($path); } my $filterre = scalar(@filters) && '(?:' . join('|', map { "\Q$_\E"; } @filters) . ')'; $texmf =~ s/^["']//; $texmf =~ s/["']$//; $texmf =~ s/^\s*\\\{(.+?)}\s*$/$1/s; @@ -443,11 +495,11 @@ sub build_kpse_cache { foreach my $dir (@dirs) { $dir =~ s/^!!//; # Presumably if no ls-R, we can ignore the directory? - if (-f "$dir/ls-R") { + if (pathname_test_f("$dir/ls-R")) { my $LSR; my $subdir; my $skip = 0; # whether to skip entries in the current subdirectory. - open($LSR, '<', "$dir/ls-R") or die "Cannot read $dir/ls-R: $!"; + pathname_openfile($LSR, '<', "$dir/ls-R") or die "Cannot read $dir/ls-R: $!"; while (<$LSR>) { chop; next if !$_ || (substr($_, 0, 1) eq '%'); diff --git a/lib/LaTeXML/Util/Test.pm b/lib/LaTeXML/Util/Test.pm index 43be8d555c..a02012ec57 100644 --- a/lib/LaTeXML/Util/Test.pm +++ b/lib/LaTeXML/Util/Test.pm @@ -31,7 +31,7 @@ sub latexml_tests { if ($options{texlive_min} && (texlive_version() < $options{texlive_min})) { plan skip_all => "Requirement minimal texlive $options{texlive_min} not met."; return done_testing(); } - if (!opendir($DIR, $directory)) { + if (!pathname_opendir($DIR, $directory)) { # Can't read directory? Fail (assumed single) test. return do_fail($directory, "Couldn't read directory $directory:$!"); } else { @@ -55,7 +55,7 @@ sub latexml_tests { foreach my $name (@core_tests) { my $test = "$directory/$name"; SKIP: { - skip("No file $test.xml", 1) unless (-f "$test.xml"); + skip("No file $test.xml", 1) unless pathname_test_f("$test.xml"); next unless check_requirements($test, 1, $$requires{'*'}, $$requires{$name}); latexml_ok("$test.tex", "$test.xml", $test, $options{compare}, $options{core_options}); } } # Carry out any post-processing tests @@ -63,7 +63,7 @@ sub latexml_tests { my $test = "$directory/$name"; SKIP: { skip("No file $test.xml and/or $test-post.xml", 1) - unless ((-f "$test.xml") && (-f "$test-post.xml")); + unless pathname_test_f("$test.xml") && pathname_test_f("$test-post.xml"); next unless check_requirements($test, 1, $$requires{'*'}, $$requires{$name}); latexmlpost_ok("$test.tex", "$test-post.xml", $test); } } # Carry out any daemon tests. @@ -71,7 +71,7 @@ sub latexml_tests { my $test = "$directory/$name"; SKIP: { skip("No file $test.xml", 1) - unless (-f "$test.xml"); + unless pathname_test_f("$test.xml"); my $ntests = ($directory =~ /runtimes/ ? 1 : 2); next unless check_requirements($test, $ntests, $$requires{'*'}, $$requires{$name}); daemon_ok($test, $directory, $options{generate}); @@ -296,7 +296,7 @@ sub daemon_ok { if (my $teststrings = process_xmlfile("$base.test.xml", $base)) { if (my $xmlstrings = process_xmlfile("$base.xml", $base)) { is_strings($teststrings, $xmlstrings, $base); } } - unlink "$base.test.xml" if -e "$base.test.xml"; + pathname_unlink("$base.test.xml") if pathname_test_e("$base.test.xml"); } else { #TODO: Skip 3 tests @@ -304,7 +304,7 @@ sub daemon_ok { pathname_chdir($dir); system(@invocation); pathname_chdir($current_dir); - move("$base.test.xml", "$base.xml") if -e "$base.test.xml"; + move("$base.test.xml", "$base.xml") if pathname_test_e("$base.test.xml"); } return; } @@ -312,7 +312,7 @@ sub read_options { my ($optionfile, $testname) = @_; my $opts = []; my $OPT; - if (open($OPT, "<", $optionfile)) { + if (pathname_openfile($OPT, "<", $optionfile)) { while (my $line = <$OPT>) { next if $line =~ /^#/; chomp($line); @@ -329,8 +329,8 @@ sub get_filecontent { my ($path, $testname) = @_; my $IN; my @lines; - if (-e $path) { - if (!open($IN, "<", $path)) { + if (pathname_test_e($path)) { + if (!pathname_openfile($IN, "<", $path)) { do_fail($testname, "Could not open $path"); } else { { local $\ = undef; @@ -356,7 +356,7 @@ sub texlive_version { my $tex; # If kpsewhich specified, look for tex next to it if (my $path = $ENV{LATEXML_KPSEWHICH}) { - if (($path =~ s/kpsewhich/tex/i) && (-X $path)) { + if ($path =~ s/kpsewhich/tex/i && pathname_test_X($path)) { $tex = $path; } } if (!$tex) { # Else look for executable $tex = which("tex"); }