1 package FileUtil; 2 # 3 # $RCSfile: FileUtil.pm,v $ 4 # $Date: 2008/04/25 00:00:45 $ 5 # $Revision: 1.24 $ 6 # 7 # Author: Manish Sud <msud@san.rr.com> 8 # 9 # Copyright (C) 2004-2008 Manish Sud. All rights reserved. 10 # 11 # This file is part of MayaChemTools. 12 # 13 # MayaChemTools is free software; you can redistribute it and/or modify it under 14 # the terms of the GNU Lesser General Public License as published by the Free 15 # Software Foundation; either version 3 of the License, or (at your option) any 16 # later version. 17 # 18 # MayaChemTools is distributed in the hope that it will be useful, but without 19 # any warranty; without even the implied warranty of merchantability of fitness 20 # for a particular purpose. See the GNU Lesser General Public License for more 21 # details. 22 # 23 # You should have received a copy of the GNU Lesser General Public License 24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or 25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, 26 # Boston, MA, 02111-1307, USA. 27 # 28 use 5.006; 29 use strict; 30 use Exporter; 31 use Carp; 32 use File::stat; 33 use Time::localtime (); 34 use TextUtil (); 35 36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 37 38 $VERSION = '1.00'; 39 @ISA = qw(Exporter); 40 @EXPORT = qw(CheckFileType ConvertCygwinPath ExpandFileNames FileModificationTimeAndDate FormattedFileModificationTimeAndDate FileSize FormatFileSize GetMayaChemToolsLibDirName GetUsageFromPod ParseFileName); 41 @EXPORT_OK = qw(); 42 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 43 44 # Check to see path contains cygdrive and convert it into windows path... 45 sub ConvertCygwinPath { 46 my($Path) = @_; 47 my($NewPath, $OSName); 48 49 $NewPath = $Path; $OSName = $^O; 50 if ($OSName =~ /cygwin/i && $Path =~ /cygdrive/i ) { 51 my(@PathParts) = split "\/", $Path; 52 my($Drive) = $PathParts[2]; 53 shift @PathParts; shift @PathParts; shift @PathParts; 54 $NewPath = join "\/", @PathParts; 55 $NewPath = $Drive. ":\/" . $NewPath; 56 } 57 return $NewPath; 58 } 59 60 # Based on the file name extension, figure out its type. 61 sub CheckFileType { 62 my($FileName, $FileExts) = @_; 63 my($Status, @FileExtsList, $Index, $Ext); 64 65 $Status = 0; 66 @FileExtsList = split " ", $FileExts; 67 for $Index (0 .. $#FileExtsList) { 68 $Ext = $FileExtsList[$Index]; 69 if ($FileName =~ /(\.$Ext)$/i) { 70 $Status = 1; 71 } 72 } 73 return ($Status); 74 } 75 76 # For any directory name specified in Files array reference parameter, generate 77 # all files names in that directory which correspond to the extensions specified. 78 # And combine them with rest of the names. Return the expanded list. 79 sub ExpandFileNames { 80 my($Files, $FileExts) = @_; 81 my($FileName, @FilesList, $Index, $Delimiter); 82 83 for ($Index = 0; $Index < @$Files; $Index++) { 84 $FileName = @$Files[$Index]; 85 $Delimiter = "\/"; 86 if ($FileName =~ /\\/ ) { 87 $Delimiter = "\\"; 88 } 89 if (-d $FileName && $FileExts) { 90 my($DirName) = $FileName; 91 # glob doesn't appear to work during command line invocation from Windows... 92 # So, use opendir to make it work... 93 # 94 # push @FilesList, map {glob("$DirName/*.$_")} split " ", $FileExts; 95 if (opendir DIRNAME, $DirName) { 96 # Collect files with specified file extensions... 97 my($FileExtsPattern) = join "|", map { "\.$_" } split " ", $FileExts; 98 push @FilesList, grep /$FileExtsPattern/, map { "$DirName$Delimiter$_" } readdir DIRNAME; 99 closedir DIRNAME; 100 } 101 else { 102 carp "Warning: Ignoring directory $DirName: Couldn't open it: $! ..."; 103 } 104 } 105 elsif ($FileName =~ /\*/) { 106 # Filenames are not expanded during command line invocation from Windows... 107 my($FileDir, $Name, $FileExt) = ParseFileName($FileName); 108 if (opendir FILEDIR, $FileDir) { 109 if (length($Name) == 1) { 110 push @FilesList, grep /\.$FileExt/, map { "$FileDir$Delimiter$_" } readdir FILEDIR; 111 } 112 else { 113 push @FilesList, grep /\.$FileExt/, grep /$Name/, map { "$FileDir$Delimiter$_" } readdir FILEDIR; 114 } 115 closedir FILEDIR; 116 } 117 else { 118 carp "Warning: Ignoring files $FileName: Couldn't open directory $FileDir: $! ..."; 119 } 120 } 121 else { 122 push @FilesList, $FileName; 123 } 124 } 125 return @FilesList; 126 } 127 128 # Formatted file modification time... 129 sub FormattedFileModificationTimeAndDate { 130 my($FileName) = @_; 131 my($TimeString, $DateString) = ('') x 2; 132 133 if (! -e $FileName) { 134 return ($TimeString, $DateString); 135 } 136 my($Hours, $Mins, $Secs, $DayName, $MonthName, $Month, $Year) = FileModificationTimeAndDate($FileName); 137 138 # Setup time suffix... 139 my($TimeSuffix) = ''; 140 if ($Hours < 12) { 141 $TimeSuffix = 'AM'; 142 } 143 elsif ($Hours > 12) { 144 $TimeSuffix = 'PM'; 145 $Hours = $Hours - 12; 146 } 147 elsif ($Hours == 12 && ($Mins > 0 || $Secs > 0)) { 148 $TimeSuffix = 'PM'; 149 } 150 elsif ($Hours == 12 && $Mins == 0 && $Secs == 0) { 151 $TimeSuffix = 'Noon'; 152 } 153 154 $Month = TextUtil::AddNumberSuffix($Month); 155 156 $TimeString = "${DayName} ${Hours}:${Mins}:${Secs} ${TimeSuffix}"; 157 $DateString = "${MonthName} ${Month}, ${Year}"; 158 159 return ($TimeString, $DateString); 160 } 161 162 # File modifcation time and date... 163 sub FileModificationTimeAndDate { 164 my($FileName) = @_; 165 my($Hours, $Mins, $Secs, $DayName, $MonthName, $Month, $Year) = ('') x 7; 166 167 if (! -e $FileName) { 168 return ($Hours, $Mins, $Secs, $DayName, $MonthName, $Month, $Year); 169 } 170 171 my($CTimeString, $FileStatRef, $TimeStamp); 172 $FileStatRef = stat($FileName); 173 174 $CTimeString = Time::localtime::ctime($FileStatRef->mtime); 175 176 # ctime returns: Thu Aug 3 10:13:53 2006 177 ($DayName, $MonthName, $Month, $TimeStamp, $Year) = split /[ ]+/, $CTimeString; 178 ($Hours, $Mins, $Secs) = split /\:/, $TimeStamp; 179 180 return ($Hours, $Mins, $Secs, $DayName, $MonthName, $Month, $Year); 181 } 182 183 # Format file size... 184 sub FormatFileSize { 185 my($Precision, $Size); 186 187 $Precision = 1; 188 if (@_ == 2) { 189 ($Size, $Precision) = @_; 190 } 191 else { 192 ($Size) = @_; 193 } 194 my($SizeDenominator, $SizeSuffix); 195 FORMAT: { 196 if ($Size < 1024) { $SizeDenominator = 1; $SizeSuffix = 'bytes'; last FORMAT;} 197 if ($Size < (1024*1024)) { $SizeDenominator = 1024; $SizeSuffix = 'KB'; last FORMAT;} 198 if ($Size < (1024*1024*1024)) { $SizeDenominator = 1024*1024; $SizeSuffix = 'MB'; last FORMAT;} 199 if ($Size < (1024*1024*1024*1024)) { $SizeDenominator = 1024*1024*1024; $SizeSuffix = 'GB'; last FORMAT;} 200 $SizeDenominator = 1; $SizeSuffix = 'bytes'; 201 } 202 $Size /= $SizeDenominator; 203 $Size = sprintf("%.${Precision}f", $Size) + 0; 204 $Size = "$Size $SizeSuffix"; 205 206 return $Size; 207 } 208 209 # Get file size in bytes... 210 sub FileSize { 211 my($File) = @_; 212 213 if (! -e $File) { 214 return undef; 215 } 216 return (-s $File) 217 } 218 219 # Get MayaChemTool's lib directory name using @INC to extract 220 # <MAYACHEMTOOLS>/lib directory location: first entry in @INC path should contain 221 # MayaChemTools modules location 222 # 223 sub GetMayaChemToolsLibDirName { 224 my($MayaChemToolsLibDir); 225 226 $MayaChemToolsLibDir = ""; 227 if ($INC[0] =~ /MayaChemTools/i) { 228 $MayaChemToolsLibDir = $INC[0]; 229 } 230 else { 231 # Go through rest of the entries... 232 my($Index); 233 INDEX: for $Index (1 .. $#INC) { 234 if ($INC[$Index] =~ /MayaChemTools/i) { 235 $MayaChemToolsLibDir = $INC[$Index]; 236 last INDEX; 237 } 238 } 239 if (!$MayaChemToolsLibDir) { 240 carp "Warning: MayaChemTools lib directory location doesn't appear to exist in library search path specified by \@INC ..."; 241 } 242 } 243 return $MayaChemToolsLibDir; 244 } 245 246 # Get Usage from Pod... 247 sub GetUsageFromPod { 248 my($Usage, $ScriptPath); 249 250 ($ScriptPath) = @_; 251 $Usage = "Script usage not available: pod2text or pod2text.bat doesn't exist in your Perl installation and direct invocation of Pod::Text also failed\n"; 252 253 # Get pod documentation: try pod2text first followed by perdoc.bat in case it fails to 254 # to handle ActiveState Perl... 255 my($PodStatus); 256 $PodStatus = (open CMD, "pod2text $ScriptPath|") ? 1 : ((open CMD, "pod2text.bat $ScriptPath|") ? 1 : 0); 257 if (!$PodStatus) { 258 # Try direct invocation of Pod::Text before giving up... 259 my($PodTextCmd); 260 $PodTextCmd = "perl -e \'use Pod::Text (); \$TextFormatter = Pod::Text->new(); \$TextFormatter->parse_from_file(\"$ScriptPath\");\'"; 261 $PodStatus = (open CMD, "$PodTextCmd|") ? 1 : 0; 262 if (!$PodStatus) { 263 return $Usage; 264 } 265 } 266 my($ProcessingSection, $InParametersSection, $InOptionsSection, @LineWords); 267 $ProcessingSection = 0; $InParametersSection = 0; $InOptionsSection = 0; 268 PODLINE: while (<CMD>) { 269 if (/^SYNOPSIS/) { 270 $_ = <CMD>; chomp; s/^ +//g; 271 (@LineWords) = split / /; 272 $Usage = qq(Usage: $LineWords[0] [-options]... ); 273 shift @LineWords; 274 $Usage .= join(" ", @LineWords) . "\n"; 275 } 276 elsif (/^(DESCRIPTION|PARAMETERS|OPTIONS|EXAMPLES|AUTHOR|SEE ALSO|COPYRIGHT)/i) { 277 # Various sections... 278 chomp; 279 $Usage .= ucfirst(lc($_)) . ":\n"; 280 $ProcessingSection = 1; 281 $InOptionsSection = /^OPTIONS/ ? 1 : 0; 282 $InParametersSection = /^PARAMETERS/ ? 1 : 0; 283 } 284 elsif ($InParametersSection|$InOptionsSection) { 285 if (/^[ ]+\-/ || /^[ ]{4,4}/) { 286 # Start of option line: any number of spaces followed by - sign. 287 # Put back in <> which pod2text replaced to ** 288 my($OptionLine) = qq($_); 289 OPTIONLINE: while (<CMD>) { 290 if (/^( )/) { 291 $OptionLine .= qq($_); 292 } 293 else { 294 $OptionLine =~ s/\*(([a-zA-Z0-9])|(\[)|(\#)|(\"))/"\<" . substr($&, -1, 1)/e; 295 $OptionLine =~ s/(([a-zA-Z0-9])|(\])|(\#)|(\"))\*/substr($&, 0, 1) . "\>"/e; 296 $Usage .= qq($OptionLine$_); 297 last OPTIONLINE; 298 } 299 } 300 } 301 } 302 else { 303 if ($ProcessingSection) { $Usage .= qq($_); } 304 } 305 } 306 close CMD; 307 308 # Take out **which pod2text puts in for <> 309 $Usage =~ s/\*(([a-zA-Z0-9;#-])|(\")|(\()|(\[)|(\.))/substr($&, -1, 1)/eg; 310 $Usage =~ s/(([a-zA-Z0-9;#-])|(\")|(\))|(\])|(\.))\*/substr($&, 0, 1)/eg; 311 312 return $Usage; 313 } 314 315 # Split full file name into directory path, file name, and the extension. 316 sub ParseFileName { 317 my($FullName) = @_; 318 my($FileDir, $FileName, $FileExt, @FullFileNameParts, @FileNameParts, $Delimiter); 319 320 $Delimiter = "\/"; 321 if ($FullName =~ /\\/ ) { 322 $Delimiter = "\\"; 323 $FullName =~ s/\\/\//g; 324 } 325 $FileDir = ""; $FileName = ""; $FileExt = ""; 326 @FullFileNameParts = (); @FileNameParts = (); 327 328 @FullFileNameParts = split "\/", $FullName; 329 @FileNameParts = split /\./, $FullFileNameParts[$#FullFileNameParts]; 330 331 # Setup file dir... 332 if (@FileNameParts == 1) { 333 $FileDir = $FullName; 334 } 335 else { 336 if (@FullFileNameParts == 1) { 337 $FileDir = "\.$Delimiter"; 338 } 339 else { 340 pop @FullFileNameParts; 341 $FileDir = join $Delimiter, @FullFileNameParts; 342 } 343 } 344 345 # Setup file name and ext... 346 if (@FileNameParts == 2) { 347 $FileName = $FileNameParts[0]; 348 $FileExt = $FileNameParts[1]; 349 } 350 elsif (@FileNameParts > 2) { 351 # Use the last entry as file extension and the rest for file name... 352 $FileExt = $FileNameParts[$#FileNameParts]; 353 pop @FileNameParts; 354 $FileName = join '.', @FileNameParts; 355 } 356 return ($FileDir, $FileName, $FileExt); 357 } 358