package VCP::ConfigFileUtils; =head1 NAME VCP::ConfigFileUtils - utilities used to parse or create vcp config files =head1 SYNOPSIS use VCP::ConfigFileUtils qw( parse_config_file write_config_file ); =head1 DESCRIPTION =cut @EXPORT_OK = qw( parse_config_file write_config_file ); @ISA = qw( Exporter ); use Exporter; use VCP::Debug qw( :debug ); use VCP::Utils qw( rel2abs ); =head1 FUNCTIONS =over =item parse_config_file parse_config_file( $fn ); parse_config_file( $fn, $may_not_be_a_config_file ); Reads a configuration file and returns a list of (section name, \@section_tokens). =cut sub parse_config_file { ## NOTE: This should *not* be used to sniff files from STDIN because ## they can be huge and we don't have a mechanism that allows us to ## read a chunk, make a decision, then relace the chunk for the XML ## parser if it looks like revml. Thus, if it comes on STDIN, the ## config file must be specced with a "vcp:-" CLI param. my ( $fn, $may_not_be_a_config_file ) = @_; my $source_desc = $fn eq "-" ? "stdin" : $fn; $may_not_be_a_config_file = 0 if $fn =~ /\.vcp\z/i; warn "vcp: reading config from $source_desc\n" unless $may_not_be_a_config_file; if ( $fn eq "-" ) { ## Note: this can only occur if vcp:- was specified, not ## if "-" was specified (see the $arg ne "-" above). *VCPSPECFILE = \*STDIN; } else { open VCPSPECFILE, "<$fn" or die "$!: $fn\n"; } my $vcp = ""; my $c; do { $c = read VCPSPECFILE, $vcp, 1_000_000, length $vcp; die "$! while reading $fn\n" unless defined $c; } while ( $c ); close VCPSPECFILE; die "IS REVML FILE\n" if $may_not_be_a_config_file && $vcp =~ m{]*>.*}m; require VCP::Utils::p4; my @vcp_spec = VCP::Utils::p4->parse_p4_form( $vcp ); undef $vcp; require Text::ParseWords; my @out; ## The Options and Dest/Destination tags are special: Options must come ## first and Dest must come last. my $options_value; my $dest_value; while ( @vcp_spec ) { my ( $tag, $value ) = ( lc shift @vcp_spec, shift @vcp_spec ); for ( $value ) { s/\A\s+//; s/\s+\z//; } ## use quotewords and tell it to keep the backslashes ## because backslashes are important on Win32. $value = [ map { s{^(['"]?)(.*)\1}{$2}; $_; } Text::ParseWords::quotewords( '\s+', 1, $value ) ]; if ( $tag eq "options" ) { die "vcp: two Options entries found in config file\n" if $options_value; $options_value = $value; } elsif ( $tag eq "dest" || $tag eq "destination" ) { die "vcp: two Destination entries found in config file\n" if $dest_value; $dest_value = $value; } elsif ( $tag eq "source" && @out ) { die "vcp: Source must come before filter sections in config file\n"; } else { push @out, $tag, $value; } } unshift @out, "options", $options_value if $options_value; push @out, "dest", $dest_value if $dest_value; if ( debugging ) { require Data::Dumper; debug( Data::Dumper->Dump( [ \@out ], [ $source_desc ] ) ); } return \@out; } =item write_config_file write_config_file( $filename, @plugins ); =cut sub write_config_file { my ( $fn, @plugins ) = @_; open CONFIG_FILE, ">$fn" or die "$!: $fn\n"; for ( @plugins ) { print CONFIG_FILE $_->config_file_section_as_string or die "$!: $fn\n"; } close CONFIG_FILE or die "$!: $fn\n"; } =back =head1 COPYRIGHT Copyright 2000, Perforce Software, Inc. All Rights Reserved. This module and the VCP package are licensed according to the terms given in the file LICENSE accompanying this distribution, a copy of which is included in L. =cut 1 ;