FileUtils.pm 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. # Copyright (C) 2003-2012 Free Software Foundation, Inc.
  2. # This program is free software; you can redistribute it and/or modify
  3. # it under the terms of the GNU General Public License as published by
  4. # the Free Software Foundation; either version 2, or (at your option)
  5. # any later version.
  6. # This program is distributed in the hope that it will be useful,
  7. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. # GNU General Public License for more details.
  10. # You should have received a copy of the GNU General Public License
  11. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. ###############################################################
  13. # The main copy of this file is in Automake's git repository. #
  14. # Updates should be sent to automake-patches@gnu.org. #
  15. ###############################################################
  16. package Autom4te::FileUtils;
  17. =head1 NAME
  18. Autom4te::FileUtils - handling files
  19. =head1 SYNOPSIS
  20. use Autom4te::FileUtils
  21. =head1 DESCRIPTION
  22. This perl module provides various general purpose file handling functions.
  23. =cut
  24. use 5.006;
  25. use strict;
  26. use Exporter;
  27. use File::stat;
  28. use IO::File;
  29. use Autom4te::Channels;
  30. use Autom4te::ChannelDefs;
  31. use vars qw (@ISA @EXPORT);
  32. @ISA = qw (Exporter);
  33. @EXPORT = qw (&open_quote &contents
  34. &find_file &mtime
  35. &update_file &up_to_date_p
  36. &xsystem &xsystem_hint &xqx
  37. &dir_has_case_matching_file &reset_dir_cache
  38. &set_dir_cache_file);
  39. =item C<open_quote ($file_name)>
  40. Quote C<$file_name> for open.
  41. =cut
  42. # $FILE_NAME
  43. # open_quote ($FILE_NAME)
  44. # -----------------------
  45. # If the string $S is a well-behaved file name, simply return it.
  46. # If it starts with white space, prepend './', if it ends with
  47. # white space, add '\0'. Return the new string.
  48. sub open_quote($)
  49. {
  50. my ($s) = @_;
  51. if ($s =~ m/^\s/)
  52. {
  53. $s = "./$s";
  54. }
  55. if ($s =~ m/\s$/)
  56. {
  57. $s = "$s\0";
  58. }
  59. return $s;
  60. }
  61. =item C<find_file ($file_name, @include)>
  62. Return the first path for a C<$file_name> in the C<include>s.
  63. We match exactly the behavior of GNU M4: first look in the current
  64. directory (which includes the case of absolute file names), and then,
  65. if the file name is not absolute, look in C<@include>.
  66. If the file is flagged as optional (ends with C<?>), then return undef
  67. if absent, otherwise exit with error.
  68. =cut
  69. # $FILE_NAME
  70. # find_file ($FILE_NAME, @INCLUDE)
  71. # --------------------------------
  72. sub find_file ($@)
  73. {
  74. use File::Spec;
  75. my ($file_name, @include) = @_;
  76. my $optional = 0;
  77. $optional = 1
  78. if $file_name =~ s/\?$//;
  79. return File::Spec->canonpath ($file_name)
  80. if -e $file_name;
  81. if (!File::Spec->file_name_is_absolute ($file_name))
  82. {
  83. foreach my $path (@include)
  84. {
  85. return File::Spec->canonpath (File::Spec->catfile ($path, $file_name))
  86. if -e File::Spec->catfile ($path, $file_name)
  87. }
  88. }
  89. fatal "$file_name: no such file or directory"
  90. unless $optional;
  91. return undef;
  92. }
  93. =item C<mtime ($file)>
  94. Return the mtime of C<$file>. Missing files, or C<-> standing for
  95. C<STDIN> or C<STDOUT> are "obsolete", i.e., as old as possible.
  96. =cut
  97. # $MTIME
  98. # MTIME ($FILE)
  99. # -------------
  100. sub mtime ($)
  101. {
  102. my ($file) = @_;
  103. return 0
  104. if $file eq '-' || ! -f $file;
  105. my $stat = stat ($file)
  106. or fatal "cannot stat $file: $!";
  107. return $stat->mtime;
  108. }
  109. =item C<update_file ($from, $to, [$force])>
  110. Rename C<$from> as C<$to>, preserving C<$to> timestamp if it has not
  111. changed, unless C<$force> is true (defaults to false). Recognize
  112. C<$to> = C<-> standing for C<STDIN>. C<$from> is always
  113. removed/renamed.
  114. =cut
  115. # &update_file ($FROM, $TO; $FORCE)
  116. # ---------------------------------
  117. sub update_file ($$;$)
  118. {
  119. my ($from, $to, $force) = @_;
  120. $force = 0
  121. unless defined $force;
  122. my $SIMPLE_BACKUP_SUFFIX = $ENV{'SIMPLE_BACKUP_SUFFIX'} || '~';
  123. use File::Compare;
  124. use File::Copy;
  125. if ($to eq '-')
  126. {
  127. my $in = new IO::File ("< " . open_quote ($from));
  128. my $out = new IO::File (">-");
  129. while ($_ = $in->getline)
  130. {
  131. print $out $_;
  132. }
  133. $in->close;
  134. unlink ($from) || fatal "cannot remove $from: $!";
  135. return;
  136. }
  137. if (!$force && -f "$to" && compare ("$from", "$to") == 0)
  138. {
  139. # File didn't change, so don't update its mod time.
  140. msg 'note', "'$to' is unchanged";
  141. unlink ($from)
  142. or fatal "cannot remove $from: $!";
  143. return
  144. }
  145. if (-f "$to")
  146. {
  147. # Back up and install the new one.
  148. move ("$to", "$to$SIMPLE_BACKUP_SUFFIX")
  149. or fatal "cannot backup $to: $!";
  150. move ("$from", "$to")
  151. or fatal "cannot rename $from as $to: $!";
  152. msg 'note', "'$to' is updated";
  153. }
  154. else
  155. {
  156. move ("$from", "$to")
  157. or fatal "cannot rename $from as $to: $!";
  158. msg 'note', "'$to' is created";
  159. }
  160. }
  161. =item C<up_to_date_p ($file, @dep)>
  162. Is C<$file> more recent than C<@dep>?
  163. =cut
  164. # $BOOLEAN
  165. # &up_to_date_p ($FILE, @DEP)
  166. # ---------------------------
  167. sub up_to_date_p ($@)
  168. {
  169. my ($file, @dep) = @_;
  170. my $mtime = mtime ($file);
  171. foreach my $dep (@dep)
  172. {
  173. if ($mtime < mtime ($dep))
  174. {
  175. verb "up_to_date ($file): outdated: $dep";
  176. return 0;
  177. }
  178. }
  179. verb "up_to_date ($file): up to date";
  180. return 1;
  181. }
  182. =item C<handle_exec_errors ($command, [$expected_exit_code = 0], [$hint])>
  183. Display an error message for C<$command>, based on the content of
  184. C<$?> and C<$!>. Be quiet if the command exited normally
  185. with C<$expected_exit_code>. If C<$hint> is given, display that as well
  186. if the command failed to run at all.
  187. =cut
  188. sub handle_exec_errors ($;$$)
  189. {
  190. my ($command, $expected, $hint) = @_;
  191. $expected = 0 unless defined $expected;
  192. if (defined $hint)
  193. {
  194. $hint = "\n" . $hint;
  195. }
  196. else
  197. {
  198. $hint = '';
  199. }
  200. $command = (split (' ', $command))[0];
  201. if ($!)
  202. {
  203. fatal "failed to run $command: $!" . $hint;
  204. }
  205. else
  206. {
  207. use POSIX qw (WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG);
  208. if (WIFEXITED ($?))
  209. {
  210. my $status = WEXITSTATUS ($?);
  211. # Propagate exit codes.
  212. fatal ('',
  213. "$command failed with exit status: $status",
  214. exit_code => $status)
  215. unless $status == $expected;
  216. }
  217. elsif (WIFSIGNALED ($?))
  218. {
  219. my $signal = WTERMSIG ($?);
  220. fatal "$command terminated by signal: $signal";
  221. }
  222. else
  223. {
  224. fatal "$command exited abnormally";
  225. }
  226. }
  227. }
  228. =item C<xqx ($command)>
  229. Same as C<qx> (but in scalar context), but fails on errors.
  230. =cut
  231. # xqx ($COMMAND)
  232. # --------------
  233. sub xqx ($)
  234. {
  235. my ($command) = @_;
  236. verb "running: $command";
  237. $! = 0;
  238. my $res = `$command`;
  239. handle_exec_errors $command
  240. if $?;
  241. return $res;
  242. }
  243. =item C<xsystem (@argv)>
  244. Same as C<system>, but fails on errors, and reports the C<@argv>
  245. in verbose mode.
  246. =cut
  247. sub xsystem (@)
  248. {
  249. my (@command) = @_;
  250. verb "running: @command";
  251. $! = 0;
  252. handle_exec_errors "@command"
  253. if system @command;
  254. }
  255. =item C<xsystem_hint ($msg, @argv)>
  256. Same as C<xsystem>, but allows to pass a hint that will be displayed
  257. in case the command failed to run at all.
  258. =cut
  259. sub xsystem_hint (@)
  260. {
  261. my ($hint, @command) = @_;
  262. verb "running: @command";
  263. $! = 0;
  264. handle_exec_errors "@command", 0, $hint
  265. if system @command;
  266. }
  267. =item C<contents ($file_name)>
  268. Return the contents of C<$file_name>.
  269. =cut
  270. # contents ($FILE_NAME)
  271. # ---------------------
  272. sub contents ($)
  273. {
  274. my ($file) = @_;
  275. verb "reading $file";
  276. local $/; # Turn on slurp-mode.
  277. my $f = new Autom4te::XFile "< " . open_quote ($file);
  278. my $contents = $f->getline;
  279. $f->close;
  280. return $contents;
  281. }
  282. =item C<dir_has_case_matching_file ($DIRNAME, $FILE_NAME)>
  283. Return true iff $DIR contains a file name that matches $FILE_NAME case
  284. insensitively.
  285. We need to be cautious on case-insensitive case-preserving file
  286. systems (e.g. Mac OS X's HFS+). On such systems C<-f 'Foo'> and C<-f
  287. 'foO'> answer the same thing. Hence if a package distributes its own
  288. F<CHANGELOG> file, but has no F<ChangeLog> file, automake would still
  289. try to distribute F<ChangeLog> (because it thinks it exists) in
  290. addition to F<CHANGELOG>, although it is impossible for these two
  291. files to be in the same directory (the two file names designate the
  292. same file).
  293. =cut
  294. use vars '%_directory_cache';
  295. sub dir_has_case_matching_file ($$)
  296. {
  297. # Note that print File::Spec->case_tolerant returns 0 even on MacOS
  298. # X (with Perl v5.8.1-RC3 at least), so do not try to shortcut this
  299. # function using that.
  300. my ($dirname, $file_name) = @_;
  301. return 0 unless -f "$dirname/$file_name";
  302. # The file appears to exist, however it might be a mirage if the
  303. # system is case insensitive. Let's browse the directory and check
  304. # whether the file is really in. We maintain a cache of directories
  305. # so Automake doesn't spend all its time reading the same directory
  306. # again and again.
  307. if (!exists $_directory_cache{$dirname})
  308. {
  309. error "failed to open directory '$dirname'"
  310. unless opendir (DIR, $dirname);
  311. $_directory_cache{$dirname} = { map { $_ => 1 } readdir (DIR) };
  312. closedir (DIR);
  313. }
  314. return exists $_directory_cache{$dirname}{$file_name};
  315. }
  316. =item C<reset_dir_cache ($dirname)>
  317. Clear C<dir_has_case_matching_file>'s cache for C<$dirname>.
  318. =cut
  319. sub reset_dir_cache ($)
  320. {
  321. delete $_directory_cache{$_[0]};
  322. }
  323. =item C<set_dir_cache_file ($dirname, $file_name)>
  324. State that C<$dirname> contains C<$file_name> now.
  325. =cut
  326. sub set_dir_cache_file ($$)
  327. {
  328. my ($dirname, $file_name) = @_;
  329. $_directory_cache{$dirname}{$file_name} = 1
  330. if exists $_directory_cache{$dirname};
  331. }
  332. 1; # for require
  333. ### Setup "GNU" style for perl-mode and cperl-mode.
  334. ## Local Variables:
  335. ## perl-indent-level: 2
  336. ## perl-continued-statement-offset: 2
  337. ## perl-continued-brace-offset: 0
  338. ## perl-brace-offset: 0
  339. ## perl-brace-imaginary-offset: 0
  340. ## perl-label-offset: -2
  341. ## cperl-indent-level: 2
  342. ## cperl-brace-offset: 0
  343. ## cperl-continued-brace-offset: 0
  344. ## cperl-label-offset: -2
  345. ## cperl-extra-newline-before-brace: t
  346. ## cperl-merge-trailing-else: nil
  347. ## cperl-continued-statement-offset: 2
  348. ## End: