summaryrefslogtreecommitdiffstats
path: root/src/lib/Gitolite/Conf/Explode.pm
blob: cf8962035482563807ad14742c1001b203c940a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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;