1package TIGR::Foundation;
2{
3
4=head1 NAME
5
6TIGR::Foundation - TIGR Foundation object
7
8=head1 SYNOPSIS
9
10  use TIGR::Foundation;
11  my $obj_instance = new TIGR::Foundation;
12
13=head1 DESCRIPTION
14
15This module defines a structure for Perl programs to utilize
16logging, version reporting, and dependency checking in a simple way.
17
18=cut
19
20   BEGIN {
21      require 5.006_00;                       # error if using Perl < v5.6.0
22   }
23
24   use strict;
25   use Cwd;
26   use Cwd 'chdir';
27   use Cwd 'abs_path';
28   use File::Basename;
29   use Getopt::Long;
30   use IO::Handle;
31   use POSIX qw(strftime);
32   use Sys::Hostname;
33   use English;
34
35   require Exporter;
36
37   our @ISA;
38   our @EXPORT;
39   @ISA = ('Exporter');
40   @EXPORT = qw(
41                isReadableFile
42                isWritableFile
43                isExecutableFile
44                isCreatableFile
45                isReadableDir
46                isWritableDir
47                isCreatableDir
48                isCreatablePath
49
50                getISODate
51                getSybaseDate
52                getMySQLDate
53                getFilelabelDate
54                getLogfileDate
55               );
56
57   ## internal variables and identifiers
58   our $REVISION = (qw$Revision: 1.1.1.1 $)[-1];
59   our $VERSION = '1.1';
60   our $VERSION_STRING = "$VERSION (Build $REVISION)";
61   our @DEPEND = ();                          # there are no dependencies
62
63
64   ## prototypes
65
66   # Functional Class : general
67   sub new();
68   sub getProgramInfo($);
69   sub runCommand($);
70
71   # Functional Class : depend
72   sub printDependInfo();
73   sub printDependInfoAndExit();
74   sub addDependInfo(@);
75
76   # Functional Class : version
77   sub getVersionInfo();
78   sub printVersionInfo();
79   sub printVersionInfoAndExit();
80   sub setVersionInfo($);
81
82   # Functional Class : help
83   sub printHelpInfo();
84   sub printHelpInfoAndExit();
85   sub setHelpInfo($);
86
87   # Functional Class : usage
88   sub printUsageInfo();
89   sub printUsageInfoAndExit();
90   sub setUsageInfo($);
91
92   # Functional Class : files
93   sub isReadableFile($);
94   sub isExecutableFile($);
95   sub isWritableFile($);
96   sub isCreatableFile($);
97   sub isReadableDir($);
98   sub isWritableDir($);
99   sub isCreatableDir($);
100   sub isCreatablePath($);
101
102   # Functional Class : date
103   sub getISODate(;@);
104   sub getSybaseDate(;@);
105   sub getMySQLDate(;@);
106   sub getFilelabelDate(;@);
107   sub getLogfileDate(;@);
108
109   # Functional Class : logging
110   sub setDebugLevel($;$);
111   sub getDebugLevel();
112   sub setLogFile($;$);
113   sub getLogFile();
114   sub getErrorFile();
115   sub printDependInfo();
116   sub invalidateLogFILES();
117   sub cleanLogFILES();
118   sub closeLogERROR();
119   sub closeLogMSG();
120   sub openLogERROR();
121   sub openLogMSG();
122   sub logAppend($;$);
123   sub debugPush();
124   sub debugPop();
125   sub logLocal($$);
126   sub logError($;$);
127   sub bail($;$);
128
129   # Functional Class : modified methods
130   sub TIGR_GetOptions(@);
131
132   ## Implementation
133
134
135# Functional Class : general
136
137=over
138
139=item $obj_instance = new TIGR::Foundation;
140
141This function creates a new instance of the TIGR::Foundation
142object.  A reference pointing to the object is returned on success.  Otherwise,
143this method returns undefined.
144
145=cut
146
147
148   sub new() {
149
150      my $self = {};
151      my $pkg = shift;
152      my $user_name = getpwuid($<);
153      my $host_name = hostname();
154
155      # create the object
156      bless $self, $pkg;
157
158      ## Instance variables and identifiers, by functional class
159
160      # Functional Class : general
161      my $pname  = basename($0, ()); # extract the script name
162
163      if((defined ($pname)) && ($pname =~ /^(.*)$/)) {
164          $pname = $1;
165	  $self->{program_name} = $pname ;
166      }
167
168      if ($self->{program_name} =~ /^-$/) {     # check if '-' is the input
169         $self->{program_name} = "STDIN";
170      }
171
172      my $pcommand = join (' ', @ARGV);
173
174      if((defined ($pcommand)) && ($pcommand =~ /^(.*)$/)) {
175          $pcommand = $1;
176	  $self->{invocation} = $pcommand ;
177      }
178
179      # The following four variables are to contain information specified by
180      # the 'host' program; there are methods of setting and retrieving each
181
182      # Functional Class : depend
183      @{$self->{depend_info}} = ();
184
185      # Functional Class : version
186      $self->{version_info} = undef;
187
188      # Functional Class : help
189      $self->{help_info} = undef;
190
191      # Functional Class : usage
192      $self->{usage_info} = undef;
193
194      # Functional Class : logging
195      $self->{debug_level} = undef;             # default debug is not defined
196      @{$self->{debug_store}} = ();             # the backup debug level stack
197      @{$self->{debug_queue}} = ();             # queue used by MSG routine
198      @{$self->{error_queue}} = ();             # queue used by ERROR routine
199      $self->{max_debug_queue_size} = 100;      # maximum size for queue before
200                                                # log entries are expired
201      @{$self->{log_files}} =                   # these log files are consulted
202         ("$self->{program_name}.log",          # on file write error and are
203          "/tmp/$self->{program_name}.$$.log"); # modified by setLogFile
204      $self->{msg_file_open_flag} = 0;          # flag to check logLocal file
205      $self->{error_file_open_flag} = 0;        # flag to check logError file
206      $self->{msg_file_used} = 0;               # flag to indicate if log file
207      $self->{error_file_used} = 0;             #   has been written to
208      $self->{msg_append_flag} = 0;             # by default logs are truncated
209      $self->{error_append_flag} = 0;           # by default logs are truncated
210      $self->{log_append_setting} = 0;          # (truncate == 0)
211      $self->{static_log_file} = undef;         # user defined log file
212      $self->{start_time} = undef;              # program start time
213      $self->{finish_time} = undef;             # program stop time
214
215      # Log program invocation
216      $self->logLocal("START: " . "Username:$user_name, ".
217                      "Hostname: $host_name ". $self->getProgramInfo('name') .
218                      " " . $self->getProgramInfo('invocation'), 0);
219      $self->{start_time} = time;
220
221      return $self;
222   }
223
224
225
226=item $value = $obj_instance->getProgramInfo($field_type);
227
228This function returns field values for specified field types describing
229attributes of the program.  The C<$field_type> parameter must be a listed
230attribute: C<name>, C<invocation>, C<env_path>, C<abs_path>.
231The C<name> field specifies the bare name of the executable.  The
232C<invocation> field specifies the command line arguments passed to the
233executable.   The C<env_path> value returns the environment path to the
234working directory.  The C<abs_path> value specifies the absolute path to the
235working directory.  If C<env_path> is found to be inconsistent, then that
236value will return the C<abs_path> value.  If an invalid C<$field_type> is
237passed, the function returns undefined.
238
239=cut
240
241
242   sub getProgramInfo($) {
243      my $self = shift;
244      my $field_type = shift;
245      my $return_value = undef;
246      if (defined $field_type) {
247         $field_type =~ /^name$/ && do {
248            $return_value = $self->{program_name};
249         };
250         $field_type =~ /^invocation$/ && do {
251            $return_value = $self->{invocation};
252         };
253         $field_type =~ /^env_path$/ && do {
254            my $return_value = "";
255            if (
256                (defined $ENV{'PWD'}) &&
257                (abs_path($ENV{'PWD'}) eq abs_path(".")) &&
258                ($ENV{'PWD'} =~ /^(.*)$/)
259               ) {
260	       $ENV{'PWD'} = $1;
261               $return_value = $ENV{'PWD'};
262            }
263            else {
264	       my $tmp_val = abs_path(".");
265
266               if((defined ($tmp_val)) && ($tmp_val =~ /^(.*)$/)) {
267                  $tmp_val = $1;
268	          $return_value = $tmp_val;
269               }
270            }
271            return $return_value;
272         };
273
274         $field_type =~ /^abs_path$/ && do {
275            my $tmp_val = abs_path(".");
276
277            if((defined ($tmp_val)) && ($tmp_val =~ /^(.*)$/)) {
278               $tmp_val = $1;
279	       $return_value = $tmp_val;
280            }
281         };
282      }
283      return $return_value;
284   }
285
286=item $exit_code = $obj_instance->runCommand($command_str);
287
288This function passes the argument C<$command_str> to /bin/sh
289for processing.  The return value is the exit code of the
290C<$command_str>.  If the exit code is not defined, then either the signal or
291core dump value of the execution is returned, whichever is applicable.  Perl
292variables C<$?> and C<$!> are set accordingly.  If C<$command_str> is not
293defined, this function returns undefined.  Log messages are recorded at log
294level 4 to indicate the type of exit status and the corresponding code.
295Invalid commands return -1.
296
297=cut
298
299
300   sub runCommand($) {
301       my $self = shift;
302       my $command_str = shift;
303       my $exit_code = undef;
304       my $signal_num = undef;
305       my $dumped_core = undef;
306       my $invalid_command = undef;
307       my $return_value = undef;
308       my @info_arr = getpwuid($<);
309       my $len = @info_arr;
310       my $home_dir = $info_arr[7];
311       my $current_dir = $self->getProgramInfo("abs_path");
312
313       if((defined ($ENV{PATH})) && ($ENV{PATH} =~ /^(.*)$/)) {#taint checking
314	  $ENV{PATH} = $1;
315          my $path_var = $ENV{PATH};
316          my @paths = split /:/, $path_var;
317          my $pathval = undef;
318          my $i = 0;
319          my $paths_len = @paths;
320
321          for ($i = 0; $i < $paths_len ; $i++) {
322             #substituting ~ with the home pathname.
323	     $pathval = $paths[$i];
324	     $pathval =~ s/^~$/$home_dir/g;
325             my $home_root = $home_dir."\/";
326             $pathval =~ s/^~\//$home_root/g;
327
328             #substituting . with the current pathname.
329             $pathval =~ s/^\.$/$current_dir/g;
330             my $current_root = $current_dir."\/";
331             $pathval =~ s/^\.\//$current_root/g;
332             $paths[$i] = $pathval;
333	  }
334
335          $ENV{PATH} = join(":", @paths);
336       }
337
338       if((defined ($command_str)) && ($command_str =~ /^(.*)$/)) {#taint
339                                                                   #checking
340          $command_str = $1;
341          system($command_str);
342          $exit_code = $? >> 8;
343          $signal_num = $? & 127;
344          $dumped_core = $? & 128;
345
346          if ($? == -1) {
347             $invalid_command = -1;
348          }
349
350          if (
351             (!defined $invalid_command) &&
352             ($exit_code == 0) &&
353             ($signal_num == 0) &&
354             ($dumped_core != 0)
355             ) {
356
357             $self->logLocal("Command '" . $command_str . "' core dumped", 4);
358             $return_value = $dumped_core;
359           }
360          elsif (
361                (!defined $invalid_command) &&
362                ($exit_code == 0) &&
363                ($signal_num != 0)
364                ) {
365
366             $self->logLocal("Command '" . $command_str .
367                             "' exited on signal " . $signal_num, 4);
368             $return_value = $signal_num;
369          }
370          elsif ((!defined $invalid_command)) {
371
372             $self->logLocal("Command '" . $command_str .
373                             "' exited with exit code " . $exit_code, 4);
374             $return_value = $exit_code;
375          }
376          else {
377
378             $self->logLocal("Command '" . $command_str .
379                             "' exited with invalid code " . $?, 4);
380             $return_value = $?;
381          }
382       }
383       return $return_value;
384   }
385
386
387# Functional Class : depend
388
389=item $obj_instance->printDependInfo();
390
391The C<printDependInfo()> function prints the dependency list created by
392C<addDependInfo()>.  One item is printed per line.
393
394=cut
395
396
397   sub printDependInfo() {
398      my $self = shift;
399      foreach my $dependent (@{$self->{depend_info}}) {
400         print STDERR $dependent, "\n";
401      }
402   }
403
404
405=item $obj_instance->printDependInfoAndExit();
406
407The C<printDependInfoAndExit()> function prints the dependency list created by
408C<addDependInfo()>.  One item is printed per line.  The function exits with
409exit code 0.
410
411=cut
412
413
414   sub printDependInfoAndExit() {
415      my $self = shift;
416      $self->printDependInfo();
417      exit 0;
418   }
419
420
421=item $obj_instance->addDependInfo(@depend_list);
422
423The C<addDependInfo()> function adds C<@depend_list> information
424to the dependency list.  If C<@depend_list> is empty, the internal
425dependency list is emptied.  Contents of C<@depend_list> are not checked
426for validity (eg. they can be composed entirely of white space or
427multiple files per record).  The first undefined record in C<@depend_list>
428halts reading in of dependency information.
429
430=cut
431
432
433   sub addDependInfo(@) {
434      my $self = shift;
435      my $num_elts = 0;
436      while (my $data_elt = shift @_) {
437         push (@{$self->{depend_info}}, $data_elt);
438         $num_elts++;
439      }
440      if ($num_elts == 0) {
441         @{$self->{depend_info}} = ();
442      }
443   }
444
445
446# Functional Class : version
447
448=item $version_string = $obj_instance->getVersionInfo();
449
450The C<getVersionInfo()> function returns the version information set by the
451C<setVersionInfo()> function.
452
453=cut
454
455
456   sub getVersionInfo() {
457      my $self = shift;
458      return $self->{version_info};
459   }
460
461
462=item $obj_instance->printVersionInfo();
463
464The C<printVersionInfo()> function prints the version information set by the
465C<setVersionInfo()> function.  If there is no defined version information,
466a message is returned notifying the user.
467
468=cut
469
470
471   sub printVersionInfo() {
472      my $self = shift;
473      if (defined $self->getVersionInfo()) {
474         print STDERR $self->getProgramInfo('name'),
475            " ", $self->getVersionInfo(), "\n";
476      }
477      else {
478         print STDERR $self->getProgramInfo('name'),
479            " has no defined version information\n";
480      }
481   }
482
483
484=item $obj_instance->printVersionInfoAndExit();
485
486The C<printVersionInfoAndExit()> function prints version info set by the
487C<setVersionInfo()> function.  If there is no defined version information,
488a message is printed notifying the user.  This function calls exit with
489exit code 0.
490
491=cut
492
493
494   sub printVersionInfoAndExit() {
495      my $self = shift;
496      $self->printVersionInfo();
497      exit 0;
498   }
499
500
501=item $obj_instance->setVersionInfo($version_string);
502
503The C<setVersionInfo()> function sets the version information to be reported
504by C<getVersionInfo()>.  If C<$version_string> is empty, invalid, or
505undefined, the stored version information will be undefined.
506
507=cut
508
509
510   sub setVersionInfo($) {
511      my $self = shift;
512      my $v_info = shift;
513      if (
514          (defined $v_info) &&
515          ($v_info =~ /\S/) &&
516          ((ref $v_info) eq "")
517         ) {
518         $self->{version_info} = $v_info;
519      }
520      else {
521         $self->{version_info} = undef;
522      }
523   }
524
525
526# Functional Class : help
527
528=item $obj_instance->printHelpInfo();
529
530The C<printHelpInfo()> function prints the help information passed by the
531C<setHelpInfo()> function.
532
533=cut
534
535
536   sub printHelpInfo() {
537      my $self = shift;
538      if (defined $self->{help_info}) {
539         print STDERR $self->{help_info};
540      }
541      else {
542         print STDERR "No help information defined.\n";
543      }
544   }
545
546
547=item $obj_instance->printHelpInfoAndExit();
548
549The C<printHelpInfoAndExit()> function prints the help info passed by the
550C<setHelpInfo()> function.  This function exits with exit code 0.
551
552=cut
553
554
555   sub printHelpInfoAndExit() {
556      my $self = shift;
557      $self->printHelpInfo();
558      exit 0;
559   }
560
561
562=item $obj_instance->setHelpInfo($help_string);
563
564The C<setHelpInfo()> function sets the help information via C<$help_string>.
565If C<$help_string> is undefined, invalid, or empty, the help information
566is undefined.
567
568=cut
569
570
571   sub setHelpInfo($) {
572      my $self = shift;
573      my $help_string = shift;
574      if (
575          (defined $help_string) &&
576          ($help_string =~ /\S/) &&
577          ((ref $help_string) eq "")
578         ) {
579	 chomp($help_string);#removing a new line if it is there.
580         $self->{help_info} = $help_string."\n";#adding a new line to help.
581      }
582      else {
583         $self->{help_info} = undef;
584      }
585   }
586
587
588# Functional Class : usage
589
590=item $obj_instance->printUsageInfo();
591
592The C<printUsageInfo()> function prints the usage information reported by the
593C<setUsageInfo()> function.  If no usage information is defined, but help
594information is defined, help information will be printed.
595
596=cut
597
598
599   sub printUsageInfo() {
600
601      my $self = shift;
602      if (defined $self->{usage_info}) {
603         print STDERR $self->{usage_info};
604      }
605      elsif (defined $self->{help_info}) {
606         print STDERR $self->{help_info};
607      }
608      else {
609         print STDERR "No usage information defined.\n";
610      }
611   }
612
613
614=item $obj_instance->printUsageInfoAndExit();
615
616The C<printUsageInfoAndExit()> function prints the usage information the
617reported by the C<setUsageInfo()> function and exits with status 1.
618
619=cut
620
621
622   sub printUsageInfoAndExit() {
623      my $self = shift;
624      $self->printUsageInfo();
625      $self->bail("Incorrect command line");
626   }
627
628
629=item $obj_instance->setUsageInfo($usage_string);
630
631The C<setUsageInfo()> function sets the usage information via C<$usage_string>.
632If C<$usage_string> is undefined, invalid, or empty, the usage information
633is undefined.
634
635=cut
636
637
638   sub setUsageInfo($) {
639      my $self = shift;
640      my $usage_string = shift;
641      if (
642          (defined $usage_string) &&
643          ($usage_string =~ /\S/) &&
644          ((ref $usage_string) eq "")
645         ) {
646	 chomp($usage_string); #removing a new line if it is there.
647         $self->{usage_info} = $usage_string."\n";#adding a new line to usage
648      }
649      else {
650         $self->{usage_info} = undef;
651      }
652   }
653
654
655# Functional Class : files
656
657=item $valid = isReadableFile($file_name);
658
659This function accepts a single scalar parameter containing a file name.
660If the file corresponding to the file name is a readable plain file or symbolic
661link, this function returns 1.  Otherwise, the function returns 0.  If the file
662name passed is undefined, this function returns 0 as well.
663
664=cut
665
666
667   sub isReadableFile($) {
668      my $file = shift;
669      if (scalar(@_) != 0) { #incase the method was invoked as an instance
670                             #method
671         $file  = shift;
672      }
673
674      if (defined ($file) &&             # was a file name passed?
675          ((-f $file) || (-l $file)) &&  # is the file a file or sym. link?
676          (-r $file)                     # is the file readable?
677         ) {
678         return 1;
679      }
680      else {
681         return 0;
682      }
683   }
684
685
686=item $valid = isExecutableFile($file_name);
687
688This function accepts a single scalar parameter containing a file name.
689If the file corresponding to the file name is an executable plain file
690or symbolic link, this function returns 1.  Otherwise, the function returns 0.
691If the file name passed is undefined, this function returns 0 as well.
692
693=cut
694
695
696   sub isExecutableFile($) {
697      my $file = shift;
698       if (scalar(@_) != 0) { # incase the method was invoked as a instance
699                              # method
700         $file  = shift;
701      }
702
703      if (defined ($file) &&             # was a file name passed?
704          ((-f $file) || (-l $file)) &&  # is the file a file or sym. link?
705          (-x $file)                     # is the file executable?
706         ) {
707         return 1;
708      }
709      else {
710         return 0;
711      }
712   }
713
714
715=item $valid = isWritableFile($file_name);
716
717This function accepts a single scalar parameter containing a file name.
718If the file corresponding to the file name is a writable plain file
719or symbolic link, this function returns 1.  Otherwise, the function returns 0.
720If the file name passed is undefined, this function returns 0 as well.
721
722=cut
723
724
725   sub isWritableFile($) {
726      my $file = shift;
727      if (scalar(@_) != 0) { # incase the method was invoked as a instance
728                             #  method
729         $file  = shift;
730      }
731
732      if (defined ($file) &&             # was a file name passed?
733          ((-f $file) || (-l $file)) &&  # is the file a file or sym. link?
734          (-w $file)                     # is the file writable?
735         ) {
736         return 1;
737      }
738      else {
739         return 0;
740      }
741   }
742
743
744=item $valid = isCreatableFile($file_name);
745
746This function accepts a single scalar parameter containing a file name.  If
747the file corresponding to the file name is creatable this function returns 1.
748The function checks if the location of the file is writable by the effective
749user id (EUID).  If the file location does not exist or the location is not
750writable, the function returns 0.  If the file name passed is undefined,
751this function returns 0 as well.  Note that files with suffix F</> are not
752supported under UNIX platforms, and will return 0.
753
754=cut
755
756
757   sub isCreatableFile($) {
758      my $file = shift;
759      if (scalar(@_) != 0) { # incase the method was invoked as an instance
760                             # method
761         $file  = shift;
762      }
763
764      my $return_code = 0;
765
766      if (
767          (defined ($file)) &&
768          (! -e $file) &&
769          ($file !~ /\/$/)
770         ) {
771         my $dirname = dirname($file);
772         # check the writability of the directory
773         $return_code = isWritableDir($dirname);
774      }
775      else {
776         # the file exists, it's not creatable
777         $return_code = 0;
778      }
779      return $return_code;
780   }
781
782
783=item $valid = isReadableDir($directory_name);
784
785This function accepts a single scalar parameter containing a directory name.
786If the name corresponding to the directory is a readable, searchable directory
787entry, this function returns 1.  Otherwise, the function returns 0.  If the
788name passed is undefined, this function returns 0 as well.
789
790=cut
791
792
793   sub isReadableDir($) {
794      my $file = shift;
795      if (scalar(@_) != 0) { # incase the method was invoked as an instance
796                             # method
797         $file  = shift;
798      }
799
800      if (defined ($file) &&             # was a name passed?
801          (-d $file) &&                  # is the name a directory?
802          (-r $file) &&                  # is the directory readable?
803          (-x $file)                     # is the directory searchable?
804         ) {
805         return 1;
806      }
807      else {
808         return 0;
809      }
810   }
811
812
813=item $valid = isWritableDir($directory_name);
814
815This function accepts a single scalar parameter containing a directory name.
816If the name corresponding to the directory is a writable, searchable directory
817entry, this function returns 1.  Otherwise, the function returns 0.  If the
818name passed is undefined, this function returns 0 as well.
819
820=cut
821
822
823   sub isWritableDir($) {
824      my $file = shift;
825      if (scalar(@_) != 0) { # incase the method was invoked as an instance
826                             # method
827         $file  = shift;
828      }
829
830      if (defined ($file) &&             # was a name passed?
831          (-d $file) &&                  # is the name a directory?
832          (-w $file) &&                  # is the directory writable?
833          (-x $file)                     # is the directory searchable?
834         ) {
835         return 1;
836      }
837      else {
838         return 0;
839      }
840   }
841
842
843=item $valid = isCreatableDir($directory_name);
844
845This function accepts a single scalar parameter containing a directory name.
846If the name corresponding to the directory is creatable this function returns
8471. The function checks if the immediate parent of the directory is writable by
848the effective user id (EUID).  If the parent directory does not exist or the
849tree is not writable, the function returns 0.  If the directory name passed is
850undefined, this function returns 0 as well.
851
852=cut
853
854
855   sub isCreatableDir($) {
856      my $dir = shift;
857      if (scalar(@_) != 0) { # incase the method was invoked as an instance
858                             # method
859         $dir  = shift;
860      }
861      my $return_code = 0;
862
863      if (defined ($dir)) {
864         $dir =~ s/\/$//g;
865         $return_code = isCreatableFile($dir);
866      }
867      return $return_code;
868   }
869
870
871=item $valid = isCreatablePath($path_name);
872
873This function accepts a single scalar parameter containing a path name.  If
874the C<$path_name> is creatable this function returns 1. The function checks
875if the directory hierarchy of the path is creatable or writable by the
876effective user id (EUID).  This function calls itself recursively until
877an existing directory node is found.  If that node is writable, ie. the path
878can be created in it, then this function returns 1.  Otherwise, the function
879returns 0.  This function also returns zero if the C<$path_name> supplied
880is disconnected from a reachable directory tree on the file system.
881If the path already exists, this function returns 0.  The C<$path_name> may
882imply either a path to a file or a directory.  Path names may be relative or
883absolute paths.  Any unresolvable relative paths will return 0 as well.  This
884includes paths with F<..> back references to nonexistent directories.
885This function is recursive whereas C<isCreatableFile()> and
886C<isCreatableDir()> are not.
887
888=cut
889
890
891   sub isCreatablePath($) {
892      my $pathname = shift;
893      if (scalar(@_) != 0) { # incase the method was invoked as an instance
894                             # method
895         $pathname  = shift;
896      }
897      my $return_code = 0;
898
899      if (defined $pathname) {
900         # strip trailing '/'
901         $pathname =~ s/(.+)\/$/$1/g;
902         my $filename = basename($pathname);
903         my $dirname = dirname($pathname);
904         if (
905             (! -e $pathname) &&
906             ($dirname ne $pathname) &&
907             ($filename ne "..")
908            ) {
909            if (-e $dirname) {
910               $return_code = isWritableDir($dirname);
911            }
912            else {
913               $return_code = isCreatablePath($dirname);
914            }
915         }
916         else {
917            $return_code = 0;
918         }
919      }
920      return $return_code;
921   }
922
923
924# Functional Class : date
925
926=item $date_string = getISODate($tm);
927
928This function returns the ISO 8601 datetime as a string given a time
929structure as returned by the C<time> function.  If no arguments
930are supplied, this function returns the current time.  If incorrect
931arguments are supplied, this function returns undefined.
932
933=cut
934
935
936   sub getISODate(;@) {
937      #checking if the function is invoked as an instance method.
938      if((defined(ref $_[0])) && ((ref $_[0]) eq "TIGR::Foundation")){
939         shift;
940      }
941      my @time_val = @_;
942      my $time_str = undef;
943      if (scalar(@time_val) == 0) {
944         @time_val = localtime;
945      }
946      eval {
947         $time_str = strftime "%Y-%m-%d %H:%M:%S", @time_val;
948      };
949      return $time_str;
950   }
951
952
953=item $date_string = getSybaseDate(@tm);
954
955This function returns a Sybase formatted datetime as a string given a time
956structure as returned by the C<time> function.  If no arguments
957are supplied, this function returns the current time.  If incorrect
958arguments are supplied, this function returns undefined.  The date string
959returned is quoted according to Sybase requirements.
960
961=cut
962
963
964   sub getSybaseDate(;@) {
965      #checking if the function is invoked as an instance method.
966      if((defined(ref $_[0])) && ((ref $_[0]) eq "TIGR::Foundation")){
967         shift;
968      }
969      my @time_val = @_;
970      my $time_str = undef;
971      if (scalar(@time_val) == 0) {
972         @time_val = localtime;
973      }
974      eval {
975         $time_str = strftime "\'%b %d %Y %I:%M%p\'", @time_val;
976      };
977      return $time_str;
978   }
979
980
981=item $date_string = getMySQLDate(@tm);
982
983This function returns a MySQL formatted datetime as a string given a time
984structure as returned by the C<time> function.  If no arguments
985are supplied, this function returns the current time.  If incorrect
986arguments are supplied, this function returns undefined.  The datetime string
987returned is prequoted according to MySQL requirements.
988
989=cut
990
991
992   sub getMySQLDate(;@) {
993      #checking if the function is invoked as an instance method.
994      if((defined(ref $_[0])) && ((ref $_[0]) eq "TIGR::Foundation")){
995         shift;
996      }
997      my @time_val = @_;
998      my $time_str = undef;
999      if (scalar(@time_val) == 0) {
1000         @time_val = localtime;
1001      }
1002      $time_str = getISODate(@time_val);
1003      if (defined $time_str) {
1004         $time_str = "\'$time_str\'";
1005      }
1006      return $time_str;
1007   }
1008
1009
1010=item $date_string = getFilelabelDate(@tm);
1011
1012This function returns the date (not time) as a compressed string
1013suitable for use as part of a file name.  The format is YYMMDD.
1014The optional parameter should be a time structure as returned by
1015the C<time> function.  If no arguments are supplied, the current time
1016is used.  If incorrect arguments are supplied, this function returns
1017undefined.
1018
1019=cut
1020
1021
1022   sub getFilelabelDate(;@) {
1023      #checking if the function is invoked as an instance method.
1024      if((defined(ref $_[0])) && ((ref $_[0]) eq "TIGR::Foundation")){
1025         shift;
1026      }
1027      my @time_val = @_;
1028      my $time_str = undef;
1029      if (scalar(@time_val) == 0) {
1030         @time_val = localtime;
1031      }
1032      eval {
1033         $time_str = strftime "%y%m%d", @time_val;
1034      };
1035      return $time_str;
1036   }
1037
1038
1039=item $date_string = $obj_instance->getLogfileDate(@tm);
1040
1041This function returns the datetime as a formatted string
1042suitable for use as a log entry header.  The optional parameter
1043should be a time structure as returned by the C<time> function.
1044If no arguments are supplied, this function uses the current time.
1045If incorrect arguments are supplied, this function sets the date/time fields
1046of the log entry string to C< INVALID|XXXXXX|>.
1047
1048=cut
1049
1050
1051   sub getLogfileDate(;@) {
1052      #checking if the function is invoked as an instance method.
1053      if((defined(ref $_[0])) && ((ref $_[0]) eq "TIGR::Foundation")){
1054         shift;
1055      }
1056      my @time_val = @_;
1057      my $time_str = undef;
1058      my $log_form = undef;
1059      if (scalar(@time_val) == 0) {
1060         @time_val = localtime;
1061      }
1062      eval {
1063         $time_str = strftime("%Y%m%d|%H%M%S|", @time_val);
1064      };
1065      if (!defined $time_str) {
1066         $time_str = " INVALID|XXXXXX|";
1067      }
1068      $log_form = $time_str . sprintf("%6d| ", $$);
1069      return $log_form;
1070   }
1071
1072
1073# Functional Class : logging
1074
1075=item $obj_instance->setDebugLevel($new_level);
1076
1077This function sets the level of debug reporting according to C<$new_level>.
1078If the debug level is less than 0, all debug reporting is turned off.
1079It is impossible to turn off error reporting from C<bail()>.  If C<$new_level>
1080is undefined, the debug level is set to 0.  This function maintains
1081compatibility with C<GetOptions()>, and will accept a second parameter
1082the debug level, provided it is an integer.  In such cases, the first parameter
1083is checked only if the second parameter is invalid.  By default, the default
1084level is undefined.  To turn on debugging, you must invoke this function.
1085
1086=cut
1087
1088
1089   sub setDebugLevel($;$) {
1090      my $self = shift;
1091      my $new_level = shift;
1092      my $getopts_new_level = shift;
1093
1094      if (
1095          (defined $getopts_new_level) &&
1096          ($getopts_new_level =~ /^-?\d+$/)
1097         ) {
1098         $new_level = $getopts_new_level;
1099      }
1100      elsif (
1101          (!defined $new_level) ||
1102          ($new_level !~ /^-?\d+$/)
1103         ) {
1104         $new_level = 0;
1105         $self->logLocal("No or invalid parameter to setDebugLevel(), " .
1106                         "setting debug level to 0", 3);
1107      }
1108
1109      if ($new_level < 0) {
1110         $new_level = -1;
1111      }
1112
1113      $self->{debug_level} = $new_level;
1114      $self->logLocal("Set debug level to " . $self->getDebugLevel(), 2);
1115   }
1116
1117
1118=item $level = $obj_instance->getDebugLevel();
1119
1120This function returns the current debug level.  If the current debug
1121level is not defined, this function returns undefined.
1122
1123=cut
1124
1125
1126   sub getDebugLevel() {
1127      my $self = shift;
1128      return $self->{debug_level};
1129   }
1130
1131
1132=item $obj_instance->setLogFile($log_file);
1133
1134This function sets the log file name for the C<logLocal()> function.
1135B<The programmer should call this function before invoking C<setDebugLevel()>>
1136if the default log file is not to be used.  The function takes one parameter,
1137C<$log_file>, which defines the new log file name.  If a log file is already
1138open, it is closed.  The old log file is not truncated or deleted.
1139Future calls to C<logLocal()> or C<bail()> will log to C<$log_file> if it
1140is successfully opened.  If the new log file is not successfully opened,
1141the function will try to open the default log file, F<program_name.log>.
1142If that file cannot be opened, F</tmp/program_name.$process_id.log> will
1143be used.  If no log file argument is passed, the function will try to open
1144the default log file.  This function is C<GetOptions()> aware; it will accept
1145two parameters, using the second one as the log file and ignoring the first if
1146and only if two parameters are passed.  Any other usage specifies the first
1147parameter as the log file name.
1148
1149=cut
1150
1151
1152   sub setLogFile($;$) {
1153      my $self = shift;
1154      my $old_log_file = defined $self->{static_log_file} ?
1155         $self->{static_log_file} : undef;
1156      $self->{static_log_file} = shift;
1157      if (scalar(@_) == 1) {
1158         $self->{static_log_file} = shift;
1159      }
1160
1161      # only consider a new log file that is definable as a file
1162      if ((defined ($self->{static_log_file})) &&
1163          ($self->{static_log_file} !~ /^\s*$/)) {
1164         # delete an old log file entry added by "setLogFile"
1165         for (my $idx = 0;
1166              ($idx <= $#{$self->{log_files}}) && defined($old_log_file);
1167              $idx++) {
1168            if ($self->{log_files}[$idx] eq $old_log_file) {
1169               splice @{$self->{log_files}}, $idx, 1;
1170               $old_log_file = undef;
1171            }
1172         }
1173         unshift @{$self->{log_files}}, $self->{static_log_file};
1174
1175         # initialize the log file variables and file spaces
1176         $self->{msg_file_used} = 0;
1177         $self->{error_file_used} = 0;
1178         $self->cleanLogFILES();
1179      }
1180   }
1181
1182
1183=item $log_file_name = $obj_instance->getLogFile();
1184
1185This function returns the name of the log file to be used for printing
1186log messages.  If no log file is available, this function returns undefined.
1187
1188=cut
1189
1190
1191   sub getLogFile() {
1192      my $self = shift;
1193      my $return_val = undef;
1194      if (
1195          (scalar(@{$self->{log_files}}) != 0) &&
1196          (defined($self->{log_files}[0]))
1197         ) {
1198         $return_val = $self->{log_files}[0];
1199      }
1200      return $return_val;
1201   }
1202
1203
1204=item $error_file_name = $obj_instance->getErrorFile();
1205
1206This function returns the name of the error file to be used for printing
1207error messages.  The error file is derived from the log file; a F<.log>
1208extension is replaced by a F<.error> extension.  If there is no F<.log>
1209extension, then F<.error> is appended to the log file name.  If no
1210log files are defined, this function returns undefined.
1211
1212=cut
1213
1214
1215   sub getErrorFile() {
1216      my $self = shift;
1217      my $return_val = $self->getLogFile();
1218      if (defined $return_val) {
1219         $return_val =~ s/\.log$//g;
1220         $return_val .= '.error';
1221      }
1222      return $return_val;
1223   }
1224
1225
1226   # the following private functions are used for logging
1227
1228
1229   # push items onto the debug level stack
1230   sub debugPush() {
1231      my $self = shift;
1232      if (defined ($self->{debug_level})) {
1233         push @{$self->{debug_store}}, $self->{debug_level};
1234      }
1235      else {
1236         push @{$self->{debug_store}}, "undef";
1237      }
1238      $self->{debug_level} = undef;
1239   }
1240
1241
1242   # pop items from the debug level stack
1243   sub debugPop() {
1244      my $self = shift;
1245      $self->{debug_level} = pop @{$self->{debug_store}};
1246      if (
1247          (!defined ($self->{debug_level})) ||
1248          ($self->{debug_level} eq "undef")
1249         ) {
1250         $self->{debug_level} = undef;
1251      }
1252   }
1253
1254
1255   # remove log files
1256   sub removeLogERROR() {
1257
1258      my $self = shift;
1259      $self->debugPush();
1260      if (
1261          (defined $self->getErrorFile()) &&
1262          (isWritableFile($self->getErrorFile()))
1263         ) {
1264	    unlink $self->getErrorFile() or
1265            $self->logLocal("Unable to remove error file " .
1266                             $self->getErrorFile(), 3);
1267      }
1268      $self->debugPop();
1269   }
1270
1271
1272   sub removeLogMSG() {
1273      my $self = shift;
1274      $self->debugPush();
1275
1276      if (
1277          (defined $self->getLogFile()) &&
1278          (isWritableFile($self->getLogFile()))
1279         ) {
1280            unlink $self->getLogFile() or
1281            $self->logLocal("Unable to remove error file " .
1282                             $self->getLogFile(), 3);
1283      }
1284      $self->debugPop();
1285   }
1286
1287
1288   # invalidate log files
1289   sub invalidateLogFILES() {
1290      my $self = shift;
1291      $self->debugPush();
1292      if (defined $self->getLogFile()) {
1293         $self->logLocal("Invalidating " . $self->getLogFile(), 2);
1294         shift @{$self->{log_files}};
1295         $self->{msg_append_flag} = $self->{error_append_flag} =
1296            $self->{log_append_setting};
1297         $self->{msg_file_used} = $self->{error_file_used} = 0;
1298         $self->cleanLogFILES();
1299      }
1300      $self->debugPop();
1301   }
1302
1303
1304   # clean previous log files
1305   sub cleanLogFILES() {
1306      my $self = shift;
1307      if ($self->{log_append_setting} == 0) {
1308         if ($self->{msg_file_used} == 0) {
1309            $self->removeLogMSG();
1310         }
1311         if ($self->{error_file_used} == 0) {
1312            $self->removeLogERROR();
1313         }
1314      }
1315   }
1316
1317
1318   # close log files
1319   sub closeLogERROR() {
1320      my $self = shift;
1321      my $return_code = 1; # need to return true for success, false for fail
1322
1323      $self->debugPush();
1324      if (!close(ERRLOG) && (defined $self->getErrorFile())) {
1325         $self->logLocal("Cannot close " . $self->getErrorFile(), 3);
1326         $return_code = 0;
1327      }
1328      else {
1329         $return_code = 1;
1330      }
1331      $self->{error_file_open_flag} = 0;
1332      $self->debugPop();
1333      return $return_code;
1334   }
1335
1336
1337   sub closeLogMSG() {
1338      my $self = shift;
1339      my $return_code = 1; # need to return true for success, false for fail
1340
1341      $self->debugPush();
1342      if (!close(MSGLOG) && (defined $self->getLogFile())) {
1343         $self->logLocal("Cannot close " . $self->getLogFile(), 3);
1344         $return_code = 0;
1345      }
1346      else {
1347         $return_code = 1;
1348      }
1349      $self->{msg_file_open_flag} = 0;
1350      $self->debugPop();
1351      return $return_code;
1352   }
1353
1354
1355   # open log files
1356   sub openLogERROR() {
1357      my $self = shift;
1358      my $return_code = 1; # need to return true for success, false for fail
1359
1360      $self->debugPush();
1361      if ((defined $self->getErrorFile()) &&
1362          ($self->{error_file_open_flag} == 0)) {
1363         my $fileop;
1364         $self->{error_file_open_flag} = 1;
1365         if ($self->{error_append_flag} == 0) {
1366            $fileop = '>';
1367            $self->{error_append_flag} = 1;
1368         }
1369         else {
1370            $fileop = '>>';
1371         }
1372         if (open(ERRLOG, $fileop . $self->getErrorFile())) {
1373            autoflush ERRLOG 1;
1374         }
1375         else {
1376            $self->logLocal("Cannot open " . $self->getErrorFile() .
1377               " for logging", 4);
1378            $self->{error_file_open_flag} = 0;
1379         }
1380      }
1381      $return_code = $self->{error_file_open_flag};
1382      $self->debugPop();
1383
1384      # this is 1 if the file stream is open, 0 if not
1385      return $return_code;
1386   }
1387
1388
1389   sub openLogMSG() {
1390      my $self = shift;
1391      my $return_code = 1; # need to return true for success, false for fail
1392
1393      $self->debugPush();
1394      if ((defined $self->getLogFile()) && ($self->{msg_file_open_flag} == 0)){
1395         my $fileop;
1396         $self->{msg_file_open_flag} = 1;
1397         if ($self->{msg_append_flag} == 0) {
1398            $fileop = '>';
1399            $self->{msg_append_flag} = 1;
1400         }
1401         else {
1402            $fileop = '>>';
1403         }
1404
1405         if (open(MSGLOG, $fileop . $self->getLogFile())) {
1406            autoflush MSGLOG 1;
1407         }
1408         else {
1409            $self->logLocal("Cannot open " . $self->getLogFile() .
1410                            " for logging", 4);
1411            $self->{msg_file_open_flag} = 0;
1412         }
1413      }
1414      $return_code = $self->{msg_file_open_flag};
1415      $self->debugPop();
1416
1417      # this is 1 if the file stream is open, 0 if not
1418      return $return_code;
1419   }
1420
1421
1422=item $obj_instance->logAppend($log_append_flag);
1423
1424The C<logAppend()> function takes either C<0> or C<1> as a flag to
1425disable or enable log file appending.  By default, log files are
1426truncated at the start of program execution or logging.  Error files are
1427controlled by this variable as well.  Invalid or undefined calls are ignored.
1428Calling this function with a C<0> argument after the log files have started
1429to be written may cause them to be truncated undesirably.  This function is
1430C<GetOptions()> compliant; if 2 and only 2 variables are passed, the second
1431option is treated as C<$log_append_flag>.
1432
1433=cut
1434
1435
1436   sub logAppend($;$) {
1437      my $self = shift;
1438      my $log_append_flag = shift;
1439      if (defined $_[0]) {
1440         $log_append_flag = shift;
1441      }
1442      if (
1443          (defined ($log_append_flag)) &&
1444          (($log_append_flag eq "0") ||
1445           ($log_append_flag eq "1"))
1446         ) {
1447         $self->{log_append_setting} = $self->{msg_append_flag} =
1448            $self->{error_append_flag} = $log_append_flag;
1449      }
1450   }
1451
1452
1453=item $obj_instance->logLocal($log_message, $log_level);
1454
1455The C<logLocal()> function takes two arguments.  The C<$log_message>
1456argument specifies the message to be written to the log file.  The
1457C<$log_level> argument specifies the level at which C<$log_message> is printed.
1458The active level of logging is set via the C<setDebugLevel()> function.
1459Only messages at C<$log_level> less than or equal to the active debug
1460level are logged.  The default debug level is undefined.  Note, a trailing
1461new line, if it exists, is stripped from the log message.
1462
1463=cut
1464
1465
1466   sub logLocal($$) {
1467      my $self = shift;
1468      my $log_message = shift;
1469      my $log_level = shift;
1470
1471      if ((!defined $log_level) || ($log_level =~ /\D/)) {
1472         $log_level = 1;
1473      }
1474
1475      if (defined $log_message) {
1476         chomp $log_message; # strip end new line, if it exists
1477
1478         $log_message = getLogfileDate() . $log_message;
1479         push @{$self->{debug_queue}}, [ $log_message, $log_level ];
1480
1481         if ((defined ($self->getDebugLevel())) &&
1482                      ($self->getDebugLevel() > -1)) {
1483            while (
1484                   (defined(my $log_record = $self->{debug_queue}[0])) &&
1485                   (defined($self->getLogFile()))
1486                  ) {
1487               ($log_message, $log_level) = @{$log_record};
1488               if (
1489                   (
1490                    ($log_level <= $self->getDebugLevel()) && # debug level
1491                    ($self->openLogMSG())                  && # check log file
1492                    (print MSGLOG "$log_message\n")        && # print message
1493                    ($self->closeLogMSG())                 && # close log file
1494                    ($self->{msg_file_used} = 1)              # log file used
1495                   ) ||
1496                   (
1497                    ($log_level > $self->getDebugLevel())     # bad dbg level
1498                   )
1499                  ) {
1500                  # log message is successfully processed, so shift it off
1501                  shift @{$self->{debug_queue}};
1502               }
1503               else {
1504                  $self->debugPush();
1505                  $self->logLocal("Cannot log message \'$log_message\' to " .
1506                     $self->getLogFile() . " = " .  $!, 9);
1507                  $self->invalidateLogFILES();
1508                  $self->debugPop();
1509               }
1510            }
1511         }
1512      }
1513      else {
1514         $self->logLocal("logLocal() called without any parameters!",3);
1515      }
1516
1517      while ($#{$self->{debug_queue}} >= $self->{max_debug_queue_size}) {
1518         # expire old entries; this needs to happen if $self->{debug_level}
1519         # is undefined or there is no writable log file, otherwise the
1520         # queue could exhaust RAM.
1521         shift @{$self->{debug_queue}};
1522      }
1523   }
1524
1525
1526=item $obj_instance->logError($log_message,$flag);
1527
1528The C<logError()> function takes two arguments, the second one being optional.
1529The C<$log_message> argument specifies the message to be written to the error
1530file. If the C<$flag> argument is defined and is non-zero, the C<$log_message>
1531is also written to STDERR. The C<$log_message> is also passed to C<logLocal>.
1532A message passed via logError() will always get logged to the log file
1533regardles of the debug level.  Note, a trailing new line, if it exists, is
1534stripped from the log message.
1535
1536=cut
1537
1538
1539   sub logError($;$) {
1540
1541      my $self = shift;
1542      my $log_message = shift;
1543      my $flag = shift;
1544      if (defined $log_message) {
1545         chomp $log_message;  # strip end new line, if it exists
1546         $self->logLocal($log_message, 0);
1547
1548         #printing error message to STDERR if flag is non zero.
1549         if((defined($flag)) && ($flag ne '0')) {
1550            print STDERR "$log_message\n";
1551         }
1552
1553         $log_message = getLogfileDate() . $log_message;
1554         push(@{$self->{error_queue}}, $log_message);
1555
1556         while (
1557                (defined(my $log_message = $self->{error_queue}[0])) &&
1558                (defined($self->getErrorFile()))
1559               ) {
1560
1561            if (
1562                ($self->openLogERROR()) &&
1563                (print ERRLOG "$log_message\n") &&
1564                ($self->closeLogERROR()) &&
1565                ($self->{error_file_used} = 1) # that is an '='
1566               ) {
1567	       shift @{$self->{error_queue}};
1568            }
1569            else {
1570               $self->debugPush();
1571               $self->logLocal("Cannot log message \'$log_message\' to " .
1572                  $self->getErrorFile() . " = $!", 6);
1573               $self->invalidateLogFILES();
1574               $self->debugPop();
1575            }
1576         }
1577      }
1578      else {
1579         $self->logLocal("logError() called without any parameters!",3);
1580      }
1581
1582      while ($#{$self->{error_queue}} >= $self->{max_debug_queue_size}) {
1583         # expire old entries; this needs to happen if $self->{debug_level}
1584         # is undefined or there is no writable log file, otherwise the
1585         # queue could exhaust RAM.
1586         shift @{$self->{error_queue}};
1587      }
1588   }
1589
1590
1591=item $obj_instance->bail($log_message);
1592
1593The C<bail()> function takes a single required argument.  The C<$log_message>
1594argument specifies the message to be passed to C<logLocal()> and displayed
1595to the screen in using the C<warn> function.  All messages passed to C<bail()>
1596are logged regardless of the debug level.  The C<bail()> function
1597calls C<exit(1)> to terminate the program.  Optionally, a second positive
1598integer argument can be passed as the exit code to use.  Note, a trailing
1599new line, if it exists, is stripped from the end of the line.
1600
1601=cut
1602
1603
1604   sub bail($;$) {
1605      my $self = shift;
1606      my $log_message = shift;
1607      my $exit_code = shift;
1608
1609      if (
1610          (!defined $exit_code) ||
1611          ($exit_code !~ /^\d+$/)
1612         ) {
1613         $exit_code = 1;
1614      }
1615      if (defined $log_message) {
1616         chomp $log_message;  # strip end new line, if it exists
1617
1618         $self->logError($log_message);
1619         print STDERR $log_message, "\n";
1620      }
1621
1622      exit $exit_code;
1623   }
1624
1625
1626# Functional Class : modified methods
1627
1628=item $getopts_error_code = $obj_instance->TIGR_GetOptions(@getopts_arguments);
1629
1630This function extends C<Getopt::Long::GetOptions()>.  It may be used
1631as C<GetOptions()> is used.  Extended functionality eliminates the need
1632to C<eval {}> the block of code containing the function.  Further, TIGR
1633standard options, such as C<-help>, are defined implicitly.  Using this
1634function promotes proper module behavior.  Log and error files from
1635previous runs are removed if the log file append option, C<-appendlog>,
1636is not set to 1.
1637
1638The following options are defined by this function:
1639
1640=over
1641
1642=item -appendlog APPEND_FLAG
1643
1644Passing '1' to this argument turns on log file appending.
1645
1646=item -debug DEBUG_LEVEL
1647
1648Set debugging to DEBUG_LEVEL.
1649
1650=item -logfile LOG_FILE_NAME
1651
1652Set the default TIGR Foundation log file to LOG_FILE_NAME.
1653
1654=item -version, -V
1655
1656Print version information and exit.
1657
1658=item -help, -h
1659
1660Print help information and exit.
1661
1662=item -depend
1663
1664Print dependency information and exit.
1665
1666=back
1667
1668Regular C<GetOptions()> may still be used, however the C<TIGR_GetOptions()>
1669function eliminates some of the confusing issues with setting log files
1670and debug levels.  B<The options defined by C<TIGR_GetOptions()> cannot be
1671overridden or recorded>.  To get the log file and debug level after parsing
1672the command line, use C<getLogFile()> and C<getDebugLevel()>.  C<GetOptions()>
1673default variables, ie. those of the form C<$opt_I<optionname>>, are not
1674supported.  This function will return 1 on success.
1675
1676=cut
1677
1678
1679   sub TIGR_GetOptions(@) {
1680      my $self = shift;
1681      my @user_options = @_;
1682
1683      my $appendlog_var = undef;
1684      my $logfile_var = undef;
1685      my $debug_var = undef;
1686      my $version_var = undef;
1687      my $help_var = undef;
1688      my $depend_var = undef;
1689
1690      # these foundation options support the defaults
1691      my @foundation_options = (
1692         "appendlog=i" => \$appendlog_var,
1693         "logfile=s" => \$logfile_var,
1694         "debug=i" => \$debug_var,
1695         "version|V" => \$version_var,
1696         "help|h" => \$help_var,
1697         "depend" => \$depend_var
1698      );
1699
1700      Getopt::Long::Configure('no_ignore_case');
1701      my $getopt_code = eval 'GetOptions (@user_options, @foundation_options)';
1702
1703      if ((defined $help_var) && ($help_var =~ /^(.*)$/))  {
1704         $self->printHelpInfoAndExit();
1705      }
1706
1707      if ((defined $version_var) && ($version_var =~ /^(.*)$/)) {
1708         $self->printVersionInfoAndExit();
1709      }
1710
1711      if ((defined $depend_var) && ($depend_var =~ /^(.*)$/)) {
1712         $self->printDependInfoAndExit();
1713      }
1714
1715      if ((defined $appendlog_var) && ($appendlog_var =~ /^(.*)$/)) {
1716	 $appendlog_var = $1;
1717         $self->logAppend($appendlog_var);
1718      }
1719
1720      if ((defined $logfile_var) && ($logfile_var =~ /^(.*)$/))  {
1721	 $logfile_var = $1;
1722         $self->setLogFile($logfile_var);
1723      }
1724
1725      if ((defined $debug_var) && ($debug_var =~ /^(.*)$/)) {
1726	 $debug_var = $1;
1727         $self->setDebugLevel($debug_var);
1728      }
1729
1730      # remove old log files, if necessary
1731      for (
1732           my $file_control_var = 0;
1733           $file_control_var <= $#{$self->{log_files}};
1734           $file_control_var++
1735          ) {
1736          $self->cleanLogFILES();
1737          push(@{$self->{log_files}}, shift @{$self->{log_files}});
1738      }
1739      return $getopt_code;
1740   }
1741
1742   DESTROY {
1743      my $self = shift;
1744      $self->{finish_time} = time;
1745      my $time_difference = $self->{finish_time} - $self->{start_time};
1746      my $num_days = int($time_difference / 86400); # there are 86400 sec/day
1747      $time_difference -= $num_days * 86400;
1748      my $num_hours = int($time_difference / 3600); # there are 3600 sec/hour
1749      $time_difference -= $num_hours * 3600;
1750      my $num_min = int($time_difference / 60); # there are 60 sec/hour
1751      $time_difference -= $num_min * 60;
1752      my $num_sec = $time_difference; # the left overs are seconds
1753      my $time_str = sprintf "%03d-%02d:%02d:%02d", $num_days, $num_hours,
1754         $num_min, $num_sec;
1755      $self->logLocal("FINISH: " . $self->getProgramInfo('name') .
1756         ", elapsed ".$time_str ,0);
1757   }
1758}
1759
1760=back
1761
1762=head1 USAGE
1763
1764To use this module, load the C<TIGR::Foundation> package
1765via the C<use> function.  Then, create a new instance of the
1766object via the C<new()> method, as shown below.  If applicable,
1767C<START> and C<FINISH> log messages are printed when the object
1768is created and destroyed, respectively.  It is advisable to
1769keep the instance of the object in scope for the whole program
1770to achieve maximum functionality.
1771
1772An example script using this module follows:
1773
1774   use strict;
1775   use TIGR::Foundation;
1776
1777   my $tfobject = new TIGR::Foundation;
1778
1779   MAIN:
1780   {
1781      # The following dependencies are not used in
1782      # this script, but are provided as an example.
1783
1784      my @DEPEND = ("/usr/bin/tee", "/sbin/stty");
1785
1786      # The user defined $VERSION variable is usable by Perl.
1787      # The auto defined $REVISION variable stores the RCS/CVS revision
1788      # The user defined $VERSION_STRING reports both.
1789
1790      my $VERSION = '1.0';
1791      my $REVISION = (qw$Revision: 1.1.1.1 $)[-1];
1792      my $VERSION_STRING = "$VERSION (Build $REVISION)";
1793
1794      my $HELP_INFO = "This is my help\n";
1795
1796      # All of the necessary information must be passed
1797      # to the foundation object instance, as below.
1798
1799      $tfobject->addDependInfo(@DEPEND);
1800      $tfobject->setVersionInfo($VERSION_STRING);
1801      $tfobject->setHelpInfo($HELP_INFO);
1802
1803      my $input_file;
1804      my $output_file;
1805
1806      $tfobject->TIGR_GetOptions("input=s" => \$input_file,
1807                                 "output=s" => \$output_file);
1808
1809      # GetOptions(), and subsequently TIGR_GetOptions(), leaves
1810      # the variables unchanged if no corresponding command line
1811      # arguments are parsed.  The passed variables are checked below.
1812
1813      if (defined $input_file) {
1814
1815         # The log message is written only if debugging is turned on.
1816         # By default, debugging is off.  To turn on debugging, use the
1817         # '-debug DEBUG_LEVEL' option on the command line.
1818         # In this example, '-debug 1' would set debugging to level 1
1819         # and report these log messages.
1820
1821         $tfobject->logLocal("My input file is $input_file", 1);
1822      }
1823
1824      print "Hello world", "\n";
1825
1826      # This case is similar to the previous one above...
1827      if (defined $output_file) {
1828         $tfobject->logLocal("My output file is $output_file.", 1);
1829      }
1830   }
1831
1832=cut
1833
18341;
1835
1836
1837
1838
1839