1 #!/usr/bin/perl -w 2 # 3 # $RCSfile: MergeTextFilesWithSD.pl,v $ 4 # $Date: 2015/02/28 20:46:20 $ 5 # $Revision: 1.39 $ 6 # 7 # Author: Manish Sud <msud@san.rr.com> 8 # 9 # Copyright (C) 2015 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 strict; 30 use FindBin; use lib "$FindBin::Bin/../lib"; 31 use Getopt::Long; 32 use File::Basename; 33 use Text::ParseWords; 34 use Benchmark; 35 use FileHandle; 36 use SDFileUtil; 37 use FileUtil; 38 use TextUtil; 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($SDFile, @TextFilesList); 57 @TextFilesList = ExpandFileNames(\@ARGV, "csv tsv"); 58 59 if (@TextFilesList < 2) { 60 die "Error: Specify one or more text files.\n"; 61 } 62 $SDFile = shift @TextFilesList; 63 64 # Process options... 65 print "Processing options...\n"; 66 my(%OptionsInfo); 67 ProcessOptions(); 68 69 # Setup information about input files... 70 print "Checking input SD and text files...\n"; 71 my(%TextFilesInfo); 72 ProcessSDFileInfo(); 73 RetrieveTextFilesInfo(); 74 RetrieveColumnsAndKeysInfo(); 75 76 # Merge files... 77 print "\nGenerating new SD file $OptionsInfo{NewSDFile}...\n"; 78 MergeTextFilesWithSD(); 79 80 print "\n$ScriptName:Done...\n\n"; 81 82 $EndTime = new Benchmark; 83 $TotalTime = timediff ($EndTime, $StartTime); 84 print "Total time: ", timestr($TotalTime), "\n"; 85 86 ############################################################################### 87 88 # Merge all valid Text files with SD file... 89 sub MergeTextFilesWithSD { 90 my($Index, $Line); 91 92 open NEWSDFILE, ">$OptionsInfo{NewSDFile}" or die "Error: Couldn't open $OptionsInfo{NewSDFile}: $! \n"; 93 94 open SDFILE, "$SDFile" or die "Error: Couldn't open $SDFile: $! \n"; 95 96 @{$TextFilesInfo{FileHandle}} = (); 97 for $Index (0 .. $#TextFilesList) { 98 $TextFilesInfo{FileHandle}[$Index] = new FileHandle; 99 100 open $TextFilesInfo{FileHandle}[$Index], "$TextFilesList[$Index]" or die "Error: Couldn't open $TextFilesList[$Index]: $! \n"; 101 GetTextLine($TextFilesInfo{FileHandle}[$Index]); 102 } 103 104 if ($OptionsInfo{Keys}) { 105 MergeTextColumnValuesUsingKeys(\*NEWSDFILE, \*SDFILE); 106 } 107 else { 108 MergeTextColumnValues(\*NEWSDFILE, \*SDFILE); 109 } 110 111 # Close all opened files... 112 close NEWSDFILE; 113 close SDFILE; 114 for $Index (0 .. $#TextFilesList) { 115 close $TextFilesInfo{FileHandle}[$Index]; 116 } 117 } 118 119 # Merge the specified text columns into SD file... 120 sub MergeTextColumnValues { 121 my($NewSDFileRef, $SDFileRef) = @_; 122 my($Index, $Value, $CmpdString, $Line, $InDelim, $ColNum, $ColIndex, @ColLabels, @ColValues, @LineWords); 123 124 while ($CmpdString = ReadCmpdString($SDFileRef)) { 125 $CmpdString =~ s/\$\$\$\$$//g; 126 print $NewSDFileRef "$CmpdString"; 127 128 # Merge coulmn values from other text files... 129 @ColLabels = (); @ColValues = (); 130 for $Index (0 .. $#TextFilesList) { 131 push @ColLabels, @{$TextFilesInfo{ColToMergeLabels}[$Index]}; 132 $InDelim = $TextFilesInfo{InDelim}[$Index]; 133 134 if ($Line = GetTextLine($TextFilesInfo{FileHandle}[$Index])) { 135 @LineWords = quotewords($InDelim, 0, $Line); 136 137 for $ColNum (@{$TextFilesInfo{ColToMerge}[$Index]}) { 138 $Value = ($ColNum < @LineWords) ? $LineWords[$ColNum] : ""; 139 push @ColValues, $Value; 140 } 141 } 142 } 143 144 for $ColIndex (0 .. $#ColLabels) { 145 print $NewSDFileRef "> <$ColLabels[$ColIndex]>\n$ColValues[$ColIndex]\n\n"; 146 } 147 print $NewSDFileRef "\$\$\$\$\n"; 148 } 149 } 150 151 # Merge the specified text columns into SD file using keys... 152 sub MergeTextColumnValuesUsingKeys { 153 my($NewSDFileRef, $SDFileRef) = @_; 154 my($Index, $CmpdString, $Value, $InDelim, $KeyColNum, $KeyColValue, $Line, $ColIndex, $ColNum, @ColLabels, @ColValues, @LineWords, @CmpdLines, @TextFilesKeysToLinesMap, %DataFieldValues); 155 156 # Retrieve text lines from all the text files... 157 @TextFilesKeysToLinesMap = (); 158 159 for $Index (0 .. $#TextFilesList) { 160 $InDelim = $TextFilesInfo{InDelim}[$Index]; 161 %{$TextFilesKeysToLinesMap[$Index]} = (); 162 $KeyColNum = $TextFilesInfo{KeysToUse}[$Index]; 163 164 while ($Line = GetTextLine($TextFilesInfo{FileHandle}[$Index])) { 165 @LineWords = quotewords($InDelim, 0, $Line); 166 167 if ($KeyColNum < @LineWords) { 168 $KeyColValue = $LineWords[$KeyColNum]; 169 170 if (length($KeyColValue)) { 171 if (exists($TextFilesKeysToLinesMap[$Index]{$KeyColValue})) { 172 warn "Warning: Ignoring line, $Line, in text file $TextFilesList[$Index]: Column key value, $KeyColValue, already exists\n"; 173 } 174 else { 175 @{$TextFilesKeysToLinesMap[$Index]{$KeyColValue}} = (); 176 push @{$TextFilesKeysToLinesMap[$Index]{$KeyColValue}}, @LineWords; 177 } 178 } 179 } 180 } 181 } 182 183 while ($CmpdString = ReadCmpdString($SDFileRef)) { 184 @CmpdLines = split "\n", $CmpdString; 185 %DataFieldValues = GetCmpdDataHeaderLabelsAndValues(\@CmpdLines); 186 187 if (exists($DataFieldValues{$OptionsInfo{SDKey}})) { 188 @ColLabels = (); @ColValues = (); 189 $CmpdString =~ s/\$\$\$\$$//g; 190 print $NewSDFileRef "$CmpdString"; 191 192 $KeyColValue = $DataFieldValues{$OptionsInfo{SDKey}}; 193 194 # Merge coulmn values from other text files... 195 for $Index (0 .. $#TextFilesList) { 196 push @ColLabels, @{$TextFilesInfo{ColToMergeLabels}[$Index]}; 197 @LineWords = (); 198 199 if (exists($TextFilesKeysToLinesMap[$Index]{$KeyColValue})) { 200 push @LineWords, @{$TextFilesKeysToLinesMap[$Index]{$KeyColValue}}; 201 } 202 203 for $ColNum (@{$TextFilesInfo{ColToMerge}[$Index]}) { 204 $Value = ($ColNum < @LineWords) ? $LineWords[$ColNum] : ""; 205 push @ColValues, $Value; 206 } 207 } 208 209 for $ColIndex (0 .. $#ColLabels) { 210 $Value = (($ColIndex < @ColValues) && IsNotEmpty($ColValues[$ColIndex]) ) ? $ColValues[$ColIndex] : ""; 211 print $NewSDFileRef "> <$ColLabels[$ColIndex]>\n$Value\n\n"; 212 } 213 print $NewSDFileRef "\$\$\$\$\n"; 214 } 215 } 216 } 217 218 # Retrieve text file columns and keys information for specified options... 219 sub RetrieveColumnsAndKeysInfo { 220 ProcessColumnsInfo(); 221 222 if ($OptionsInfo{Keys}) { 223 ProcessKeysInfo(); 224 } 225 } 226 227 # Process specified columns... 228 sub ProcessColumnsInfo { 229 my($Index, $Values, $ColIndex, $ColNum, $ColLabel, @Words); 230 231 @{$TextFilesInfo{ColSpecified}} = (); 232 @{$TextFilesInfo{ColToMerge}} = (); 233 @{$TextFilesInfo{ColToMergeLabels}} = (); 234 @{$TextFilesInfo{ColToMergeNumToLabelMap}} = (); 235 236 for $Index (0 .. $#TextFilesList) { 237 238 @{$TextFilesInfo{ColSpecified}[$Index]} = (); 239 240 $Values = "all"; 241 if ($OptionsInfo{Columns}) { 242 $Values = $OptionsInfo{ColValues}[$Index]; 243 } 244 245 if ($Values =~ /all/i) { 246 if ($OptionsInfo{Mode} =~ /^colnum$/i) { 247 for $ColNum (1 .. $TextFilesInfo{ColCount}[$Index]) { 248 push @{$TextFilesInfo{ColSpecified}[$Index]}, $ColNum; 249 } 250 } else { 251 push @{$TextFilesInfo{ColSpecified}[$Index]}, @{$TextFilesInfo{ColLabels}[$Index]}; 252 } 253 } 254 else { 255 @Words = split ",", $Values; 256 push @{$TextFilesInfo{ColSpecified}[$Index]}, @Words; 257 } 258 259 @{$TextFilesInfo{ColToMerge}[$Index]} = (); 260 %{$TextFilesInfo{ColToMergeNumToLabelMap}[$Index]} = (); 261 262 if ($OptionsInfo{Mode} =~ /^collabel$/i) { 263 for $ColIndex (0 .. $#{$TextFilesInfo{ColSpecified}[$Index]}) { 264 $ColLabel = $TextFilesInfo{ColSpecified}[$Index][$ColIndex]; 265 266 if (exists($TextFilesInfo{ColLabelToNumMap}[$Index]{$ColLabel})) { 267 $ColNum = $TextFilesInfo{ColLabelToNumMap}[$Index]{$ColLabel}; 268 push @{$TextFilesInfo{ColToMerge}[$Index]}, $ColNum; 269 $TextFilesInfo{ColToMergeNumToLabelMap}[$Index]{$ColNum} = $ColLabel; 270 } 271 else { 272 warn "Warning: Ignoring value, $ColLabel, specified using \"-c --column\" option: column name doesn't exist in $TextFilesList[$Index] \n"; 273 } 274 } 275 } 276 else { 277 for $ColIndex (0 .. $#{$TextFilesInfo{ColSpecified}[$Index]}) { 278 $ColNum = $TextFilesInfo{ColSpecified}[$Index][$ColIndex]; 279 280 # Make sure it's a numeric value... 281 if (!IsPositiveInteger($ColNum)) { 282 warn "Warning: Ignoring value, $ColNum, specified using \"-c --column\" option: Allowed integer values: > 0\n"; 283 } 284 else { 285 if ($ColNum > 0 && $ColNum <= $TextFilesInfo{ColCount}[$Index]) { 286 $ColNum -= 1; 287 push @{$TextFilesInfo{ColToMerge}[$Index]}, $ColNum; 288 $TextFilesInfo{ColToMergeNumToLabelMap}[$Index]{$ColNum} = $TextFilesInfo{ColLabels}[$Index][$ColNum]; 289 } 290 else { 291 warn "Warning: Ignoring value, $ColNum, specified using \"-c --column\" option: column number doesn't exist in $TextFilesList[$Index] \n"; 292 } 293 } 294 } 295 } 296 297 my (@TextFilesColToMergeSorted) = sort { $a <=> $b } @{$TextFilesInfo{ColToMerge}[$Index]}; 298 299 @{$TextFilesInfo{ColToMerge}[$Index]} = (); 300 push @{$TextFilesInfo{ColToMerge}[$Index]}, @TextFilesColToMergeSorted; 301 302 # Set up the labels... 303 @{$TextFilesInfo{ColToMergeLabels}[$Index]} = (); 304 for $ColNum (@TextFilesColToMergeSorted) { 305 push @{$TextFilesInfo{ColToMergeLabels}[$Index]}, $TextFilesInfo{ColToMergeNumToLabelMap}[$Index]{$ColNum}; 306 } 307 } 308 } 309 310 # Process specified keys.... 311 sub ProcessKeysInfo { 312 my($Index, $ColNum, $ColLabel, $Key); 313 314 @{$TextFilesInfo{KeysSpecified}} = (); 315 @{$TextFilesInfo{KeysToUse}} = (); 316 317 for $Index (0 .. $#TextFilesList) { 318 $Key = $OptionsInfo{KeyValues}[$Index]; 319 320 $TextFilesInfo{KeysSpecified}[$Index] = $Key; 321 $TextFilesInfo{KeysToUse}[$Index] = -1; 322 323 if ($OptionsInfo{Mode} =~ /^collabel$/i) { 324 $ColLabel = $Key; 325 326 if (exists($TextFilesInfo{ColLabelToNumMap}[$Index]{$ColLabel})) { 327 $TextFilesInfo{KeysToUse}[$Index] = $TextFilesInfo{ColLabelToNumMap}[$Index]{$ColLabel}; 328 } 329 else { 330 warn "Warning: Ignoring value, $ColLabel, specified using \"-k --keys\" option: column name doesn't exist in $TextFilesList[$Index] \n"; 331 } 332 } 333 else { 334 $ColNum = $Key; 335 if (!IsPositiveInteger($ColNum)) { 336 warn "Warning: Ignoring value, $ColNum, specified using \"-k --keys\" option: Allowed integer values: > 0 \n"; 337 } 338 else { 339 if ($ColNum > 0 && $ColNum <= $TextFilesInfo{ColCount}[$Index]) { 340 $TextFilesInfo{KeysToUse}[$Index] = $ColNum - 1; 341 } 342 else { 343 warn "Warning: Ignoring value, $ColNum, specified using \"-k --keys\" option: column number doesn't exist in $TextFilesList[$Index] \n"; 344 } 345 } 346 } 347 } 348 349 # Modify columns to merge list to make sure the columns identified by key are taken off the list 350 my(@TextFilesColToMergeFiltered, @TextFilesColToMergeLabelsFiltered); 351 352 for $Index (0 .. $#TextFilesList) { 353 @TextFilesColToMergeFiltered = (); 354 @TextFilesColToMergeLabelsFiltered = (); 355 356 for $ColNum (@{$TextFilesInfo{ColToMerge}[$Index]}) { 357 if ($TextFilesInfo{KeysToUse}[$Index] != $ColNum) { 358 push @TextFilesColToMergeFiltered, $ColNum; 359 push @TextFilesColToMergeLabelsFiltered, $TextFilesInfo{ColToMergeNumToLabelMap}[$Index]{$ColNum}; 360 } 361 } 362 363 @{$TextFilesInfo{ColToMerge}[$Index]} = (); 364 push @{$TextFilesInfo{ColToMerge}[$Index]}, @TextFilesColToMergeFiltered; 365 366 @{$TextFilesInfo{ColToMergeLabels}[$Index]} = (); 367 push @{$TextFilesInfo{ColToMergeLabels}[$Index]}, @TextFilesColToMergeLabelsFiltered; 368 } 369 } 370 371 # Check SD file... 372 sub ProcessSDFileInfo { 373 if (!CheckFileType($SDFile, "sd sdf")) { 374 die "Error: Invalid first file $SDFile: It's not a SD file\n"; 375 } 376 if (!(-e $SDFile)) { 377 die "Error: SDFile $SDFile doesn't exist\n"; 378 } 379 } 380 381 # Retrieve information about input text files... 382 sub RetrieveTextFilesInfo { 383 my($Index, $TextFile, $FileDir, $FileName, $FileExt, $InDelim, $Line, $ColNum, $ColLabel, $FileNotOkayCount, @ColLabels,); 384 385 %TextFilesInfo = (); 386 387 @{$TextFilesInfo{FileOkay}} = (); 388 @{$TextFilesInfo{ColCount}} = (); 389 @{$TextFilesInfo{ColLabels}} = (); 390 @{$TextFilesInfo{ColLabelToNumMap}} = (); 391 @{$TextFilesInfo{InDelim}} = (); 392 393 $FileNotOkayCount = 0; 394 395 FILELIST: for $Index (0 .. $#TextFilesList) { 396 $TextFile = $TextFilesList[$Index]; 397 398 $TextFilesInfo{FileOkay}[$Index] = 0; 399 $TextFilesInfo{ColCount}[$Index] = 0; 400 $TextFilesInfo{InDelim}[$Index] = ""; 401 402 @{$TextFilesInfo{ColLabels}[$Index]} = (); 403 %{$TextFilesInfo{ColLabelToNumMap}[$Index]} = (); 404 405 if (!(-e $TextFile)) { 406 warn "Warning: Ignoring file $TextFile: It doesn't exist\n"; 407 $FileNotOkayCount++; 408 next FILELIST; 409 } 410 if (!CheckFileType($TextFile, "csv tsv")) { 411 warn "Warning: Ignoring file $TextFile: It's not a csv or tsv file\n"; 412 $FileNotOkayCount++; 413 next FILELIST; 414 } 415 ($FileDir, $FileName, $FileExt) = ParseFileName($TextFile); 416 if ($FileExt =~ /^tsv$/i) { 417 $InDelim = "\t"; 418 } 419 else { 420 $InDelim = "\,"; 421 if ($OptionsInfo{InDelim} !~ /^(comma|semicolon)$/i) { 422 warn "Warning: Ignoring file $TextFile: The value specified, $OptionsInfo{InDelim}, for option \"--indelim\" is not valid for csv files\n"; 423 $FileNotOkayCount++; 424 next FILELIST; 425 } 426 if ($OptionsInfo{InDelim} =~ /^semicolon$/i) { 427 $InDelim = "\;"; 428 } 429 } 430 431 if (!open TEXTFILE, "$TextFile") { 432 warn "Warning: Ignoring file $TextFile: Couldn't open it: $! \n"; 433 $FileNotOkayCount++; 434 next FILELIST; 435 } 436 437 $Line = GetTextLine(\*TEXTFILE); 438 @ColLabels = quotewords($InDelim, 0, $Line); 439 close TEXTFILE; 440 441 $TextFilesInfo{FileOkay}[$Index] = 1; 442 $TextFilesInfo{InDelim}[$Index] = $InDelim; 443 444 $TextFilesInfo{ColCount}[$Index] = @ColLabels; 445 push @{$TextFilesInfo{ColLabels}[$Index]}, @ColLabels; 446 for $ColNum (0 .. $#ColLabels) { 447 $ColLabel = $ColLabels[$ColNum]; 448 $TextFilesInfo{ColLabelToNumMap}[$Index]{$ColLabel} = $ColNum; 449 } 450 } 451 # Make sure all specified files are valid for merging to work properly... 452 if ($FileNotOkayCount) { 453 die "Error: Problems with input text file(s)...\n"; 454 } 455 } 456 457 # Process option values... 458 sub ProcessOptions { 459 my($Index, $FileDir, $FileName, $FileExt, $NewSDFile, @ColValues, @KeyValues); 460 461 %OptionsInfo = (); 462 463 $OptionsInfo{Mode} = $Options{mode}; 464 465 $OptionsInfo{Columns} = $Options{columns}; 466 @{$OptionsInfo{ColValues}} = (); 467 468 if ($Options{columns}) { 469 @ColValues = split ";", $Options{columns}; 470 if (@ColValues != @TextFilesList) { 471 die "Error: Invalid number of values specified by \"-c --columns\" option: it must be equal to number of input text files.\n"; 472 } 473 for $Index (0 .. $#ColValues) { 474 if (!length($ColValues[$Index])) { 475 die "Error: Invalid value specified by \"-c --columns\" option: empty values are not allowed.\n"; 476 } 477 } 478 @{$OptionsInfo{ColValues}} = @ColValues; 479 } 480 481 $OptionsInfo{Keys} = $Options{keys}; 482 @{$OptionsInfo{KeyValues}} = (); 483 484 if ($Options{keys}) { 485 @KeyValues = split ";", $Options{keys}; 486 if (@KeyValues != @TextFilesList) { 487 die "Error: Invalid number of values specified by \"-k --keys\" option: it must be equal to number of input text files.\n"; 488 } 489 for $Index (0 .. $#KeyValues) { 490 if (!length($KeyValues[$Index])) { 491 die "Error: Invalid value specified by \"-k --keys\" option: empty values are not allowed.\n"; 492 } 493 } 494 @{$OptionsInfo{KeyValues}} = @KeyValues; 495 } 496 497 $OptionsInfo{InDelim} = $Options{indelim}; 498 499 $OptionsInfo{OutFileRoot} = defined $Options{root} ? $Options{root} : undef; 500 $OptionsInfo{Overwrite} = defined $Options{overwrite} ? $Options{overwrite} : undef; 501 502 $OptionsInfo{SDKey} = defined $Options{sdkey} ? $Options{sdkey} : undef; 503 504 # Setup new SD file... 505 if ($Options{root}) { 506 $FileDir = ""; $FileName = ""; $FileExt = ""; 507 ($FileDir, $FileName, $FileExt) = ParseFileName($Options{root}); 508 if ($FileName && $FileExt) { 509 $NewSDFile = $FileName; 510 } 511 else { 512 $NewSDFile = $Options{root}; 513 } 514 } 515 else { 516 $FileDir = ""; $FileName = ""; $FileExt = ""; 517 ($FileDir, $FileName, $FileExt) = ParseFileName($SDFile); 518 519 $NewSDFile = $FileName; 520 ($FileDir, $FileName, $FileExt) = ParseFileName($TextFilesList[0]); 521 522 $NewSDFile = $NewSDFile . "MergedWith" . $FileName . "1To" . @TextFilesList; 523 } 524 525 $NewSDFile = $NewSDFile . ".sdf"; 526 if (!$Options{overwrite}) { 527 if (-e $NewSDFile) { 528 die "Error: The file $NewSDFile already exists.\n"; 529 } 530 } 531 if ($Options{root}) { 532 if (lc($NewSDFile) eq lc($SDFile)) { 533 die "Error: Output filename, $NewSDFile, is similar to a input file name.\nSpecify a different name using \"-r --root\" option or use default name.\n"; 534 } 535 } 536 $OptionsInfo{NewSDFile} = $NewSDFile; 537 } 538 539 # Setup script usage and retrieve command line arguments specified using various options... 540 sub SetupScriptUsage { 541 542 # Retrieve all the options... 543 %Options = (); 544 $Options{mode} = "colnum"; 545 $Options{indelim} = "comma"; 546 547 if (!GetOptions(\%Options, "help|h", "indelim=s", "columns|c=s", "keys|k=s", "mode|m=s", "overwrite|o", "root|r=s", "sdkey|s=s", "workingdir|w=s")) { 548 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"; 549 } 550 if ($Options{workingdir}) { 551 if (! -d $Options{workingdir}) { 552 die "Error: The value specified, $Options{workingdir}, for option \"-w --workingdir\" is not a directory name.\n"; 553 } 554 chdir $Options{workingdir} or die "Error: Couldn't chdir $Options{workingdir}: $! \n"; 555 } 556 if ($Options{mode} !~ /^(colnum|collabel)$/i) { 557 die "Error: The value specified, $Options{mode}, for option \"-m --mode\" is not valid. Allowed values: colnum, or collabel\n"; 558 } 559 if ($Options{indelim} !~ /^(comma|semicolon)$/i) { 560 die "Error: The value specified, $Options{indelim}, for option \"--indelim\" is not valid. Allowed values: comma or semicolon\n"; 561 } 562 if ($Options{sdkey} && !$Options{keys}) { 563 die "Error: The option \"-s --sdkey\" can't be specified without the \"-k --keys\" option.\n"; 564 } 565 elsif (!$Options{sdkey} && $Options{keys}) { 566 die "Error: The option \"-k --keys\" can't be specified without the \"-s --sdkey\" option.\n"; 567 } 568 } 569