MayaChemTools

   1 #!/usr/bin/perl -w
   2 #
   3 # $RCSfile: ElementalAnalysisTextFiles.pl,v $
   4 # $Date: 2008/01/30 21:44:45 $
   5 # $Revision: 1.14 $
   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 
  29 use 5.006;
  30 use strict;
  31 use FindBin; use lib "$FindBin::Bin/../lib";
  32 use Getopt::Long;
  33 use File::Basename;
  34 use Text::ParseWords;
  35 use Benchmark;
  36 use FileUtil;
  37 use TextUtil;
  38 use MolecularFormula;
  39 
  40 my($ScriptName, %Options, $StartTime, $EndTime, $TotalTime);
  41 
  42 # Autoflush STDOUT
  43 $| = 1;
  44 
  45 # Starting message...
  46 $ScriptName = basename($0);
  47 print "\n$ScriptName: Starting...\n\n";
  48 $StartTime = new Benchmark;
  49 
  50 # Get the options and setup script...
  51 SetupScriptUsage();
  52 if ($Options{help} || @ARGV < 1) {
  53   die GetUsageFromPod("$FindBin::Bin/$ScriptName");
  54 }
  55 
  56 my(@TextFilesList);
  57 @TextFilesList = ExpandFileNames(\@ARGV, "csv tsv");
  58 
  59 my($DetailLevel, $OutDelim, $CheckFormula, $Precision, $OutQuote, $SpecifiedFormulaCol, @SpecifiedCalculations, @SpecifiedValueLabels);
  60 ProcessOptions();
  61 
  62 print "Checking input text file(s)...\n";
  63 my(@TextFilesOkay, @TextFilesColCount, @TextFilesColLabels, @TextFilesColLabelToNumMap, @TextFilesInDelim, @TextFilesOutFile);
  64 RetrieveTextFilesInfo();
  65 
  66 my(@TextFilesFormulaColNum);
  67 ProcessFormulaColumnsInfo();
  68 
  69 my(@TextFilesValueLabelsMap, @TextFilesColNumsBeforeNew, @TextFilesColNumsAfterNew);
  70 ProcessStartColumnsAndValueLabelsInfo();
  71 
  72 # Generate output files...
  73 my($Index, $TextFile);
  74 if (@TextFilesList > 1) {
  75   print "Processing text files...\n";
  76 }
  77 for $Index (0 .. $#TextFilesList) {
  78   if ($TextFilesOkay[$Index]) {
  79     $TextFile = $TextFilesList[$Index];
  80     if (@TextFilesList > 1) {
  81       print "\nProcessing file $TextFile...\n";
  82     }
  83     else {
  84       print "Processing file $TextFile...\n"
  85     }
  86     PerformElementalAnalysis($Index);
  87   }
  88 }
  89 
  90 print "$ScriptName:Done...\n\n";
  91 
  92 $EndTime = new Benchmark;
  93 $TotalTime = timediff ($EndTime, $StartTime);
  94 print "Total time: ", timestr($TotalTime), "\n";
  95 
  96 ###############################################################################
  97 
  98 # Process option values...
  99 sub ProcessOptions {
 100   $DetailLevel = $Options{detail};
 101   $CheckFormula = $Options{fast} ? 0 : 1;
 102   $Precision = $Options{precision};
 103 
 104   $OutDelim = ($Options{outdelim} =~ /^tab$/i ) ? "\t" : (($Options{outdelim} =~ /^semicolon$/i) ? "\;" : "\,");
 105   $OutQuote = ($Options{quote} =~ /^yes$/i) ? 1 : 0;
 106 
 107   $SpecifiedFormulaCol = "";
 108   if (defined $Options{formulacol}) {
 109     $SpecifiedFormulaCol = $Options{formulacol};
 110     if ($Options{colmode} =~ /^colnum$/i) {
 111       if (!IsPositiveInteger($SpecifiedFormulaCol)) {
 112 	die "Error: Invalid value $Options{formulacol} specified using \"-f -formulacol\" option: Allowed values: > 0\n";
 113       }
 114     }
 115   }
 116 
 117   # Setup what to calculate...
 118   @SpecifiedCalculations = ();
 119   if ($Options{mode} =~ /^All$/i) {
 120     @SpecifiedCalculations = qw(ElementalAnalysis MolecularWeight ExactMass);
 121   }
 122   else {
 123     my($Mode, $ModeValue, @SpecifiedModeValues);
 124     $Mode = $Options{mode};
 125     $Mode =~ s/ //g;
 126     @SpecifiedModeValues = split /\,/, $Mode;
 127     for $ModeValue (@SpecifiedModeValues) {
 128       if ($ModeValue !~ /^(ElementalAnalysis|MolecularWeight|ExactMass)$/i) {
 129 	if ($ModeValue =~ /^All$/i) {
 130 	  die "Error: All value for option \"-m --mode\" is not allowed with other valid values.\n";
 131 	}
 132 	else {
 133 	  die "Error: The value specified, $ModeValue, for option \"-m --mode\" is not valid. Allowed values: ElementalAnalysis, MolecularWeight, or ExactMass\n";
 134 	}
 135       }
 136       push @SpecifiedCalculations, $ModeValue;
 137     }
 138   }
 139 
 140   @SpecifiedValueLabels = ();
 141   if ($Options{valuecollabels}) {
 142     my($Value, $Label, @ValueLabels);
 143     @ValueLabels = split /\,/, $Options{valuecollabels};
 144     if (@ValueLabels % 2) {
 145       die "Error: The value specified, $Options{valuecollabels}, for option \"-v --valuecollabels\" is not valid: It must contain even number of comma delimited values\n";
 146     }
 147     for ($Index = 0; $Index < @ValueLabels; $Index +=2) {
 148       $Value = $ValueLabels[$Index];
 149       $Value =~ s/ //g;
 150       $Label = $ValueLabels[$Index + 1];
 151       if ($Value !~ /^(ElementalAnalysis|MolecularWeight|ExactMass)$/i) {
 152 	die "Error: The value specified, $Value, using option \"-v --valuecollabels\" is not valid. Allowed values: ElementalAnalysis, MolecularWeight, or ExactMass\n";
 153       }
 154       push @SpecifiedValueLabels, ($Value, $Label);
 155     }
 156   }
 157 }
 158 
 159 # Retrieve information about input text files...
 160 sub RetrieveTextFilesInfo {
 161   my($Index, $TextFile, $FileDir, $FileName, $FileExt, $InDelim, $Line, @ColLabels, $OutFileRoot,  $OutFile, $ColNum, $ColLabel);
 162 
 163   @TextFilesOkay = ();
 164   @TextFilesColCount = (); @TextFilesColLabels = ();
 165   @TextFilesColLabelToNumMap = ();
 166   @TextFilesInDelim = ();
 167   @TextFilesOutFile = ();
 168 
 169  FILELIST: for $Index (0 .. $#TextFilesList) {
 170     $TextFile = $TextFilesList[$Index];
 171     $TextFilesOkay[$Index] = 0;
 172     $TextFilesColCount[$Index] = 0;
 173     $TextFilesInDelim[$Index] = "";
 174     $TextFilesOutFile[$Index] = "";
 175     @{$TextFilesColLabels[$Index]} = ();
 176     %{$TextFilesColLabelToNumMap[$Index]} = ();
 177     if (!(-e $TextFile)) {
 178       warn "Warning: Ignoring file $TextFile: It doesn't exist\n";
 179       next FILELIST;
 180     }
 181     if (!CheckFileType($TextFile, "csv tsv")) {
 182       warn "Warning: Ignoring file $TextFile: It's not a csv or tsv file\n";
 183       next FILELIST;
 184     }
 185     ($FileDir, $FileName, $FileExt) = ParseFileName($TextFile);
 186     if ($FileExt =~ /^tsv$/i) {
 187       $InDelim = "\t";
 188     }
 189     else {
 190       $InDelim = "\,";
 191       if ($Options{indelim} !~ /^(comma|semicolon)$/i) {
 192 	warn "Warning: Ignoring file $TextFile: The value specified, $Options{indelim}, for option \"--indelim\" is not valid for csv files\n";
 193 	next FILELIST;
 194       }
 195       if ($Options{indelim} =~ /^semicolon$/i) {
 196 	$InDelim = "\;";
 197       }
 198     }
 199 
 200     if (!open TEXTFILE, "$TextFile") {
 201       warn "Warning: Ignoring file $TextFile: Couldn't open it: $! \n";
 202       next FILELIST;
 203     }
 204 
 205     $Line = GetTextLine(\*TEXTFILE);
 206     @ColLabels = quotewords($InDelim, 0, $Line);
 207     close TEXTFILE;
 208 
 209     $FileDir = ""; $FileName = ""; $FileExt = "";
 210     ($FileDir, $FileName, $FileExt) = ParseFileName($TextFile);
 211     $FileExt = "csv";
 212     if ($Options{outdelim} =~ /^tab$/i) {
 213       $FileExt = "tsv";
 214     }
 215     if ($Options{root} && (@TextFilesList == 1)) {
 216       my ($RootFileDir, $RootFileName, $RootFileExt) = ParseFileName($Options{root});
 217       if ($RootFileName && $RootFileExt) {
 218 	$FileName = $RootFileName;
 219       }
 220       else {
 221 	$FileName = $Options{root};
 222       }
 223       $OutFileRoot = $FileName;
 224     }
 225     else {
 226       $OutFileRoot = $FileName . "ElementalAnalysis";
 227     }
 228 
 229     $OutFile = $OutFileRoot . ".$FileExt";
 230     if (lc($OutFile) eq lc($TextFile)) {
 231       warn "Warning: Ignoring file $TextFile:Output file name, $OutFile, is same as input text file name, $TextFile\n";
 232       next FILELIST;
 233     }
 234     if (!$Options{overwrite}) {
 235       if (-e $OutFile) {
 236 	warn "Warning: Ignoring file $TextFile: The file $OutFile already exists\n";
 237 	next FILELIST;
 238       }
 239     }
 240 
 241     $TextFilesOkay[$Index] = 1;
 242     $TextFilesInDelim[$Index] = $InDelim;
 243     $TextFilesOutFile[$Index] = "$OutFile";
 244 
 245     $TextFilesColCount[$Index] = @ColLabels;
 246     push @{$TextFilesColLabels[$Index]}, @ColLabels;
 247     for $ColNum (0 .. $#ColLabels) {
 248       $ColLabel = $ColLabels[$ColNum];
 249       $TextFilesColLabelToNumMap[$Index]{$ColLabel} = $ColNum;
 250     }
 251   }
 252 
 253 }
 254 
 255 # Make sure specified formula column are okay...
 256 sub ProcessFormulaColumnsInfo {
 257   my($Index, $TextFile);
 258 
 259   @TextFilesFormulaColNum = ();
 260  FILELIST: for $Index (0 .. $#TextFilesList) {
 261     $TextFile = $TextFilesList[$Index];
 262     $TextFilesFormulaColNum[$Index] = 0;
 263 
 264     if ($TextFilesOkay[$Index]) {
 265       my($FormulaColNum, $FormulaColValid);
 266 
 267       $FormulaColNum = 0;
 268       $FormulaColValid = 0;
 269       if ($SpecifiedFormulaCol) {
 270 	if ($Options{colmode} =~ /^colnum$/i) {
 271 	  if ($SpecifiedFormulaCol <= $TextFilesColCount[$Index]) {
 272 	    $FormulaColNum = $SpecifiedFormulaCol - 1;
 273 	    $FormulaColValid = 1;
 274 	  }
 275 	}
 276 	else {
 277 	  if (exists($TextFilesColLabelToNumMap[$Index]{$SpecifiedFormulaCol})) {
 278 	    $FormulaColNum =  $TextFilesColLabelToNumMap[$Index]{$SpecifiedFormulaCol};
 279 	    $FormulaColValid = 1;
 280 	  }
 281 	}
 282       }
 283       else {
 284 	# Grab the first column with the word Formula in its label...
 285 	my($ColLabel);
 286 	LABEL: for $ColLabel (@{$TextFilesColLabels[$Index]}) {
 287 	  if ($ColLabel =~ /Formula/i) {
 288 	    $FormulaColNum =  $TextFilesColLabelToNumMap[$Index]{$ColLabel};
 289 	    $FormulaColValid = 1;
 290 	    last LABEL;
 291 	  }
 292 	}
 293       }
 294       if ($FormulaColValid) {
 295 	$TextFilesFormulaColNum[$Index] = $FormulaColNum;
 296       }
 297       else {
 298 	if ($SpecifiedFormulaCol) {
 299 	  warn "Warning: Ignoring file $TextFile: Formula column specified, $SpecifiedFormulaCol, using \"f --formulacol\" option doesn't exist\n";
 300 	}
 301 	else {
 302 	  warn "Warning: Ignoring file $TextFile: Column label containing the word Formula doesn't exist\n";
 303 	}
 304 	$TextFilesOkay[$Index] = 0;
 305       }
 306     }
 307   }
 308 }
 309 
 310 # Setup starting column number for adding calculated values and
 311 # column lables to use for these values...
 312 sub ProcessStartColumnsAndValueLabelsInfo {
 313   my($Index, $TextFile, $SpecifiedStartColNum, $StartColNum, $Label, $Value, $NewLabel, $Count, $BeforeStartColNum, $AfterStartColNum, $FirstColNum, $LastColNum, $ColNum, $Part1StartColNum, $Part1EndColNum, $Part2StartColNum, $Part2EndColNum, @Part1ColNums, @Part2ColNums);
 314 
 315   # Start column number for inserting new values...
 316   $SpecifiedStartColNum = "last";
 317   if (exists($Options{startcol})) {
 318     if (length($Options{startcol})) {
 319       $SpecifiedStartColNum = $Options{startcol}
 320     }
 321   }
 322 
 323   # Column labels for for new calculated values...
 324   my(%NewValueLabels) = (ElementalAnalysis => 'ElementalAnalysis', MolecularWeight => 'MolecularWeight', ExactMass => 'ExactMass');
 325   if (@SpecifiedValueLabels) {
 326     for ($Index = 0; $Index < @SpecifiedValueLabels; $Index +=2) {
 327       $Value = $SpecifiedValueLabels[$Index];
 328       $Label = $SpecifiedValueLabels[$Index + 1];
 329       if (exists $NewValueLabels{$Value}) {
 330 	$NewValueLabels{$Value} = $Label;
 331       }
 332     }
 333   }
 334 
 335   @TextFilesColNumsBeforeNew = ();
 336   @TextFilesColNumsAfterNew = ();
 337   @TextFilesValueLabelsMap = ();
 338 
 339  FILELIST: for $Index (0 .. $#TextFilesList) {
 340     $TextFile = $TextFilesList[$Index];
 341 
 342     @{$TextFilesColNumsBeforeNew[$Index]} = ();
 343     @{$TextFilesColNumsAfterNew[$Index]} = ();
 344     %{$TextFilesValueLabelsMap[$Index]} = ();
 345 
 346     if (!$TextFilesOkay[$Index]) {
 347       next FILELIST;
 348     }
 349 
 350     if ($SpecifiedStartColNum !~ /^last$/i) {
 351       if ($Options{colmode} =~ /^collabel$/i) {
 352 	if (exists($TextFilesColLabelToNumMap[$Index]{$SpecifiedStartColNum})) {
 353 	  $StartColNum = $TextFilesColLabelToNumMap[$Index]{$SpecifiedStartColNum};
 354 	}
 355 	else {
 356 	  die "Error: Invalid value $SpecifiedStartColNum specified using \"-s --startcol\" option: column name doesn't exist in  $TextFile  \n";
 357 	}
 358       }
 359       else {
 360 	if ($SpecifiedStartColNum > 0 && $SpecifiedStartColNum <= $TextFilesColCount[$Index]) {
 361 	  $StartColNum -= 1;
 362 	}
 363 	else {
 364 	  die "Error: Invalid value $SpecifiedStartColNum specified using \"-s --startcol\" option: column number doesn't exist in  $TextFile  \n";
 365 	}
 366       }
 367     }
 368     else {
 369       $StartColNum = $TextFilesColCount[$Index] - 1;
 370     }
 371     # Set up columns lists for before and after the addition of calculated column values
 372     # for each text file...
 373     my($BeforeStartColNum, $AfterStartColNum, $FirstColNum, $LastColNum, $ColNum, $Part1StartColNum, $Part1EndColNum, $Part2StartColNum, $Part2EndColNum, @Part1ColNums, @Part2ColNums);
 374 
 375     $FirstColNum = 0; $LastColNum = $TextFilesColCount[$Index] - 1;
 376 
 377     $BeforeStartColNum = $StartColNum - 1;
 378     $AfterStartColNum = $StartColNum + 1;
 379 
 380     if ($Options{startcolmode} =~ /^after$/i) {
 381       $Part1StartColNum = $FirstColNum; $Part1EndColNum = $StartColNum;
 382       $Part2StartColNum = $AfterStartColNum; $Part2EndColNum = $LastColNum;
 383     }
 384     else {
 385       $Part1StartColNum = $FirstColNum; $Part1EndColNum = $BeforeStartColNum;
 386       $Part2StartColNum = $StartColNum; $Part2EndColNum = $LastColNum;
 387     }
 388     @Part1ColNums = (); @Part2ColNums = ();
 389     for $ColNum (0 .. $TextFilesColCount[$Index]) {
 390       if ($ColNum >= $Part1StartColNum && $ColNum <= $Part1EndColNum) {
 391 	push @Part1ColNums, $ColNum;
 392       }
 393     }
 394     for $ColNum (0 .. $TextFilesColCount[$Index]) {
 395       if ($ColNum >= $Part2StartColNum && $ColNum <= $Part2EndColNum) {
 396 	push @Part2ColNums, $ColNum;
 397       }
 398     }
 399     push @{$TextFilesColNumsBeforeNew[$Index]}, @Part1ColNums;
 400     push @{$TextFilesColNumsAfterNew[$Index]}, @Part2ColNums;
 401 
 402     # Setup column labels for calculated values...
 403     for $Value (keys %NewValueLabels) {
 404       $Label = $NewValueLabels{$Value};
 405 
 406       # Make sure it doesn't already exists...
 407       $Count = 1;
 408       $NewLabel = $Label;
 409       while (exists $TextFilesColLabelToNumMap[$Index]{$NewLabel}) {
 410 	$Count++;
 411 	$NewLabel = $Label . $Count;
 412       }
 413       $TextFilesValueLabelsMap[$Index]{$Value} = $NewLabel;
 414     }
 415   }
 416 }
 417 
 418 # Perform elemental analysis...
 419 sub PerformElementalAnalysis {
 420   my($Index) = @_;
 421   my($TextFile, $NewTextFile, $FormulaCol, $Line, $NewLine, $FormulaColValue, $InDelim, $ColNum, $Value, $Status, $ErrorMsg, @ColLabels, @LineWords, @ColNumsBeforeNew, @ColNumsAfterNew);
 422 
 423   $TextFile = $TextFilesList[$Index];
 424   $InDelim = $TextFilesInDelim[$Index];
 425   $NewTextFile = $TextFilesOutFile[$Index];
 426   $FormulaCol = $TextFilesFormulaColNum[$Index];
 427 
 428   @ColNumsBeforeNew = @{$TextFilesColNumsBeforeNew[$Index]};
 429   @ColNumsAfterNew = @{$TextFilesColNumsAfterNew[$Index]};
 430 
 431   print "Generating new Text file $NewTextFile...\n";
 432   open NEWTEXTFILE, ">$NewTextFile" or die "Error: Couldn't open $NewTextFile: $! \n";
 433   open TEXTFILE, "$TextFile" or die "Error: Can't open $TextFile: $! \n";
 434 
 435   # Skip over column labels from old file...
 436   $Line = GetTextLine(\*TEXTFILE);
 437 
 438   # Add column lablels in new file...
 439   @ColLabels = ();
 440   for $ColNum (@ColNumsBeforeNew) {
 441     push @ColLabels, $TextFilesColLabels[$Index][$ColNum];
 442   }
 443   for $Value (@SpecifiedCalculations) {
 444     push @ColLabels, $TextFilesValueLabelsMap[$Index]{$Value};
 445   }
 446   for $ColNum (@ColNumsAfterNew) {
 447     push @ColLabels, $TextFilesColLabels[$Index][$ColNum];
 448   }
 449   $NewLine = '';
 450   $NewLine = JoinWords(\@ColLabels, $OutDelim, $OutQuote);
 451   print NEWTEXTFILE "$NewLine\n";
 452 
 453   # Go over all rows...
 454   my($LineCount, $ElementsRef, $ElementCompositionRef, $CalculationType, $CalculatedValue, @CalculatedValues);
 455 
 456   $LineCount = 1;
 457   TEXTLINE: while ($Line = GetTextLine(\*TEXTFILE)) {
 458     @LineWords = quotewords($InDelim, 0, $Line);
 459     $LineCount++;
 460 
 461     @CalculatedValues = ();
 462     for $Value (@SpecifiedCalculations) {
 463       push @CalculatedValues, '';
 464     }
 465     if ($FormulaCol > @LineWords) {
 466       $ErrorMsg = "Ignoring line $LineCount: Formula column $ColLabels[$FormulaCol] not found";
 467       PrintErrorMsg($Line, $ErrorMsg);
 468       ComposeAndWriteNewLine(\*NEWTEXTFILE, \@LineWords, \@ColNumsBeforeNew, \@ColNumsAfterNew, \@CalculatedValues);
 469       next TEXTLINE;
 470     }
 471 
 472     # Make sure it's a valid molecular formula...
 473     $FormulaColValue = $LineWords[$FormulaCol];
 474     if ($CheckFormula) {
 475       ($Status, $ErrorMsg) = MolecularFormula::IsMolecularFormula($FormulaColValue);
 476       if (!$Status) {
 477 	$ErrorMsg = "Ignoring line $LineCount: Formula column $ColLabels[$FormulaCol] value is not valid: $ErrorMsg";
 478 	PrintErrorMsg($Line, $ErrorMsg);
 479 	ComposeAndWriteNewLine(\*NEWTEXTFILE, \@LineWords, \@ColNumsBeforeNew, \@ColNumsAfterNew, \@CalculatedValues);
 480 	next TEXTLINE;
 481       }
 482     }
 483 
 484     # Calculate appropriate values and write 'em out...
 485     @CalculatedValues = ();
 486     for $CalculationType (@SpecifiedCalculations) {
 487       if ($CalculationType =~ /^ElementalAnalysis$/i) {
 488 	($ElementsRef, $ElementCompositionRef) = MolecularFormula::CalculateElementalComposition($FormulaColValue);
 489 	$CalculatedValue = (defined($ElementsRef) && defined($ElementCompositionRef)) ? MolecularFormula::FormatCompositionInfomation($ElementsRef, $ElementCompositionRef, $Precision) : '';
 490       }
 491       elsif ($CalculationType =~ /^MolecularWeight$/i) {
 492 	$CalculatedValue = MolecularFormula::CalculateMolecularWeight($FormulaColValue);
 493 	$CalculatedValue = (defined($CalculatedValue) && length($CalculatedValue)) ? (sprintf("%.${Precision}f", $CalculatedValue)) : "";
 494       }
 495       elsif ($CalculationType =~ /^ExactMass$/i) {
 496 	$CalculatedValue = MolecularFormula::CalculateExactMass($FormulaColValue);
 497 	$CalculatedValue = (defined($CalculatedValue) && length($CalculatedValue)) ? (sprintf("%.${Precision}f", $CalculatedValue)) : "";
 498       }
 499       else {
 500 	$CalculatedValue = '';
 501       }
 502       push @CalculatedValues, $CalculatedValue;
 503     }
 504 
 505     ComposeAndWriteNewLine(\*NEWTEXTFILE, \@LineWords, \@ColNumsBeforeNew, \@ColNumsAfterNew, \@CalculatedValues);
 506   }
 507   close NEWTEXTFILE;
 508   close TEXTFILE;
 509 
 510 }
 511 
 512 # Write out new line using old and new calculated data...
 513 sub ComposeAndWriteNewLine {
 514   my($NewTextFileRef, $OldLineWordsRef, $ColNumsBeforeNewRef, $ColNumsAfterNewRef, $CalculatedValuesRef) = @_;
 515   my($NewLine, $ColNum, $Value, @NewLineWords);
 516 
 517   @NewLineWords = ();
 518   for $ColNum (@{$ColNumsBeforeNewRef}) {
 519     push @NewLineWords, $OldLineWordsRef->[$ColNum];
 520   }
 521   for $Value (@{$CalculatedValuesRef}) {
 522     push @NewLineWords, $Value;
 523   }
 524   for $ColNum (@{$ColNumsAfterNewRef}) {
 525     push @NewLineWords, $OldLineWordsRef->[$ColNum];
 526   }
 527   $NewLine = JoinWords(\@NewLineWords, $OutDelim, $OutQuote);
 528   print $NewTextFileRef "$NewLine\n";
 529 }
 530 
 531 # Print out error message...
 532 sub PrintErrorMsg {
 533   my($Line, $ErrorMsg) = @_;
 534 
 535   if ($DetailLevel >= 2 ) {
 536     print "$ErrorMsg: $Line\n";
 537   }
 538   elsif ($DetailLevel >= 1) {
 539     print "$ErrorMsg\n";
 540   }
 541 }
 542 
 543 # Setup script usage  and retrieve command line arguments specified using various options...
 544 sub SetupScriptUsage {
 545 
 546   # Retrieve all the options...
 547   %Options = ();
 548   $Options{colmode} = "colnum";
 549   $Options{detail} = 1;
 550   $Options{mode} = "All";
 551   $Options{indelim} = "comma";
 552   $Options{outdelim} = "comma";
 553   $Options{precision} = 2;
 554   $Options{quote} = "yes";
 555   $Options{startcolmode} = "after";
 556 
 557   if (!GetOptions(\%Options, "colmode|c=s", "detail|d=i", "fast", "formulacol|f=s", "help|h", "indelim=s", "mode|m=s", "outdelim=s", "overwrite|o", "precision|p=i", "quote|q=s", "root|r=s", "startcol|s=s", "startcolmode=s", "valuecollabels|v=s", "workingdir|w=s")) {
 558     die "\nTo get a list of valid options and their values, use \"$ScriptName -h\" or\n\"perl -S $ScriptName -h\" command and try again...\n";
 559   }
 560   if ($Options{workingdir}) {
 561     if (! -d $Options{workingdir}) {
 562       die "Error: The value specified, $Options{workingdir}, for option \"-w --workingdir\" is not a directory name.\n";
 563     }
 564     chdir $Options{workingdir} or die "Error: Couldn't chdir $Options{workingdir}: $! \n";
 565   }
 566   if ($Options{colmode} !~ /^(colnum|collabel)$/i) {
 567     die "Error: The value specified, $Options{colmode}, for option \"-c --colmode\" is not valid. Allowed values: colnum or collabel\n";
 568   }
 569   if ($Options{indelim} !~ /^(comma|semicolon)$/i) {
 570     die "Error: The value specified, $Options{indelim}, for option \"--indelim\" is not valid. Allowed values: comma or semicolon\n";
 571   }
 572   if ($Options{outdelim} !~ /^(comma|semicolon|tab)$/i) {
 573     die "Error: The value specified, $Options{outdelim}, for option \"--outdelim\" is not valid. Allowed values: comma, tab, or semicolon\n";
 574   }
 575   if (!IsPositiveInteger($Options{precision})) {
 576     die "Error: The value specified, $Options{precision}, for option \"-p --precision\" is not valid. Allowed values: > 0 \n";
 577   }
 578   if ($Options{quote} !~ /^(yes|no)$/i) {
 579     die "Error: The value specified, $Options{quote}, for option \"-q --quote\" is not valid. Allowed values: yes or no\n";
 580   }
 581   if ($Options{startcolmode} !~ /(^(before|after)$)/i) {
 582     die "Error: The value specified, $Options{quote}, for option \"--startcolmode\" is not valid. Allowed values: before or after\n";
 583   }
 584   if (!IsPositiveInteger($Options{detail})) {
 585     die "Error: The value specified, $Options{detail}, for option \"-d --detail\" is not valid. Allowed values: > 0\n";
 586   }
 587 }
 588