source: LMDZ6/branches/blowing_snow/tools/fcm/lib/Fcm/CfgFile.pm @ 4484

Last change on this file since 4484 was 3435, checked in by Laurent Fairhead, 6 years ago

"Historic" :-) commit merging the physics branch used for DYNAMICO with the LMDZ trunk.
The same physics branch can now be used seamlessly with the traditional lon-lat LMDZ
dynamical core and DYNAMICO.
Testing consisted in running a lon-lat LMDZ bucket simulation with the NPv6.1 physics package
with the original trunk sources and the merged sources. Tests were succesful in the sense that
numeric continuity was preserved in the restart files from both simulation. Further tests
included running both versions of the physics codes for one year in a LMDZOR setting in which
the restart files also came out identical.

Caution:

  • as the physics package now manages unstructured grids, grid information needs to be transmitted

to the surface scheme ORCHIDEE. This means that the interface defined in surf_land_orchidee_mod.F90
is only compatible with ORCHIDEE version orchidee2.1 and later versions. If previous versions of
ORCHIDEE need to be used, the CPP key ORCHIDEE_NOUNSTRUCT needs to be set at compilation time.
This is done automatically if makelmdz/makelmdz_fcm are called with the veget orchidee2.0 switch

  • due to a limitation in XIOS, the time at which limit conditions will be read in by DYNAMICO will be

delayed by one physic timestep with respect to the time it is read in by the lon-lat model. This is caused
by the line

IF (MOD(itime-1, lmt_pas) == 0 .OR. (jour_lu /= jour .AND. grid_type /= unstructured)) THEN ! time to read

in limit_read_mod.F90

Work still needed on COSP integration and XML files for DYNAMICO

EM, YM, LF

File size: 25.2 KB
Line 
1#!/usr/bin/perl
2# ------------------------------------------------------------------------------
3# NAME
4#   Fcm::CfgFile
5#
6# DESCRIPTION
7#   This class is used for reading and writing FCM config files. A FCM config
8#   file is a line-based text file that provides information on how to perform
9#   a particular task using the FCM system.
10#
11# COPYRIGHT
12#   (C) Crown copyright Met Office. All rights reserved.
13#   For further details please refer to the file COPYRIGHT.txt
14#   which you should have received as part of this distribution.
15# ------------------------------------------------------------------------------
16
17package Fcm::CfgFile;
18
19# Standard pragma
20use warnings;
21no warnings 'uninitialized';
22use strict;
23
24# Standard modules
25use Carp;
26use File::Basename;
27use File::Path;
28use File::Spec;
29use File::Spec::Functions;
30
31# FCM component modules
32use Fcm::Util;
33
34# Local module variables
35my $expand_type   = 'bld|ext'; # config file type that needs variable expansions
36
37# ------------------------------------------------------------------------------
38# SYNOPSIS
39#   $cfgfile = Fcm::CfgFile->new (CONFIG=> $config, SRC => $src, TYPE => $type);
40#
41# DESCRIPTION
42#   This method constructs a new instance of the Fcm::CfgFile class.
43#
44# ARGUMENTS
45#   CONFIG - reference to a Fcm::Config instance
46#   SRC    - configuration file source
47#   TYPE   - type of expected configuration file
48# ------------------------------------------------------------------------------
49
50sub new {
51  my $this  = shift;
52  my %args  = @_;
53  my $class = ref $this || $this;
54
55  my $self = {
56    CONFIG     => exists $args{CONFIG} ? $args{CONFIG} : &main::cfg,
57    SRC        => exists $args{SRC}    ? $args{SRC}    : undef,
58    TYPE       => exists $args{TYPE}   ? $args{TYPE}   : undef,
59
60    # Version of the configuration file
61    VERSION    => undef,
62
63    # List of references to hash tables for each line in the file
64    LINES      => [],
65
66    # Actual source of configuration file
67    ACTUAL_SRC => undef,
68    PEGREV     => undef,
69  };
70  bless $self, $class;
71  return $self;
72}
73
74# ------------------------------------------------------------------------------
75# SYNOPSIS
76#   $config = $cfgfile->config;
77#
78# DESCRIPTION
79#   This method returns a reference to the Fcm::Config instance.
80# ------------------------------------------------------------------------------
81
82sub config {
83  my $self = shift;
84
85  return $self->{CONFIG};
86}
87
88# ------------------------------------------------------------------------------
89# SYNOPSIS
90#   $src = $cfgfile->src ();
91#   $cfgfile->src ($src);
92#
93# DESCRIPTION
94#   This method returns the specified source of the configuration file. If an
95#   argument is specified, the source of the configuration file is modified to
96#   the value of the argument.
97# ------------------------------------------------------------------------------
98
99sub src {
100  my $self = shift;
101
102  if (@_) {
103    $self->{SRC} = shift;
104  }
105
106  return $self->{SRC};
107}
108
109# ------------------------------------------------------------------------------
110# SYNOPSIS
111#   $src = $cfgfile->actual_src ();
112#
113# DESCRIPTION
114#   This method returns the actual source of the configuration file. If an
115#   argument is specified, the source of the configuration file is modified to
116#   the value of the argument.
117# ------------------------------------------------------------------------------
118
119sub actual_src {
120  my $self = shift;
121
122  return $self->{ACTUAL_SRC};
123}
124
125# ------------------------------------------------------------------------------
126# SYNOPSIS
127#   $rev = $cfgfile->pegrev ();
128#
129# DESCRIPTION
130#   This method returns the peg revision of the configuration file.
131# ------------------------------------------------------------------------------
132
133sub pegrev {
134  my $self = shift;
135
136  return $self->{PEGREV};
137}
138
139# ------------------------------------------------------------------------------
140# SYNOPSIS
141#   $type = $cfgfile->type ();
142#   $cfgfile->type ($type);
143#
144# DESCRIPTION
145#   This method returns the configuration file type. If an argument is
146#   specified, the type is set to the value of the argument.
147# ------------------------------------------------------------------------------
148
149sub type {
150  my $self = shift;
151
152  if (@_) {
153    $self->{TYPE} = shift;
154  }
155
156  return $self->{TYPE};
157}
158
159# ------------------------------------------------------------------------------
160# SYNOPSIS
161#   $version = $cfgfile->version ();
162#
163# DESCRIPTION
164#   This method returns the version of the configuration file.
165# ------------------------------------------------------------------------------
166
167sub version {
168  my $self = shift;
169
170  return $self->{VERSION};
171}
172
173# ------------------------------------------------------------------------------
174# SYNOPSIS
175#   @lines = $cfgfile->lines ();
176#
177# DESCRIPTION
178#   This method returns an array containing all "lines" in the configuration
179#   file. Each "line" is a reference to a hash table with the following keys:
180#
181#   SRC     - the source of the configuration file
182#   NUMBER  - the line number in the source
183#   LABEL   - the label of the of the configuration line
184#   VALUE   - the value of the configuration line
185#   COMMENT - comment in the configuration line
186# ------------------------------------------------------------------------------
187
188sub lines {
189  my $self = shift;
190
191  return @{ $self->{LINES} };
192}
193
194# ------------------------------------------------------------------------------
195# SYNOPSIS
196#   $line = $cfgfile->line ($line_number);
197#
198# DESCRIPTION
199#   This method return a "line" at $line_number in the configuration file. A
200#   "line" is a reference to a hash table with the following keys:
201#
202#   SRC     - the source of the configuration file
203#   NUMBER  - the line number in the source
204#   LABEL   - the label of the of the configuration line
205#   VALUE   - the value of the configuration line
206#   COMMENT - comment in the configuration line
207# ------------------------------------------------------------------------------
208
209sub line {
210  my $self     = shift;
211  my $line_num = shift;
212
213  if (exists $self->{LINES}[$line_num]) {
214    return $self->{LINES}[$line_num]; # returns a ref to a label:value pair hash
215
216  } else {
217    return undef;
218  }
219}
220
221# ------------------------------------------------------------------------------
222# SYNOPSIS
223#   $cfgfile->add_line (
224#     LABEL   => $label,
225#     VALUE   => $value,
226#     COMMENT => $comment,
227#     SRC     => $src,
228#     NUMBER  => $line_number
229#   );
230#
231# DESCRIPTION
232#   This method adds a "line" to the configuration file. LABEL is the
233#   configuration line label. VALUE is the configuration line value. COMMENT
234#   is the comment in the line. VALUE should only be specified if LABEL is
235#   specified. COMMENT can be specified without LABEL. In such case, the whole
236#   line is a comment line. A blank line is inserted if no argument is
237#   specified. SRC can be specified to indicate the name of the source file
238#   from which this line is obtained. If not specified, the source file of the
239#   current configuration file is used. NUMBER can be specified to indicate
240#   the line number of the source file from which this line is obtained.
241# ------------------------------------------------------------------------------
242
243sub add_line {
244  my $self = shift;
245  my %args = @_;
246
247  my $line = {
248    SRC     => exists $args{SRC    } ? $args{SRC    } : $self->actual_src,
249    NUMBER  => exists $args{NUMBER } ? $args{NUMBER } : 0,
250    LABEL   => exists $args{LABEL  } ? $args{LABEL  } : '',
251    VALUE   => exists $args{VALUE  } ? $args{VALUE  } : '',
252    COMMENT => exists $args{COMMENT} ? $args{COMMENT} : '',
253  };
254
255  push @{ $self->{LINES} }, $line;
256
257  return $line;
258}
259
260# ------------------------------------------------------------------------------
261# SYNOPSIS
262#   $cfgfile->add_comment_block ($line1, [$line2, ...]);
263#
264# DESCRIPTION
265#   This method adds a comment block to the configuration file. Each argument
266#   represents a line in the comment block.
267# ------------------------------------------------------------------------------
268
269sub add_comment_block {
270  my $self = shift;
271
272  $self->add_line (COMMENT => '-' x 78,);
273
274  while (my $line = shift @_) {
275    $self->add_line (COMMENT => $line,)
276  }
277
278  $self->add_line (COMMENT => '-' x 78,);
279  $self->add_line;
280
281  return;
282}
283
284# ------------------------------------------------------------------------------
285# SYNOPSIS
286#   $cfgfile->add_header ();
287#
288# DESCRIPTION
289#   This method adds a header to the configuration file, with its type and
290#   type version. It returns 1 on success.
291# ------------------------------------------------------------------------------
292
293sub add_header {
294  my $self = shift;
295
296  return undef unless $self->{TYPE};
297
298  $self->{VERSION} = $self->config->setting ('CFG_VERSION', uc ($self->{TYPE}))
299    if not $self->{VERSION};
300
301  $self->add_comment_block ('File header');
302
303  $self->add_line (
304    LABEL => $self->config->setting (qw/CFG_LABEL CFGFILE TYPE/),
305    VALUE => $self->{TYPE},
306  );
307
308  $self->add_line (
309    LABEL => $self->config->setting (qw/CFG_LABEL CFGFILE VERSION/),
310    VALUE => $self->{VERSION},
311  );
312
313  $self->add_line;
314
315  return 1;
316}
317
318# ------------------------------------------------------------------------------
319# SYNOPSIS
320#   $exist = $cfg->label_exists ($label);
321#   @lines = $cfg->label_exists ($label);
322#
323# DESCRIPTION
324#   This method returns the (number of) "lines" with their LABEL matching the
325#   argument $label.
326# ------------------------------------------------------------------------------
327
328sub label_exists {
329  my $self  = shift;
330  my $label = shift;
331
332  return grep {$_->{LABEL} eq $label} @{ $self->{LINES} };
333}
334
335# ------------------------------------------------------------------------------
336# SYNOPSIS
337#   $mtime = $cfgfile->mtime ();
338#
339# DESCRIPTION
340#   This method returns the modified time of the configuration file source.
341# ------------------------------------------------------------------------------
342
343sub mtime {
344  my $self  = shift;
345  my $mtime = undef;
346
347  if (-f $self->{SRC}) {
348    $mtime = (stat $self->{SRC})[9];
349  }
350
351  return $mtime;
352}
353
354# ------------------------------------------------------------------------------
355# SYNOPSIS
356#   $read = $cfgfile->read_cfg ();
357#
358# DESCRIPTION
359#   This method reads the current configuration file. It returns the number of
360#   lines read from the config file, or "undef" if it fails. The result is
361#   placed in the LINES array of the current instance, and can be accessed via
362#   the "lines" method.
363# ------------------------------------------------------------------------------
364
365sub read_cfg {
366  my $self = shift;
367
368  my @lines = $self->_get_cfg_lines;
369
370  # List of CFG labels
371  my %cfg_labels = %{ $self->config->setting ('CFG_LABEL') };
372
373  # List of CFG types that need INC declarations expansion
374  my %exp_inc    = ();
375  for (split (/,/, $self->config->setting ('CFG_EXP_INC'))) {
376    $exp_inc{uc ($_)} = 1;
377  }
378
379  # List of CFG labels that are reserved keywords
380  my %cfg_keywords = ();
381  for (split (/,/, $self->config->setting ('CFG_KEYWORD'))) {
382    $cfg_keywords{$cfg_labels{$_}} = 1;
383  }
384
385  # Loop each line, to separate lines into label : value pairs
386  my $cont = undef;
387  my $here = undef;
388  for my $line_num (1 .. @lines) {
389    my $line = $lines[$line_num - 1];
390    chomp $line;
391
392    my $label   = '';
393    my $value   = '';
394    my $comment = '';
395
396    # If this line is a continuation, set $start to point to the line that
397    # starts this continuation. Otherwise, set $start to undef
398    my $start   = defined ($cont) ? $self->line ($cont) : undef;
399
400    if ($line =~ /^(\s*#.*)$/) { # comment line
401      $comment = $1;
402
403    } elsif ($line =~ /\S/) {    # non-blank line
404      if (defined $cont) {
405        # Previous line has a continuation mark
406        $value = $line;
407
408        # Separate value and comment
409        if ($value =~ s/((?:\s+|^)#\s+.*)$//) {
410          $comment = $1;
411        }
412
413        # Remove leading spaces
414        $value =~ s/^\s*\\?//;
415
416        # Expand environment variables
417        $value = $self->_expand_variable ($value, 1) if $value;
418
419        # Expand internal variables
420        $value = $self->_expand_variable ($value) if $value;
421
422        # Get "line" that begins the current continuation
423        ($start->{VALUE} .= $value) =~ s/\\$//;
424
425      } else {
426        # Previous line does not have a continuation mark
427        if ($line =~ /^\s*(\S+)(?:\s+(.*))?$/) {
428          # Check line contains a valid label:value pair
429          $label = $1;
430          $value = defined ($2) ? $2 : '';
431
432          # Separate value and comment
433          if ($value =~ s/((?:\s+|^)#\s+.*)$//) {
434            $comment = $1;
435          }
436
437          # Remove trailing spaces
438          $value =~ s/\s+$//;
439
440          # Value begins with $HERE?
441          $here  = ($value =~ /\$\{?HERE\}?(?:[^A-Z_]|$)/);
442
443          # Expand environment variables
444          $value = $self->_expand_variable ($value, 1) if $value;
445
446          # Expand internal variables
447          $value = $self->_expand_variable ($value) if $value;
448        }
449      }
450
451      # Determine whether current line ends with a continuation mark
452      if ($value =~ s/\\$//) {
453        $cont = scalar ($self->lines) unless $cont;
454
455      } else {
456        $cont = undef;
457      }
458    }
459
460    if (exists $exp_inc{uc ($self->type)} and
461        uc ($start ? $start->{LABEL} : $label) eq $cfg_labels{INC} and
462        not defined $cont) {
463      # Current configuration file requires expansion of INC declarations
464      # The start/current line is an INC declaration
465      # The current line is not a continuation or is the end of the continuation
466
467      # Get lines from an "include" configuration file
468      my $src = ($start ? $start->{VALUE} : $value);
469      $src   .= '@' . $self->pegrev if $here and $self->pegrev;
470
471      if ($src) {
472        # Invoke a new instance to read the source
473        my $cfg = Fcm::CfgFile->new (
474          SRC    => expand_tilde ($src),
475          TYPE   => $self->type,
476          CONFIG => $self->config,
477        );
478
479        $cfg->read_cfg;
480
481        # Add lines to the "LINES" array in the current configuration file
482        $comment = 'INC ' . $src . ' ';
483        $self->add_line (
484          COMMENT => $comment . '# Start',
485          NUMBER  => ($start ? $start->{NUMBER} : $line_num),
486        );
487        $self->add_line (%{ $_ }) for (($cfg->lines));
488        $self->add_line (COMMENT => $comment . '# End');
489
490      } else {
491        $self->add_line (NUMBER  => $line_num);
492        w_report 'Warning: ', $self->actual_src, ': line ', $line_num,
493                 ': empty INC declaration.' if $self->config->verbose > 2;
494      }
495
496    } else {
497      # Push label:value pair into "LINES" array
498      $self->add_line (
499        LABEL   => $label,
500        VALUE   => ($label ? $value : ''),
501        COMMENT => $comment,
502        NUMBER  => $line_num,
503      );
504    }
505
506    next if defined $cont; # current line not a continuation
507
508    my $slabel = ($start ? $start->{LABEL} : $label);
509    my $svalue = ($start ? $start->{VALUE} : $value);
510    next unless $slabel;
511
512    # Check config file type and version
513    if (uc ($slabel) eq $cfg_labels{CFGFILE}{TYPE}) {
514      $self->type ($svalue);
515
516    } elsif (uc ($slabel) eq $cfg_labels{CFGFILE}{VERSION}) {
517      $self->version ($svalue);
518    }
519
520    # Set internal variable
521    $slabel =~ s/^\%//; # Remove leading "%" from label
522
523    $self->config->assign_variable (
524      LABEL => $slabel,
525      VALUE => $svalue,
526    ) unless exists $cfg_keywords{$slabel};
527  }
528
529  return $self->lines;
530
531}
532
533# ------------------------------------------------------------------------------
534# SYNOPSIS
535#   $rc = $cfgfile->print_cfg ($file);
536#
537# DESCRIPTION
538#   This method prints the content of current configuration file. If no
539#   argument is specified, it prints output to the standard output. If $file
540#   is specified, and is a writable file name, the output is sent to the file.
541#   If the file already exists, its content is compared to the current output.
542#   Nothing will be written if the content is unchanged. Otherwise, for typed
543#   configuration files, the existing file is renamed using a prefix that
544#   contains its last modified time. The method returns 1 if there is no
545#   error.
546# ------------------------------------------------------------------------------
547
548sub print_cfg {
549  my $self = shift;
550  my $file = shift;
551
552  # Count maximum number of characters in the labels, (for pretty printing)
553  my $max_label_len = 0;
554  for my $line (@{ $self->{LINES} }) {
555    next unless $line->{LABEL};
556    my $label_len  = length $line->{LABEL};
557    $max_label_len = $label_len if $label_len > $max_label_len;
558  }
559
560  # Output string
561  my $out = '';
562
563  # Append each line of the config file to the output string
564  for my $line (@{ $self->{LINES} }) {
565    my $label   = $line->{LABEL};
566    my $value   = $line->{VALUE};
567    my $comment = $line->{COMMENT};
568
569    if ($label) {
570      # Line up label/value for pretty printing
571      $label   = $label . ' ' x ($max_label_len - length ($label));
572      $comment =~ s/^\s+/ / if $comment;
573
574      $out .= $label;
575      $out .= ' ' . $value if defined $value;
576
577    } else {
578      # Make sure comments begin with a "#"
579      $comment = '# ' . $comment if $comment and $comment !~ /^\s*($|#)/;
580      $comment =~ s/^\s*//;
581    }
582
583    $out .= $comment if $comment;
584    $out .= "\n";
585  }
586
587  if ($out) {
588
589    my $old_select = select;
590
591    # Open file if necessary
592    if ($file) {
593      # Make sure the host directory exists and is writable
594      my $dirname = dirname $file;
595      if (not -d $dirname) {
596        print 'Make directory: ', $dirname, "\n" if $self->config->verbose;
597        mkpath $dirname;
598      }
599      croak 'Cannot write to config file directory: "', $dirname, '", abort'
600        unless -d $dirname and -w $dirname;
601
602      # If config file already exists, make sure it is writable
603      if (-f $file) {
604        if (-r $file) {
605          # Read old config file to see if content has changed
606          open IN, '<', $file;
607          my $in_lines = '';
608          while (my $line = <IN>) {
609            $in_lines .= $line;
610          }
611          close IN;
612
613          # Return if content is up-to-date
614          if ($in_lines eq $out) {
615            print 'No change in ', lc ($self->{TYPE}), ' cfg: ', $file,
616                  "\n" if $self->config->verbose > 1 and $self->{TYPE};
617            return;
618          }
619        }
620
621        if (-w $file) {
622          if ($self->{TYPE}) {
623            # Existing config file writable, rename it using its time stamp
624            my $mtime = (stat $file)[9];
625            my ($sec, $min, $hour, $mday, $mon, $year) = (gmtime $mtime)[0 .. 5];
626            my $timestamp = sprintf '%4d%2.2d%2.2d_%2.2d%2.2d%2.2d_',
627                            $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
628            my $oldfile   = catfile $dirname, $timestamp . basename ($file);
629            rename $file, $oldfile;
630            print 'Renamed existing ', lc ($self->{TYPE}), ' cfg: ',
631                  $oldfile, "\n" if $self->config->verbose > 1;
632          }
633        } else {
634          # Existing config file not writable, throw an error
635          croak 'Config file "', $file, '" not writable, abort';
636        }
637      }
638
639      # Open file and select file handle
640      open OUT, '>', $file
641        or croak 'Cannot write to config file "', $file, '" (', $!, '), abort';
642      select OUT;
643    }
644
645    # Print output
646    print $out;
647
648    # Close file if necessary
649    if ($file) {
650      select $old_select;
651      close OUT;
652
653      if ($self->{TYPE} and $self->config->verbose > 1) {
654        print 'Generated ', lc ($self->{TYPE}), ' cfg: ', $file, "\n";
655      } elsif ($self->config->verbose > 2) {
656        print 'Generated cfg: ', $file, "\n";
657      }
658    }
659
660  } else {
661
662    # Warn if nothing to print
663    my $warning = 'Empty configuration';
664    $warning   .= ' - nothing written to file: "' . $file . '"' if $file;
665    carp $warning if $self->{TYPE};
666
667  }
668
669  return 1;
670}
671
672# ------------------------------------------------------------------------------
673# SYNOPSIS
674#   @lines = $self->_get_cfg_lines ();
675#
676# DESCRIPTION
677#   This internal method reads from a configuration file residing in a
678#   Subversion repository or in the normal file system.
679# ------------------------------------------------------------------------------
680
681sub _get_cfg_lines {
682  my $self  = shift;
683  my @lines = ();
684
685  my $verbose = $self->config->verbose;
686
687  # Expand URL keywords if necessary
688  {
689    my $src = expand_url_keyword (URL => $self->src, CFG => $self->config);
690    $self->src ($src) if $src ne $self->src;
691  }
692
693  if (&is_url ($self->src)) {
694    # Config file resides in a SVN repository
695    # --------------------------------------------------------------------------
696    # Set URL source and version
697    my $src = $self->src;
698    my $rev = 'HEAD';
699
700    # Extract version from source if it exists
701    if ($src =~ s/@(.+)$//) {
702      $rev = $1;
703    }
704
705    # Expand revision keyword, if required
706    $rev = expand_rev_keyword (REV => $rev, URL => $src, HEAD => 1);
707
708    # Check whether URL is a config file
709    my $rc;
710    my @cmd = (qw/svn cat/, $src . '@' . $rev);
711    @lines = &run_command (
712      \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
713    );
714
715    # Error in "svn cat" command
716    if ($rc) {
717      # See whether specified config file is a known type
718      my %cfgname = %{ $self->config->setting ('CFG_NAME') };
719      my $key     = uc $self->type;
720      my $file    = exists $cfgname{$key} ? $cfgname{$key} : '';
721
722      # If config file is a known type, specified URL may be a directory
723      if ($file) {
724        # Check whether a config file with a default name exists in the URL
725        my $sep  = $self->config->setting (qw/MISC DIR_SEPARATOR/);
726        my $path = $src . $sep . $file;
727        my @cmd  = (qw/svn cat/, $path . '@' . $rev);
728
729        @lines = &run_command (
730          \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
731        );
732
733        # Check whether a config file with a default name exists under the "cfg"
734        # sub-directory of the URL
735        if ($rc) {
736          my $cfgdir = $self->config->setting (qw/DIR CFG/);
737          $path   = $src . $sep . $cfgdir . $sep . $file;
738          my @cmd = (qw/svn cat/, $path . '@' . $rev);
739
740          @lines  = &run_command (
741            \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
742          );
743        }
744
745        $src = $path unless $rc;
746      }
747    }
748
749    if ($rc) {
750      # Error in "svn cat"
751      croak 'Unable to locate config file from "', $self->src, '", abort';
752
753    } else {
754      # Print diagnostic, if necessary
755      if ($verbose and $self->type and $self->type =~ /$expand_type/) {
756        print '# Config file (', $self->type, '): ', $src;
757        print '@', $rev if $rev;
758        print "\n";
759      }
760    }
761
762    # Record the actual source location
763    $self->{PEGREV    } = $rev;
764    $self->{ACTUAL_SRC} = $src;
765
766  } else {
767    # Config file resides in the normal file system
768    # --------------------------------------------------------------------------
769    my $src = $self->src;
770
771    if (-d $src) { # Source is a directory
772      croak 'Config file "', $src, '" is a directory, abort' if not $self->type;
773
774      # Get name of the config file by looking at the type
775      my %cfgname = %{ $self->config->setting ('CFG_NAME') };
776      my $key     = uc $self->type;
777      my $file    = exists $cfgname{$key} ? $cfgname{$key} : '';
778
779      if ($file) {
780        my $cfgdir = $self->config->setting (qw/DIR CFG/);
781
782        # Check whether a config file with a default name exists in the
783        # specified path, then check whether a config file with a default name
784        # exists under the "cfg" sub-directory of the specified path
785        if (-f catfile $self->src, $file) {
786          $src = catfile $self->src, $file;
787
788        } elsif (-f catfile ($self->src, $cfgdir, $file)) {
789          $src = catfile $self->src, $cfgdir, $file;
790
791        } else {
792          croak 'Unable to locate config file from "', $self->src, '", abort';
793        }
794
795      } else {
796        croak 'Unknown config file type "', $self->type, '", abort';
797      }
798    }
799
800    if (-r $src) {
801      open FILE, '<', $src;
802      print '# Config file (', $self->type, '): ', $src, "\n"
803        if $verbose and $self->type and $self->type =~ /$expand_type/;
804
805      @lines = readline 'FILE';
806      close FILE;
807
808    } else {
809      croak 'Unable to read config file "', $src, '", abort';
810    }
811
812    # Record the actual source location
813    $self->{ACTUAL_SRC} = $src;
814  }
815
816  return @lines;
817}
818
819# ------------------------------------------------------------------------------
820# SYNOPSIS
821#   $string = $self->_expand_variable ($string[, $env]);
822#
823# DESCRIPTION
824#   This internal method expands variables in $string. If $env is specified
825#   and is true, it expands environment variables. Otherwise, it expands
826#   local variables.
827# ------------------------------------------------------------------------------
828
829sub _expand_variable {
830  my ($self, $string, $env) = @_;
831
832  # Pattern for environment/local variable
833  my $pattern = $env ? '\$\{?([A-Z][A-Z0-9_]+)\}?' : '%\{?([\w:]+)\}?';
834
835  while ($string and $string =~ /$pattern/) {
836    my $var_label = $1; # variable label
837
838    # Get variable value from environment or local configuration
839    my $variable = $env
840                   ? (exists $ENV{$var_label} ? $ENV{$var_label} : undef)
841                   : $self->config->variable ($var_label);
842
843    $variable = dirname ($self->actual_src)
844      if $env and $var_label eq 'HERE' and not defined $variable;
845
846    # Substitute match with value of variable
847    if (defined $variable) {
848      $string =~ s/$pattern/$variable/;
849
850    } else {
851      w_report 'Warning: ', $self->actual_src, ': variable "',
852               ($env ? '$' : '%'), $var_label, '" not expanded.';
853      last;
854    }
855  }
856
857  return $string;
858}
859
8601;
861
862__END__
Note: See TracBrowser for help on using the repository browser.