Path.pm 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. # Copyright © 2007-2011 Raphaël Hertzog <hertzog@debian.org>
  2. # Copyright © 2011 Linaro Limited
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. package Dpkg::Path;
  17. use strict;
  18. use warnings;
  19. our $VERSION = "1.02";
  20. use base qw(Exporter);
  21. use File::Spec;
  22. use Cwd qw(realpath);
  23. use Dpkg::IPC;
  24. our @EXPORT_OK = qw(get_pkg_root_dir relative_to_pkg_root
  25. guess_pkg_root_dir check_files_are_the_same
  26. resolve_symlink canonpath find_command
  27. get_control_path);
  28. =encoding utf8
  29. =head1 NAME
  30. Dpkg::Path - some common path handling functions
  31. =head1 DESCRIPTION
  32. It provides some functions to handle various path.
  33. =head1 METHODS
  34. =over 8
  35. =item get_pkg_root_dir($file)
  36. This function will scan upwards the hierarchy of directory to find out
  37. the directory which contains the "DEBIAN" sub-directory and it will return
  38. its path. This directory is the root directory of a package being built.
  39. If no DEBIAN subdirectory is found, it will return undef.
  40. =cut
  41. sub get_pkg_root_dir($) {
  42. my $file = shift;
  43. $file =~ s{/+$}{};
  44. $file =~ s{/+[^/]+$}{} if not -d $file;
  45. while ($file) {
  46. return $file if -d "$file/DEBIAN";
  47. last if $file !~ m{/};
  48. $file =~ s{/+[^/]+$}{};
  49. }
  50. return undef;
  51. }
  52. =item relative_to_pkg_root($file)
  53. Returns the filename relative to get_pkg_root_dir($file).
  54. =cut
  55. sub relative_to_pkg_root($) {
  56. my $file = shift;
  57. my $pkg_root = get_pkg_root_dir($file);
  58. if (defined $pkg_root) {
  59. $pkg_root .= "/";
  60. return $file if ($file =~ s/^\Q$pkg_root\E//);
  61. }
  62. return undef;
  63. }
  64. =item guess_pkg_root_dir($file)
  65. This function tries to guess the root directory of the package build tree.
  66. It will first use get_pkg_root_dir(), but it will fallback to a more
  67. imprecise check: namely it will use the parent directory that is a
  68. sub-directory of the debian directory.
  69. It can still return undef if a file outside of the debian sub-directory is
  70. provided.
  71. =cut
  72. sub guess_pkg_root_dir($) {
  73. my $file = shift;
  74. my $root = get_pkg_root_dir($file);
  75. return $root if defined $root;
  76. $file =~ s{/+$}{};
  77. $file =~ s{/+[^/]+$}{} if not -d $file;
  78. my $parent = $file;
  79. while ($file) {
  80. $parent =~ s{/+[^/]+$}{};
  81. last if not -d $parent;
  82. return $file if check_files_are_the_same("debian", $parent);
  83. $file = $parent;
  84. last if $file !~ m{/};
  85. }
  86. return undef;
  87. }
  88. =item check_files_are_the_same($file1, $file2, $resolve_symlink)
  89. This function verifies that both files are the same by checking that the device
  90. numbers and the inode numbers returned by stat()/lstat() are the same. If
  91. $resolve_symlink is true then stat() is used, otherwise lstat() is used.
  92. =cut
  93. sub check_files_are_the_same($$;$) {
  94. my ($file1, $file2, $resolve_symlink) = @_;
  95. return 0 if ((! -e $file1) || (! -e $file2));
  96. my (@stat1, @stat2);
  97. if ($resolve_symlink) {
  98. @stat1 = stat($file1);
  99. @stat2 = stat($file2);
  100. } else {
  101. @stat1 = lstat($file1);
  102. @stat2 = lstat($file2);
  103. }
  104. my $result = ($stat1[0] == $stat2[0]) && ($stat1[1] == $stat2[1]);
  105. return $result;
  106. }
  107. =item canonpath($file)
  108. This function returns a cleaned path. It simplifies double //, and remove
  109. /./ and /../ intelligently. For /../ it simplifies the path only if the
  110. previous element is not a symlink. Thus it should only be used on real
  111. filenames.
  112. =cut
  113. sub canonpath($) {
  114. my $path = shift;
  115. $path = File::Spec->canonpath($path);
  116. my ($v, $dirs, $file) = File::Spec->splitpath($path);
  117. my @dirs = File::Spec->splitdir($dirs);
  118. my @new;
  119. foreach my $d (@dirs) {
  120. if ($d eq '..') {
  121. if (scalar(@new) > 0 and $new[-1] ne "..") {
  122. next if $new[-1] eq ""; # Root directory has no parent
  123. my $parent = File::Spec->catpath($v,
  124. File::Spec->catdir(@new), '');
  125. if (not -l $parent) {
  126. pop @new;
  127. } else {
  128. push @new, $d;
  129. }
  130. } else {
  131. push @new, $d;
  132. }
  133. } else {
  134. push @new, $d;
  135. }
  136. }
  137. return File::Spec->catpath($v, File::Spec->catdir(@new), $file);
  138. }
  139. =item $newpath = resolve_symlink($symlink)
  140. Return the filename of the file pointed by the symlink. The new name is
  141. canonicalized by canonpath().
  142. =cut
  143. sub resolve_symlink($) {
  144. my $symlink = shift;
  145. my $content = readlink($symlink);
  146. return undef unless defined $content;
  147. if (File::Spec->file_name_is_absolute($content)) {
  148. return canonpath($content);
  149. } else {
  150. my ($link_v, $link_d, $link_f) = File::Spec->splitpath($symlink);
  151. my ($cont_v, $cont_d, $cont_f) = File::Spec->splitpath($content);
  152. my $new = File::Spec->catpath($link_v, $link_d . "/" . $cont_d, $cont_f);
  153. return canonpath($new);
  154. }
  155. }
  156. =item my $cmdpath = find_command($command)
  157. Return the path of the command if available on an absolute or relative
  158. path or on the $PATH, undef otherwise.
  159. =cut
  160. sub find_command($) {
  161. my $cmd = shift;
  162. if ($cmd =~ m{/}) {
  163. return "$cmd" if -x "$cmd";
  164. } else {
  165. foreach my $dir (split(/:/, $ENV{'PATH'})) {
  166. return "$dir/$cmd" if -x "$dir/$cmd";
  167. }
  168. }
  169. return undef;
  170. }
  171. =item my $control_file = get_control_path($pkg, $filetype)
  172. Return the path of the control file of type $filetype for the given
  173. package.
  174. =item my @control_files = get_control_path($pkg)
  175. Return the path of all available control files for the given package.
  176. =cut
  177. sub get_control_path($;$) {
  178. my ($pkg, $filetype) = @_;
  179. my $control_file;
  180. my @exec = ("dpkg-query", "--control-path", $pkg);
  181. push @exec, $filetype if defined $filetype;
  182. spawn(exec => \@exec, wait_child => 1, to_string => \$control_file);
  183. chomp($control_file);
  184. if (defined $filetype) {
  185. return undef if $control_file eq "";
  186. return $control_file;
  187. }
  188. return () if $control_file eq "";
  189. return split(/\n/, $control_file);
  190. }
  191. =back
  192. =head1 AUTHOR
  193. Raphaël Hertzog <hertzog@debian.org>.
  194. =cut
  195. 1;