#!/usr/bin/env perl #-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # Common stuff for the ximian-setup-tools backends. # # Copyright (C) 2000-2001 Ximian, Inc. # # Authors: Hans Petter Jansson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published # by the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. $SCRIPTSDIR = "___scriptsdir___"; if ($SCRIPTSDIR =~ /^___scriptsdir__[_]/) { $SCRIPTSDIR = "."; $DOTIN = ".in"; } require "$SCRIPTSDIR/report.pl$DOTIN"; require "$SCRIPTSDIR/platform.pl$DOTIN"; require "$SCRIPTSDIR/gettext.pl$DOTIN"; require "$SCRIPTSDIR/xml.pl$DOTIN"; # --- Operation modifying variables --- # # Variables are set to their default value, which may be overridden by user. Note # that a $prefix of "" will cause the configurator to use '/' as the base path, # and disables creation of directories and writing of previously non-existent # files. # We should get rid of all these globals. $xst_name = ""; # Short name of tool. # $xst_version = ""; # Version of tool - [major.minor.revision]. Deprecated: now in hash # structure generated by &xst_init. # $xst_operation = ""; # Major operation user wants to perform - [get | set | filter]. Same as xst_version. $xst_prefix = ""; $xst_do_verbose = 0; $xst_do_report = 0; $xst_debug = 0; $xst_do_immediate = 1; # Location management stuff $xst_location = ""; $xst_no_archive = 0; sub xst_print_usage_synopsis { my ($tool) = @_; my ($ops_syn, $i); my @ops = qw (get set filter); foreach $i (@ops) { $ops_syn .= "--$i | " if exists $ {$$tool{"directives"}}{$i}; } print STDERR "Usage: $$tool{name}-conf <${ops_syn}--interface | --directive | --help | --version>\n"; print STDERR " " x length $$tool{"name"}; print STDERR " [--disable-immediate] [--prefix ]\n"; print STDERR " " x length $$tool{"name"}; print STDERR " [--progress] [--report] [--verbose]\n\n"; } sub xst_print_usage_generic { my ($tool) = @_; my (%usage, $i); my @ops = qw (get set filter); my $usage_generic_head =<< "end_of_usage_generic;"; Major operations (specify one of these): end_of_usage_generic; my $usage_generic_tail =<< "end_of_usage_generic;"; -i --interface Shows the available backend directives for interactive mode, in XML format. Interactive mode is set when no -g, -s or -f arguments are given. -d --directive Takes a \'name::arg1::arg2...::argN\' directive value as comming from standard input in interactive mode. -h --help Prints this page to standard error. --version Prints version information to standard output. Modifiers (specify any combination of these): --platform Overrides the detection of your platform\'s name and version, e.g. redhat-6.2. Use with care. See the documentation for a full list of supported platforms. --disable-immediate With --set, prevents the configurator from running any commands that make immediate changes to the system configuration. Use with --prefix to make a dry run that won\'t affect your configuration. With --get, suppresses running of non-vital external programs that might take a long time to finish. -p --prefix Specifies a directory prefix where the configuration is looked for or stored. When storing (with --set), directories and files may be created. --progress Prints machine-readable progress information to standard output, before any XML, consisting of three-digit percentages always starting with \'0\'. --report Prints machine-readable diagnostic messages to standard output, before any XML. Each message has a unique three-digit ID. The report ends in a blank line. -v --verbose Prints human-readable diagnostic messages to standard error. end_of_usage_generic; $usage{"get"} =<< "end_of_usage_generic;"; -g --get Prints the current configuration to standard output, as a standalone XML document. The configuration is read from the host\'s system config files. end_of_usage_generic; $usage{"set"} =<< "end_of_usage_generic;"; -s --set Updates the current configuration from a standalone XML document read from standard input. The format is the same as for the document generated with --get. end_of_usage_generic; $usage{"filter"} =<< "end_of_usage_generic;"; -f --filter Reads XML configuration from standard input, parses it, and writes the configurator\'s impression of it back to standard output. Good for debugging and parsing tests. end_of_usage_generic; print STDERR $usage_generic_head; foreach $i (@ops) { print STDERR $usage{$i} if exists $ {$$tool{"directives"}}{$i}; } print STDERR $usage_generic_tail; } # if $exit_code is provided (ne undef), exit with that code at the end. sub xst_print_usage { my ($tool, $exit_code) = @_; &xst_print_usage_synopsis ($tool); print STDERR $$tool{"description"} . "\n"; &xst_print_usage_generic ($tool); exit $exit_code if $exit_code ne undef; } sub xst_print_version { my ($tool, $exit_code) = @_; print "$$tool{name} $$tool{version}\n"; exit $exit_code if $exit_code ne undef; } # --- Initialization and finalization --- # sub xst_set_operation { my ($tool, $operation) = @_; if ($tool{"operation"} ne "") { print STDERR "Error: You may specify only one major operation.\n\n"; &xst_print_usage ($tool, 1); exit (1); } $$tool{"operation"} = $operation; } sub xst_set_with_param { my ($tool, $arg_name, $value) = @_; if ($$tool{$arg_name} ne "") { print STDERR "Error: You may specify --$arg_name only once.\n\n"; &xst_print_usage ($tool, 1); } if ($value eq "") { print STDERR "Error: You must specify an argument to the --$arg_name option.\n\n"; &xst_print_usage ($tool, 1); } $$tool{$arg_name} = $value; } sub xst_set_op_directive { my ($tool, $directive) = @_; &xst_set_with_param ($tool, "directive", $directive); &xst_set_operation ($tool, "directive"); } sub xst_set_prefix { my ($tool, $prefix) = @_; &xst_set_with_param ($tool, "prefix", $prefix); $xst_prefix = $prefix; } sub xst_set_dist { my ($tool, $dist) = @_; &xst_set_with_param ($tool, "platform", $dist); $xst_dist = $dist; } sub xst_set_location { my ($tool, $location) = @_; &xst_set_with_param ($tool, "location", $location); $xst_location = $location; } sub xst_merge_std_directives { my ($tool) = @_; my ($directives, $i); my %std_directives = ( # platforms directive to do later. # "platforms" => [ \&xst_print_platforms, [], # "Print XML showing platforms supported by backend." ], "platform_set" => [ \&xst_platform_set, ["platform"], "Force the selected platform. platform arg must be one of the listed in the" . "reports." ], "interface" => [ \&xst_interface_directive, [], "Print XML showing backend capabilities." ], "end" => [ \&xst_end_directive, [], "Finish gracefuly and exit with success." ] ); $directives = $$tool{"directives"}; # Standard directives may be overriden. foreach $i (keys %std_directives) { $$directives{$i} = $std_directives{$i} if !exists $$directives{$i}; } } sub xst_is_tool { my ($tool) = @_; if ((ref $tool eq "HASH") && (exists $$tool{"is_tool"}) && ($$tool{"is_tool"} == 1)) { return 1; } return 0; } sub xst_init { my ($name, $version, $description, $directives, @args) = @_; my (%tool, $arg); # Set the output autoflush. $| = 1; $tool{"is_tool"} = 1; # Set backend descriptors. $tool{"name"} = $xst_name = $name; $tool{"version"} = $version; $tool{"description"} = $description; $tool{"directives"} = $directives; &textdomain ("ximian-setup-tools"); &xst_merge_std_directives (\%tool); # Parse arguments. while ($arg = shift (@args)) { if ($arg eq "--get" || $arg eq "-g") { &xst_set_operation (\%tool, "get"); } elsif ($arg eq "--set" || $arg eq "-s") { &xst_set_operation (\%tool, "set"); } elsif ($arg eq "--filter" || $arg eq "-f") { &xst_set_operation (\%tool, "filter"); } elsif ($arg eq "--directive" || $arg eq "-d") { &xst_set_op_directive (\%tool, shift @args); } elsif ($arg eq "--interface" || $arg eq "-i") { &xst_interface_print (\%tool, 0); } elsif ($arg eq "--help" || $arg eq "-h") { &xst_print_usage (\%tool, 0); } elsif ($arg eq "--version") { &xst_print_version (\%tool, 0); } elsif ($arg eq "--prefix" || $arg eq "-p") { &xst_set_prefix (\%tool, shift @args); } elsif ($arg eq "--platform") { &xst_set_dist (\%tool, shift @args); } elsif ($arg eq "--progress") { $tool{"progress"} = $xst_progress = 1; } elsif ($arg eq "--location") { &xst_set_location (\%tool, shift @args); } elsif ($arg eq "--no-archive") { $tool{"no_archive"} = $xst_no_archive = 1; } elsif ($arg eq "--debug") { $tool{"debug"} = $xst_debug = 1; } elsif ($arg eq "--verbose" || $arg eq "-v") { $tool{"do_verbose"} = $xst_do_verbose = 1; &xst_report_set_threshold (99); } elsif ($arg eq "--report") { $tool{"do_report"} = $xst_do_report = 1; &xst_report_set_threshold (99); } else { print STDERR "Error: Unrecognized option '$arg'.\n\n"; &xst_print_usage (\%tool, 1); } } # See if debug requested in env. $tool{"debug"} = $xst_debug = 1 if ($ENV{"SET_ME_UP_HARDER"}); # Set up subsystems. &xst_platform_guess (\%tool) if !$tool{"platform"}; &xst_report_begin (); return \%tool; } sub xst_terminate { &xst_report_set_threshold (-1); &xst_debug_close_all (); exit (0); } sub xst_end_directive { my ($tool) = @_; &xst_report_end (); &xst_xml_print_request_end (); &xst_terminate (); } sub xst_interface_print_comment { my ($name, $directive) = @_; my %std_comments = ("get" => "Prints the current configuration to standard output, as " . "a standalone XML document. The configuration is read from " . "the host\'s system config files.", "set" => "Updates the current configuration from a standalone XML " . "document read from standard input. The format is the same " . "as for the document generated with --get.", "filter" => "Reads XML configuration from standard input, parses it, " . "and writes the configurator\'s impression of it back to " . "standard output. Good for debugging and parsing tests." ); $comment = $$directive[2]; $comment = $std_comments{$name} if (exists $std_comments{$name}); if ($comment) { &xst_xml_print_line (""); &xst_xml_print_line ($comment); &xst_xml_print_line (""); } } # if $exit_code is provided (ne undef), exit with that code at the end. sub xst_interface_print { my ($tool, $exit_code) = @_; my ($directives, $key); $directives = $$tool{"directives"}; &xst_xml_print_begin ("interface"); foreach $key (sort keys %$directives) { my $comment = $ {$$directives{$key}}[2]; my @args = @{ $ {$$directives{$key}}[1]}; my $arg; &xst_xml_container_enter ("directive"); &xst_xml_print_line ("$key"); &xst_interface_print_comment ($key, $$directives{$key}); while ($arg = shift (@args)) { if ($arg =~ /\*$/) { my $tmp = $arg; &xst_report ("directive_invalid", $key) if ($#args != -1); chop $tmp; &xst_xml_print_line ("$tmp"); } else { &xst_xml_print_line ("$arg"); } } &xst_xml_container_leave (); } &xst_xml_print_end ("interface"); exit $exit_code if $exit_code ne undef; } sub xst_interface_directive { my ($tool) = @_; &xst_report_end (); &xst_interface_print ($tool); } sub xst_directive_fail { my (@report_args) = @_; &xst_report (@report_args); &xst_report_end (); &xst_xml_print_request_end (); &xst_debug_close_all (); } # Normal use for the direcives hash in the backends is: # # "name" => [ \&sub, [ "arg1", "arg2", "arg3",... "argN" ], "comment" ] # # name name of the directive that will be used in interactive mode. # sub is the function that runs the directive. # arg1...argN show the number of arguments that the function may use. The # name of the argument is used for documentation purposes for # the interfaces XML (dumped by the "interfaces" directive). # An argument ending with * means that 0 or more arguments # may be given. # comment documents the directive in a brief way, for the interface XML. # # Example: # # "install_font" => [ \&xst_font_install, [ "directory", "file", "morefiles*" ], "Installs fonts." ] # # This means that when an interactive mode directive is given, such as: # # install_font::/usr/share/fonts::/tmp/myfile::/tmp/myfile2 # # the function xst_font_install will be called, with the tool structure, /usr/share/fonts, # /tmp/myfile and /tmp/myfile2 as arguments. Directives with 1 or 0 arguments # would be rejected, as we are requiring 2, and optionaly allowing more. # Check enable_iface in network-conf.in for an example of a directive handler. # # The generated interface XML piece for this entry would be: # # # xst_font_install # # Installs fonts. # # directory # file # morefiles # sub xst_directive_run { my ($tool, $line) = @_; my ($key, @args, $directives, $proc, $reqargs); ($key, @args) = split ("::", $line); $directives = $$tool{"directives"}; &xst_report_begin (); if (!exists $$directives{$key}) { &xst_directive_fail ("directive_unsup", $key); return; } $reqargs = $ {$$directives{$key}}[1]; if ($$reqargs[$#$reqargs] =~ /\*$/) { if ($#args < ($#$reqargs - 1)) { &xst_directive_fail ("directive_lowargs", $key, $#$reqargs, join (',', $key, @args)); return; } } if ($#args != $#$reqargs) { &xst_directive_fail ("directive_badargs", $key, $#$reqargs + 1, join (',', $key, @args)); return; } &xst_report ("directive_run", $key, join (',', @args)); $proc = $ {$$directives{$key}}[0]; &$proc ($tool, @args); &xst_xml_print_request_end (); &xst_debug_close_all (); } sub xst_run { my ($tool) = @_; my ($line); if ($$tool{"operation"} ne "directive") { my @stdops = qw (get set filter); my ($op); foreach $op (@stdops) { if ($$tool{"operation"} eq $op) { $$tool{"operation"} = "directive"; $$tool{"directive"} = $op; } } } &xst_report_end (); if ($$tool{"directive"}) { &xst_directive_run ($tool, $$tool{"directive"}); &xst_terminate (); } while ($line = ) { chomp $line; &xst_directive_run ($tool, $line); } } 1;