summaryrefslogtreecommitdiff
path: root/fml/libexec/Standalone.pm
blob: de714b0e60361594c774efe887c6a4c385397ae5 (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#-*- perl -*-
#
#  Copyright (C) 2001 Ken'ichi Fukamachi
#   All rights reserved. This program is free software; you can
#   redistribute it and/or modify it under the same terms as Perl itself. 
#
# $FML: Standalone.pm,v 1.6 2001/05/29 16:21:17 fukachan Exp $
#


package Standalone;

use strict;
use Carp;

=head1 NAME

Standalone - minimal parser for libexec/* programs

=head1 SYNOPSIS

   use Standalone;
   my $main_cf = Standalone::load_cf($main_cf_file, $params);

where $param is optional. The format of it is

  $params = "key1=value1 key2=value2";

=head1 DESCRIPTION

C<load_cf()> reads the C<key = value> style configuration file and
return the hash. The file format is like this:

   key1 = value1

   key2 = value2
          value3

key2 is equivalent to 

   key2 = value2 value3

A set of space separeted elements constructs an array of values.


=head1 METHODS

=head2 C<load_cf()>

load "key = value" style configuration file and build a hash.
return HASH REFERENCE.

=cut


# Descriptions: load "key = value" style configuration.
#               It is available to use the following style.
#                    key = value1 value2
#                          value3
#               XXX This file is non-Object Oriented style but 
#               XXX this is minimum module used in standalone program.
#    Arguments: $file $params
#               $params is 'key1=value1 key2=value2' syntax.
# Side Effects: $config (hash reference) is allocated on memory here.
# Return Value: hash reference to configuration parameters
sub load_cf
{
    my ($file, $params) = @_;
    my $config = $params ? _parse_params($params) : {};

    use FileHandle;
    my $fh = new FileHandle $file;

    if (defined $fh) {
	my $curkey;
	while (<$fh>) {
	    next if /^\#/;
	    chop;

	    if (/^([A-Za-z]\w+)\s+=\s*(.*)/) {
		my ($key, $value) = ($1, $2);
		$curkey           = $key;
		$config->{$key}   = $value;
	    }
	    if (/^\s+(.*)/) {
		$config->{ $curkey }  .= " ". $1;
	    }
	}
	$fh->close;
    }
    else {
	croak("Error: cannot open $file");
    }

    _expand_variables( $config );
    return $config;
}


# Descriptions: expand $var to the value of $var.
#    Arguments: $ref_to_config
# Side Effects: rewrite the given $config 
# Return Value: none
sub _expand_variables
{
    my ($config) = @_;
    my @order  = keys %$config;

    # check whether the variable definition is recursive.
    # For example, definition "var_a = $var_a/b/c" causes a loop.
    for my $x ( @order ) {
	if ((defined $x) && defined ($config->{ $x })) {
		if ($config->{ $x } =~ /\$$x/) {
		    croak("loop1: definition of $x is recursive\n");
		}
	}
    }

    # main expansion loop
    my $org = '';
    my $max = 0;
  KEY:
    for my $x ( @order ) {
	next KEY unless defined($config->{ $x });
	next KEY if $config->{ $x } !~ /\$/o;

	# we need a loop to expand nested variables, for example, 
	# a = $x/y and b = $a/c/0
	# 
	$max = 0;
      EXPANSION_LOOP:
	while ($max++ < 16) {
	    $org = $config->{ $x };

	    $config->{ $x } =~ s/\$([a-z_]+)/$config->{$1}/g;

	    last EXPANSION_LOOP if $config->{ $x } !~ /\$/o;
	    last EXPANSION_LOOP if $org eq $config->{ $x };

	    if ($config->{ $x } =~ /\$$x/) {
		croak("loop2: definition of $x is recursive\n");
	    }
        }

	if ($max >= 16) {
	    croak("variable expansion of $x causes infinite loop\n");
	} 
    }
}


sub _parse_params
{
    my ($params) = @_;
    my %config = ();

    for my $x (split(/\s+/, $params)) {
	my ($key, $value) = split(/=/, $x);
	$config{ $key } = $value;
    }

    \%config;
}


=head1 AUTHOR

Ken'ichi Fukamachi

=head1 COPYRIGHT

Copyright (C) 2001 Ken'ichi Fukamachi

All rights reserved. This program is free software; you can
redistribute it and/or modify it under the same terms as Perl itself. 

=head1 HISTORY

Standalone appeared in fml5 mailing list driver package.
See C<http://www.fml.org/> for more details.

=cut


1;