From 594f40b413c0c0d1e2688be6386d24113367a17b Mon Sep 17 00:00:00 2001 From: Michael Pyne Date: Thu, 7 Apr 2011 23:15:32 -0400 Subject: [PATCH] Add "include" option for config file reading. Based on a recommendation from Michael Hansen, this commit adds the ability to include other files from the rc file used for kdesrc-build. Tilde-expansion is supported on the filename, but only of the ~/foo variety (not ~user/foo). It works by essentially intercepting the input stream as necessary, so unfortunately things like a common "global" followed by different "global" sections doesn't work (although let me know if that's needed, it wouldn't be too hard). On the other hand each rc-file would be able to include the global options that are common. To use, simply use "include /path/to/file-to-include" as a single line in your config file. Leading spaces are supported, but the remainder of the file after the "include" and all whitespace immediately following "include" is assumed to be the filename, so don't leave trailing spaces. BUG:223331 FIXED-IN:1.14 --- kdesrc-build | 110 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 99 insertions(+), 11 deletions(-) diff --git a/kdesrc-build b/kdesrc-build index ff38f1c..ac73e0d 100755 --- a/kdesrc-build +++ b/kdesrc-build @@ -867,6 +867,93 @@ my @screen_log; } # }}} +# package RecursiveFH {{{ +{ + package RecursiveFH; + + # Alias the global make_exception into this package. + *make_exception = *main::make_exception; + + sub new + { + my ($class) = @_; + my $data = { + 'filehandles' => [], # Stack of filehandles to read + 'current' => undef, # Current filehandle to read + }; + + return bless $data, $class; + } + + sub addFilehandle + { + my ($self, $fh) = @_; + push @{$self->{filehandles}}, $fh; + $self->{current} = $fh; + } + + # Reads the next line of input and returns it. + # If a line of the form "include foo" is read, this function automatically + # opens the given file and starts reading from it instead. The original + # file is not read again until the entire included file has been read. This + # works recursively as necessary. + # + # No further modification is performed to returned lines. + # + # undef is returned on end-of-file (but only of the initial filehandle, not + # included files from there) + sub readLine + { + my $self = shift; + my $fh = $self->{current}; + + # Sanity check since different methods might try to read same file reader + return undef unless defined $fh; + + my $line = readline($fh); + + if (not defined $line) { + my $oldFh = pop @{$self->{filehandles}}; + close $oldFh; + + # If last file, return undef + $self->{current} = undef; + return undef if scalar @{$self->{filehandles}} == 0; + + # Else, use last filehandle (top of the stack) + $self->{current} = ${$self->{filehandles}}[-1]; + return $self->readLine(); + } + elsif ($line =~ /^\s*include\s+\S/) { + # Include found, extract file name and open file. + chomp $line; + my ($filename) = ($line =~ /^\s*include\s+(.+)$/); + + if (not defined $filename || !$filename) { + die make_exception('Config', + "Unable to handle file include on line $., '$line'"); + } + + my $newFh; + $filename =~ s/^~\//$ENV{HOME}\//; # Tilde-expand + + open ($newFh, '<', $filename) or + die make_exception('Config', + "Unable to open file $filename which was included from line $."); + + push @{$self->{filehandles}}, $newFh; + $self->{current} = $newFh; + return $self->readLine(); + } + else { + return $line; + } + } + + 1; +} +# }}} + # Debugging routines {{{ # Colors my ($RED, $GREEN, $YELLOW, $NORMAL, $BOLD) = ("") x 5; @@ -3353,7 +3440,7 @@ sub split_option_value # 'module' if we should expect an end module statement. sub parse_module { - my ($fh, $module) = @_; + my ($fileReader, $module) = @_; $module = 'global' unless $module; # Setup default options in case user specifies only module name to get it @@ -3364,7 +3451,7 @@ sub parse_module } # Read in each option - while (read_line($fh)) + while (read_line($fileReader->readLine())) { if($module eq 'global') { @@ -3477,14 +3564,14 @@ sub ensure_projects_xml_present sub parse_moduleset { my $ctx = shift; - my $fh = shift; + my $fileReader = shift; my $moduleSetName = shift || ''; my $repoSet = get_option('global', 'git-repository-base'); my @modules; my %optionSet; # We read all options, and apply them to all modules my $startLine = $.; # For later error messages - while(read_line($fh)) { + while(read_line($fileReader->readLine())) { last if /^end\s+module(-?set)?$/; my ($option, $value) = split_option_value($_); @@ -3741,8 +3828,11 @@ sub read_options my @module_list; my ($option, $modulename, %readModules); + my $fileReader = RecursiveFH->new(); + $fileReader->addFilehandle($fh); + # Read in global settings - while (<$fh>) + while ($fileReader->readLine()) { s/#.*$//; # Remove comments s/^\s*//; # Remove leading whitespace @@ -3758,7 +3848,7 @@ sub read_options } # Now read in each global option - parse_module($fh, 'global'); + parse_module($fileReader, 'global'); last; } @@ -3775,7 +3865,7 @@ sub read_options } # Now read in module settings - while (<$fh>) + while ($fileReader->readLine()) { s/#.*$//; # Remove comments s/^\s*//; # Remove leading whitespace @@ -3797,10 +3887,10 @@ sub read_options } # A moduleset can give us more than one module to add. - push @module_list, parse_moduleset($ctx, $fh, $modulename); + push @module_list, parse_moduleset($ctx, $fileReader, $modulename); } else { - parse_module($fh, $modulename); + parse_module($fileReader, $modulename); push @module_list, Module->new($ctx, $modulename); } @@ -3808,8 +3898,6 @@ sub read_options $using_default = 0; } - close $fh; - # All modules and their options have been read, filter out modules not # to update or build, based on the --ignore-modules option already present # on the command line. manual-update and manual-build are also relevant,