You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
594 lines
20 KiB
594 lines
20 KiB
2 years ago
|
#!/usr/bin/env perl
|
||
|
# Time-stamp: <2005-06-23 17:26:59 barre>
|
||
|
#
|
||
|
# Convert VTK headers to doxygen format
|
||
|
#
|
||
|
# roeim : Vetle Roeim <vetler@ifi.uio.no>
|
||
|
# barre : Sebastien Barre <sebastien@barre.nom.fr>
|
||
|
#
|
||
|
# 0.9 (barre) :
|
||
|
# - add --conds : add \cond...\endcond around public:, private:, protected:
|
||
|
#
|
||
|
# 0.83 (barre) :
|
||
|
# - add --stdout : print converted file to standard output
|
||
|
#
|
||
|
# 0.82 (barre) :
|
||
|
# - add --relativeto path : each file/directory to document is considered
|
||
|
# relative to 'path', where --to and --relativeto should be absolute
|
||
|
#
|
||
|
# 0.81 (barre) :
|
||
|
# - fix pb if both --to and path to the file to document were absolute
|
||
|
# - remove warning when date or revision not found
|
||
|
#
|
||
|
# 0.8 (barre) :
|
||
|
# - update to match the new VTK 4.0 tree
|
||
|
# - change default --dirs so that it can be launched from Utilities/Doxygen
|
||
|
# - change default --to so that it can be launched from Utilities/Doxygen
|
||
|
# - handle more .SECTION syntax
|
||
|
# - add group support (at last)
|
||
|
#
|
||
|
# 0.76 (barre) :
|
||
|
# - add 'parallel' to the default set of directories
|
||
|
#
|
||
|
# 0.75 (barre) :
|
||
|
# - change default --to to '../vtk-doxygen' to comply with Kitware's doxyfile
|
||
|
#
|
||
|
# 0.74 (barre) :
|
||
|
# - as doxygen now handles RCS/CVS tags of the form $word:text$, use them
|
||
|
#
|
||
|
# 0.73 (barre) :
|
||
|
# - change doxygen command style from \ to @ to match javadoc, autodoc, etc.
|
||
|
#
|
||
|
# 0.72 (barre) :
|
||
|
# - change default --to to '../vtk-dox'
|
||
|
#
|
||
|
# 0.71 (barre) :
|
||
|
# - fix O_TEXT flag problem
|
||
|
# - switch to Unix CR/LF format
|
||
|
#
|
||
|
# 0.7 (barre) :
|
||
|
# - change name
|
||
|
# - remove -c option
|
||
|
#
|
||
|
# 0.6 (barre) :
|
||
|
# - change (warning) default --to to '../vtk2' because I ruined my own
|
||
|
# VTK distrib too many times :(
|
||
|
# - add automatic creation of missing directory trees
|
||
|
# - add check for current OS (if Windows, do not perform tests based
|
||
|
# on stat()/idev/ino features)
|
||
|
#
|
||
|
# 0.5 (barre) :
|
||
|
# - better .SECTION handling
|
||
|
# - add support for empty lines in documentation block
|
||
|
# - fix problem with headers not corresponding to classes
|
||
|
# - change name to doc_header2doxygen (removed vtk_)
|
||
|
# - change '-s' (silent) to '-v' (verbose)
|
||
|
# - add function description reformatting
|
||
|
#
|
||
|
# 0.4 (barre) :
|
||
|
# - change /*! ... */ position upon request
|
||
|
# - add 'Date:' support as @date
|
||
|
# - add 'Version:' support as @version
|
||
|
# - add 'Thanks:' support as @par Thanks
|
||
|
#
|
||
|
# 0.3 (barre) :
|
||
|
# - fix various " // Description" spelling problems :)
|
||
|
#
|
||
|
# 0.2 (barre) :
|
||
|
# - fix problem with classes with no brief documentation
|
||
|
#
|
||
|
# 0.1 (barre) :
|
||
|
# - add Perl syntactic sugar, options...
|
||
|
# - add standard output (filter) mode (-c)
|
||
|
# - add silent mode (-s)
|
||
|
# - add update mode, convert only if newer (-u)
|
||
|
# - add conversion to another directory (--to)
|
||
|
# - add '.SECTION Caveats' support as @warning
|
||
|
# - add/fix '.SECTION whatever' support as @par
|
||
|
# - add default directories to process
|
||
|
#
|
||
|
# 0.0 (roeim)
|
||
|
# - first release (thanks to V. Roeim !)
|
||
|
|
||
|
|
||
|
use Carp;
|
||
|
use Cwd 'abs_path';
|
||
|
use Getopt::Long;
|
||
|
use Fcntl;
|
||
|
use File::Basename;
|
||
|
use File::Find;
|
||
|
use File::Path;
|
||
|
use Text::Wrap;
|
||
|
use strict;
|
||
|
|
||
|
my ($VERSION, $PROGNAME, $AUTHOR) = (0.9, $0, "Sebastien Barre et al.");
|
||
|
$PROGNAME =~ s/^.*[\\\/]//;
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Defaults (add options as you want: "verbose" => 1 for default verbose mode)
|
||
|
|
||
|
my %default =
|
||
|
(
|
||
|
dirs => ["../../Common",
|
||
|
"../../Filtering",
|
||
|
"../../GenericFiltering",
|
||
|
"../../GenericFiltering/Testing/Cxx",
|
||
|
"../../Graphics",
|
||
|
"../../GUISupport/MFC",
|
||
|
"../../GUISupport/Qt",
|
||
|
"../../Hybrid",
|
||
|
"../../Imaging",
|
||
|
"../../IO",
|
||
|
"../../Parallel",
|
||
|
"../../Patented",
|
||
|
"../../Rendering",
|
||
|
"../../VolumeRendering"],
|
||
|
relativeto => "",
|
||
|
temp => "doc_header2doxygen.tmp",
|
||
|
to => "../../../VTK-doxygen"
|
||
|
);
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Parse options
|
||
|
|
||
|
my %args;
|
||
|
Getopt::Long::Configure("bundling");
|
||
|
GetOptions (\%args, "help", "verbose|v", "update|u", "conds|c", "force|f", "temp=s", "to=s", "stdout", "relativeto=s");
|
||
|
|
||
|
print "$PROGNAME $VERSION, by $AUTHOR\n" if ! exists $args{"stdout"};
|
||
|
|
||
|
if (exists $args{"help"}) {
|
||
|
print <<"EOT";
|
||
|
Usage : $PROGNAME [--help] [--verbose|-v] [--update|-u] [--conds|-c] [--force|-f] [--temp file] [--to path] [--relativeto path] [files|directories...]
|
||
|
--help : this message
|
||
|
--verbose|-v : verbose (display filenames while processing)
|
||
|
--update|-u : update (convert only if newer, requires --to)
|
||
|
--force|-f : force conversion for all files (overrides --update)
|
||
|
--stdout : print converted file to standard output
|
||
|
--temp file : use 'file' as temporary file (default: $default{temp})
|
||
|
--to path : use 'path' as destination directory (default: $default{to})
|
||
|
--relativeto path : each file/directory to document is considered relative to 'path', where --to and --relativeto should be absolute (default: $default{relativeto})
|
||
|
--conds|-c : use \cond sections around public, protected, private
|
||
|
|
||
|
Example:
|
||
|
$PROGNAME --to ../vtk-doxygen
|
||
|
$PROGNAME contrib
|
||
|
EOT
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
$args{"verbose"} = 1 if exists $default{"verbose"};
|
||
|
$args{"update"} = 1 if exists $default{"update"};
|
||
|
$args{"conds"} = 1 if exists $default{"conds"};
|
||
|
$args{"force"} = 1 if exists $default{"force"};
|
||
|
$args{"temp"} = $default{temp} if ! exists $args{"temp"};
|
||
|
$args{"to"} = $default{"to"} if ! exists $args{"to"};
|
||
|
$args{"to"} =~ s/[\\\/]*$// if exists $args{"to"};
|
||
|
$args{"relativeto"} = $default{"relativeto"} if ! exists $args{"relativeto"};
|
||
|
$args{"relativeto"} =~ s/[\\\/]*$// if exists $args{"relativeto"};
|
||
|
|
||
|
croak "$PROGNAME: --update requires --to\n"
|
||
|
if exists $args{"update"} && ! exists $args{"to"};
|
||
|
|
||
|
my $os_is_win = ($^O =~ m/(MSWin32|Cygwin)/i);
|
||
|
my $open_file_as_text = $os_is_win ? O_TEXT : 0;
|
||
|
my $start_time = time();
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Collect all files and directories
|
||
|
|
||
|
push @ARGV, @{$default{dirs}} if !@ARGV;
|
||
|
|
||
|
print "Collecting...\n" if ! exists $args{"stdout"};
|
||
|
my @files;
|
||
|
foreach my $file (@ARGV) {
|
||
|
if (-f $file) {
|
||
|
push @files, $file;
|
||
|
} elsif (-d $file) {
|
||
|
find sub { push @files, $File::Find::name; }, $file;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# Process files corresponding to headers
|
||
|
|
||
|
print "Converting...\n" if ! exists $args{"stdout"};
|
||
|
my $intermediate_time = time();
|
||
|
my $nb_file = 0;
|
||
|
|
||
|
foreach my $source (@files) {
|
||
|
|
||
|
next if $source !~ /vtk[^\\\/]*\.h\Z/;
|
||
|
|
||
|
# Figure out destination file now
|
||
|
|
||
|
my $dest;
|
||
|
if (! exists $args{"to"}) {
|
||
|
$dest = $args{"temp"};
|
||
|
} else {
|
||
|
# if source has absolute path, just use the basename, unless a
|
||
|
# relativeto path has been set
|
||
|
if ($source =~ m/^(\/|[a-zA-W]\:[\/\\])/) {
|
||
|
if ($args{"relativeto"}) {
|
||
|
my ($dir, $absrel) = (abs_path(dirname($source)),
|
||
|
abs_path($args{"relativeto"}));
|
||
|
$dir =~ s/$absrel//;
|
||
|
$dest = $args{"to"} . $dir . '/' . basename($source);
|
||
|
} else {
|
||
|
$dest = $args{"to"} . '/' . basename($source);
|
||
|
}
|
||
|
} else {
|
||
|
my $source2 = $source;
|
||
|
# let's remove the ../ component before the source filename, so
|
||
|
# that it might be appended to the "to" directory
|
||
|
$source2 =~ s/^(\.\.[\/\\])*//;
|
||
|
$dest = $args{"to"} . '/' . $source2;
|
||
|
}
|
||
|
# Ensure both source and target are different
|
||
|
if (!$os_is_win) {
|
||
|
my ($i_dev, $i_ino) = stat $source;
|
||
|
my ($o_dev, $o_ino) = stat $dest;
|
||
|
croak "$PROGNAME: sorry, $source and $dest are the same file\n"
|
||
|
if ($i_dev == $o_dev && $i_ino == $o_ino);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Update mode : skip the file if it is not newer than the
|
||
|
# previously converted target
|
||
|
|
||
|
if (exists $args{"update"} && ! exists $args{"force"}) {
|
||
|
next if -e $dest && (stat $source)[9] < (stat $dest)[9];
|
||
|
}
|
||
|
|
||
|
++$nb_file;
|
||
|
print " $source\n" if exists $args{"verbose"};
|
||
|
|
||
|
# Open file, feed it entirely to an array
|
||
|
|
||
|
sysopen(HEADERFILE, $source, O_RDONLY|$open_file_as_text)
|
||
|
or croak "$PROGNAME: unable to open $source\n";
|
||
|
my @headerfile = <HEADERFILE>;
|
||
|
close(HEADERFILE);
|
||
|
|
||
|
my ($date, $revision) = ("", "");
|
||
|
my @converted = ();
|
||
|
my @thanks = ();
|
||
|
|
||
|
# Parse the file until the beginning of the documentation block
|
||
|
# is found. The copyright and disclaimer sections are parsed to
|
||
|
# extract the 'Date', 'Version' and 'Thanks' values.
|
||
|
|
||
|
my $line;
|
||
|
while ($line = shift @headerfile) {
|
||
|
|
||
|
# Quit if the beginning of the documentation block has been reached.
|
||
|
# It is supposed to start with:
|
||
|
# // .NAME vtkFooBar - foo bar class
|
||
|
|
||
|
last if $line =~ /\/\/ \.NAME/;
|
||
|
|
||
|
# Date. Example:
|
||
|
# Date: $Date: 2005/06/23 21:48:34 $
|
||
|
|
||
|
if ($line =~ /^\s*Date:\s*(.*)$/) {
|
||
|
$date = $1;
|
||
|
|
||
|
# Version. Example:
|
||
|
# Version: $Revision: 1.13 $
|
||
|
|
||
|
} elsif ($line =~ /^\s*Version:\s*(.*)$/) {
|
||
|
$revision = $1;
|
||
|
|
||
|
# Thanks (maybe multi-lines). Example:
|
||
|
# Thanks: Thanks to Sebastien Barre who developed this class.
|
||
|
|
||
|
} elsif ($line =~ /^\s*Thanks:\s*(.*)$/) {
|
||
|
push @thanks, " ", $1, "\n";
|
||
|
# Handle multi-line thanks
|
||
|
while ($line = shift @headerfile) {
|
||
|
last if $line =~ /^\s*$/;
|
||
|
$line =~ s/^(\s*)//;
|
||
|
push @thanks, " ", $line;
|
||
|
}
|
||
|
push @converted, $line;
|
||
|
|
||
|
# Everything else goes to the converted file
|
||
|
|
||
|
} else {
|
||
|
push @converted, $line;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Process the documentation block
|
||
|
# Extract the name of the class and its short description
|
||
|
# // .NAME vtkFooBar - foo bar class
|
||
|
|
||
|
if (defined($line) && $line =~ /\/\/ \.NAME (\w*)( \- (.*))?/) {
|
||
|
|
||
|
my ($class_name, $class_short_description) = ($1, $3);
|
||
|
$class_name =~ s/\.h//;
|
||
|
|
||
|
# Insert class description, date, revision, thanks
|
||
|
|
||
|
push @converted, "/*! \@class $class_name\n";
|
||
|
push @converted, " \@brief $class_short_description\n"
|
||
|
if $class_short_description;
|
||
|
|
||
|
if ($date) {
|
||
|
push @converted, "\n $date\n";
|
||
|
}
|
||
|
|
||
|
# WARNING : need a blank line between RCS tags and previous dox tag
|
||
|
|
||
|
if ($revision) {
|
||
|
push @converted, "\n" if (!$date);
|
||
|
push @converted, " $revision\n";
|
||
|
}
|
||
|
|
||
|
# Do not add thanks anymore. Will be done externally.
|
||
|
# push @converted, " \@par Thanks:\n", @thanks if @thanks;
|
||
|
|
||
|
# Read until the end of the documentation block is reached
|
||
|
# Translate 'See Also', 'Caveats' and whatever .SECTION
|
||
|
# As of 24 sep 2001, there are:
|
||
|
# 137 // .SECTION Caveats
|
||
|
# 1 // .SECTION Credits
|
||
|
# 702 // .SECTION Description
|
||
|
# 3 // .SECTION Note
|
||
|
# 1 // .SECTION note
|
||
|
# 329 // .SECTION See Also
|
||
|
# 4 // .SECTION See also
|
||
|
# 70 // .SECTION see also
|
||
|
# 1 // .SECTION Warning
|
||
|
# find . -name vtk\*.h -exec grep "\.SECTION" {} \; | sort | uniq -c
|
||
|
# Let's provide support for bugs too:
|
||
|
# // .SECTION Bug
|
||
|
# // .SECTION Bugs
|
||
|
# // .SECTION Todo
|
||
|
|
||
|
my ($tag, $inblock) = ("", 0);
|
||
|
while ($line = shift @headerfile) {
|
||
|
|
||
|
# Quit if the end of the documentation block has been reached.
|
||
|
# Let'say that it is supposed to end as soon as the usual
|
||
|
# inclusion directives are found, for example:
|
||
|
# #ifndef __vtkAbstractTransform_h
|
||
|
# #define __vtkAbstractTransform_h
|
||
|
|
||
|
last if $line =~ /^\#/;
|
||
|
|
||
|
# Process and recognize a .SECTION command and convert it to
|
||
|
# the corresponding doxygen tag ($tag)
|
||
|
|
||
|
if ($line =~ /^\/\/\s+\.SECTION\s+(.+)\s*$/i) {
|
||
|
|
||
|
my $type = $1;
|
||
|
|
||
|
# Bugs (@bugs). Starts with:
|
||
|
# // .SECTION Bug
|
||
|
# // .SECTION Bugs
|
||
|
|
||
|
if ($type =~ /Bugs?/i) {
|
||
|
$tag = "\@bug";
|
||
|
}
|
||
|
|
||
|
# Caveats or Warnings (@warning). Starts with:
|
||
|
# // .SECTION Caveats
|
||
|
# // .SECTION Warning
|
||
|
# // .SECTION Warnings
|
||
|
|
||
|
elsif ($type =~ /(Caveats|Warnings?)/i) {
|
||
|
$tag = "\@warning";
|
||
|
}
|
||
|
|
||
|
# Description. Starts with:
|
||
|
# // .SECTION Description
|
||
|
|
||
|
elsif ($type =~ /Description/i) {
|
||
|
$tag = "";
|
||
|
push @converted, "\n";
|
||
|
}
|
||
|
|
||
|
# Note (@attention). Starts with:
|
||
|
# // .SECTION Note
|
||
|
|
||
|
elsif ($type =~ /Note/i) {
|
||
|
$tag = "\@attention";
|
||
|
}
|
||
|
|
||
|
# See also (@sa). Starts with:
|
||
|
# // .SECTION See Also
|
||
|
|
||
|
elsif ($type =~ /See Also/i) {
|
||
|
$tag = "\@sa";
|
||
|
}
|
||
|
|
||
|
# Todo (@todo). Starts with:
|
||
|
# // .SECTION Todo
|
||
|
|
||
|
elsif ($type =~ /Todo/i) {
|
||
|
$tag = "\@todo";
|
||
|
}
|
||
|
|
||
|
# Any other .SECTION (@par). Starts with:
|
||
|
# // .SECTION whatever
|
||
|
|
||
|
else {
|
||
|
$tag = "\@par " . $type . ":";
|
||
|
}
|
||
|
|
||
|
$inblock = 0;
|
||
|
}
|
||
|
|
||
|
# If the line starts with '//', we are still within the tag block.
|
||
|
# Remove '//' for non empty lines, eventually put or duplicate
|
||
|
# the tag name if an empty comment is found (meaning that a new
|
||
|
# 'paragraph' is requested but with the same tag type)
|
||
|
# Example:
|
||
|
# // .SECTION Caveats
|
||
|
# // blabla1q
|
||
|
# // blabla1b
|
||
|
# //
|
||
|
# // blabla2
|
||
|
# Gets translated into:
|
||
|
# @warning
|
||
|
# blabla1q
|
||
|
# blabla1b
|
||
|
#
|
||
|
# @warning
|
||
|
# blabla2
|
||
|
|
||
|
elsif ($line =~ /^\/\/(.*)/) {
|
||
|
my $remaining = $1;
|
||
|
if ($remaining =~ /\S/) {
|
||
|
push @converted, " $tag\n"
|
||
|
if $tag ne "" && ! $inblock;
|
||
|
push @converted, $remaining, "\n";
|
||
|
$inblock = 1;
|
||
|
} else {
|
||
|
push @converted, "\n";
|
||
|
$inblock = 0;
|
||
|
}
|
||
|
} else {
|
||
|
# Does not starts with // but still within block or just
|
||
|
# before the end (#). Probably an empty line.
|
||
|
# Hack : let's have a look at the next line... if it begins
|
||
|
# with // then the current line is included (was a space).
|
||
|
|
||
|
if (my $next_line = shift @headerfile) {
|
||
|
push @converted, $line if $next_line =~ /^\/\//;
|
||
|
unshift @headerfile, $next_line;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Close the doxygen documentation block describing the class
|
||
|
|
||
|
push @converted, "*/\n\n", $line;
|
||
|
}
|
||
|
|
||
|
# Read until the end of the header and translate the description of
|
||
|
# each function provided that it is located in a C++ comment
|
||
|
# containing the 'Description:' keyword.
|
||
|
# Example:
|
||
|
# // Description:
|
||
|
# // Construct with automatic computation of divisions, averaging
|
||
|
# // 25 points per bucket.
|
||
|
# static vtkPointLocator2D *New();
|
||
|
|
||
|
my $in_section = "";
|
||
|
|
||
|
while ($line = shift @headerfile) {
|
||
|
|
||
|
# Track the public:, protected: and private: sections and put them
|
||
|
# between \cond... \endcond so that they can be removed from the
|
||
|
# documentation conditionally. Add them to ENABLED_SECTION
|
||
|
# to show them.
|
||
|
# IMPORTANT: *no* spaces are allowed between the beginning of the
|
||
|
# line and the qualifier. This is mandatory to solve issues
|
||
|
# with nested class definitions, since it is non-trivial to
|
||
|
# track the fact that we are leaving a class definition to
|
||
|
# re-enter the parent class definition, etc.
|
||
|
|
||
|
if (exists $args{"conds"}) {
|
||
|
if ($line =~ /^(public|protected|private):/) {
|
||
|
if ($in_section ne "") {
|
||
|
push @converted, "// \@endcond\n";
|
||
|
}
|
||
|
$in_section = $1;
|
||
|
push @converted, "// \@cond section_$in_section\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($line =~ /^(\s*)\/\/\s*De(s|c)(s|c)?ription/) {
|
||
|
|
||
|
my $indent = $1;
|
||
|
$Text::Wrap::columns = 76;
|
||
|
|
||
|
# While there are still lines beginning with '//' append them to
|
||
|
# the function's description and trim spaces.
|
||
|
|
||
|
my @description = ();
|
||
|
while ($line = shift @headerfile) {
|
||
|
last if $line !~ /^\s*\/\//;
|
||
|
chop $line;
|
||
|
$line =~ s/^\s*\/\/\s*//;
|
||
|
$line =~ s/\s*$//;
|
||
|
push @description, $line;
|
||
|
}
|
||
|
|
||
|
# While there are non-empty lines add these lines to the
|
||
|
# list of declarations (and/or inline definitions)
|
||
|
# pertaining to the same description.
|
||
|
|
||
|
my @declarations = ();
|
||
|
while ($line && $line =~ /\s+\S/) {
|
||
|
push @declarations, $line;
|
||
|
$line = shift @headerfile
|
||
|
}
|
||
|
|
||
|
# If there is more than one declaration or at least a macro,
|
||
|
# enclose in a group (actually a single multiline declaration will
|
||
|
# be enclosed too, but who cares :)...
|
||
|
|
||
|
my $enclose =
|
||
|
(scalar @declarations > 1 || $declarations[0] =~ /vtk.+Macro/);
|
||
|
|
||
|
push @converted, "$indent//@\{\n" if $enclose;
|
||
|
push @converted,
|
||
|
wrap("$indent/*! ", "$indent ", @description), " */\n"
|
||
|
if @description;
|
||
|
push @converted, @declarations;
|
||
|
push @converted, "$indent//@\}\n" if $enclose;
|
||
|
}
|
||
|
|
||
|
push @converted, $line;
|
||
|
}
|
||
|
|
||
|
if (exists $args{"conds"}) {
|
||
|
if ($in_section ne "") {
|
||
|
push @converted, "// \@endcond";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Write the converted header to its destination
|
||
|
# or to standard output.
|
||
|
|
||
|
if (exists $args{"stdout"}) {
|
||
|
|
||
|
print @converted;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
# Open the target and create the missing directory if any
|
||
|
|
||
|
if (!sysopen(DEST_FILE,
|
||
|
$dest,
|
||
|
O_WRONLY|O_TRUNC|O_CREAT|$open_file_as_text)) {
|
||
|
my $dir = dirname($dest);
|
||
|
mkpath($dir);
|
||
|
sysopen(DEST_FILE,
|
||
|
$dest,
|
||
|
O_WRONLY|O_TRUNC|O_CREAT|$open_file_as_text)
|
||
|
or croak "$PROGNAME: unable to open destination file $dest\n";
|
||
|
}
|
||
|
print DEST_FILE @converted;
|
||
|
close(DEST_FILE);
|
||
|
|
||
|
# If in-place conversion was requested, remove source and rename target
|
||
|
# (or temp file) to source
|
||
|
|
||
|
if (! exists $args{"to"}) {
|
||
|
unlink($source)
|
||
|
or carp "$PROGNAME: unable to delete original file $source\n";
|
||
|
rename($args{"temp"}, $source)
|
||
|
or carp "$PROGNAME: unable to rename ", $args{"temp"}, " to $source\n";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (! exists $args{"stdout"}) {
|
||
|
print " => $nb_file files converted in ", time() - $intermediate_time, " s. \n";
|
||
|
print "Finished in ", time() - $start_time, " s.\n";
|
||
|
}
|