package Gitolite::Conf::Explode; # include/subconf processor # ---------------------------------------------------------------------- @EXPORT = qw( explode ); use Exporter 'import'; use Gitolite::Rc; use Gitolite::Common; use strict; use warnings; # ---------------------------------------------------------------------- # 'seen' for include/subconf files my %included = (); # 'seen' for group names on LHS my %prefixed_groupname = (); sub explode { trace( 3, @_ ); my ( $file, $subconf, $out ) = @_; # seed the 'seen' list if it's empty $included{ device_inode("gitolite.conf") }++ unless %included; my $fh = _open( "<", $file ); while (<$fh>) { my $line = cleanup_conf_line($_); next unless $line =~ /\S/; # subst %HOSTNAME word if rc defines a hostname, else leave as is $line =~ s/%HOSTNAME\b/$rc{HOSTNAME}/g if $rc{HOSTNAME}; $line = prefix_groupnames( $line, $subconf ) if $subconf ne 'master'; if ( $line =~ /^(include|subconf) (?:(\S+) )?(\S.+)$/ ) { incsub( $1, $2, $3, $subconf, $out ); } else { # normal line, send it to the callback function push @{$out}, "# $file $."; push @{$out}, $line; } } } sub incsub { my $is_subconf = ( +shift eq 'subconf' ); my ( $new_subconf, $include_glob, $current_subconf, $out ) = @_; _die "subconf '$current_subconf' attempting to run 'subconf'\n" if $is_subconf and $current_subconf ne 'master'; _die "invalid include/subconf file/glob '$include_glob'" unless $include_glob =~ /^"(.+)"$/ or $include_glob =~ /^'(.+)'$/; $include_glob = $1; trace( 3, $is_subconf, $include_glob ); for my $file ( glob($include_glob) ) { _warn("included file not found: '$file'"), next unless -f $file; _die "invalid include/subconf filename '$file'" unless $file =~ m(([^/]+).conf$); my $basename = $1; next if already_included($file); if ($is_subconf) { push @{$out}, "subconf " . ( $new_subconf || $basename ); explode( $file, ( $new_subconf || $basename ), $out ); push @{$out}, "subconf $current_subconf"; } else { explode( $file, $current_subconf, $out ); } } } sub prefix_groupnames { my ( $line, $subconf ) = @_; my $lhs = ''; # save 'foo' if it's an '@foo = list' line $lhs = $1 if $line =~ /^@(\S+) = /; # prefix all @groups in the line $line =~ s/(^| )(@\S+)(?= |$)/ $1 . ($prefixed_groupname{$subconf}{$2} || $2) /ge; # now prefix the LHS and store it if needed if ($lhs) { $line =~ s/^@\S+ = /"\@$subconf.$lhs = "/e; $prefixed_groupname{$subconf}{"\@$lhs"} = "\@$subconf.$lhs"; trace( 3, "prefixed_groupname.$subconf.\@$lhs = \@$subconf.$lhs" ); } return $line; } sub already_included { my $file = shift; my $file_id = device_inode($file); return 0 unless $included{$file_id}++; _warn("$file already included"); trace( 3, "$file already included" ); return 1; } sub device_inode { my $file = shift; trace( 3, $file, ( stat $file )[ 0, 1 ] ); return join( "/", ( stat $file )[ 0, 1 ] ); } 1;