+sub loarg() { usageerr("missing option value") if !@ARGV; return shift @ARGV; }
+sub soarg() { my ($rv); $rv=$_; $_=''; return length $rv ? $rv : loarg(); }
+
+usageerr("-q may be specified at most twice") if $verbosity<0;
+usageerr("-v may be specified at most once") if $verbosity>3;
+usageerr("-D may be specified at most twice") if $debug>2;
+usageerr("must specify either -f|-y|-n or zones (and not both)")
+ if !!$mode == !!@ARGV;
+
+sub usageerr ($) {
+ die <<END;
+$_[0]
+
+usage: chiark-named-conf [options] -f|-y|-n|<zone>...
+operation modes:
+ -f --force install without checking
+ -y --yes check and install
+ -n --no check only (configured zones)
+ <zone> ... check only (specified zones, even unconfigured ones)
+additional options:
+ -A --all report on zones marked ? (ones we know are broken)
+ -D debug $quis (does not help debug your DNS config)
+ -g --glueless do not warn about any glueless referrals (not recommended)
+ -l --localonly full checks only on zones which we primary
+ -q --quiet no output for OK zones
+ -r --repeat repeat warnings for all sources of imperfect data
+ -v --verbose extra verbose info about each zone
+ -C|--config <DIR/FILE use FILE as default config and DIR as default dir
+
+chiark-named-conf is Copyright 2002 Ian Jackson. It is Free software, under
+the GNU General Public License, and you are welcome to change it and/or
+distribute copies under certain conditions. There is ABSOLUTELY NO WARRANTY.
+END
+}
+
+cfg_fail("config filename $etcfile should not be directory")
+ if $etcfile =~ m,/$,;
+
+use vars qw($default_dir);
+$default_dir= $etcfile =~ m,^.*/, ? $& : './';
+
+use vars qw($slave_dir $slave_prefix $slave_suffix);
+$slave_dir= 'slave';
+$slave_prefix= '';
+$slave_suffix= '';
+
+use vars qw(@self_ns @self_soa @self_addr @forbid_addr @conv_glueless);
+@self_ns= @self_soa= @self_addr= @forbid_addr= ();
+@conv_glueless= qw(in-addr.arpa ip6.arpa ip6.int);
+
+use vars qw(%zone_cfg @zone_cfg_list);
+%zone_cfg= ();
+@zone_cfg_list= ();
+
+use vars qw($output $default_output %output_contents);
+$output= '';
+$default_output= '';
+%output_contents= ();
+
+use vars qw($check $install);
+$check= $mode !~ m/^f/;
+$install= $mode =~ m/^[yf]/;
+
+read_config($etcfile);
+debug_dump('@zone_cfg_list %zone_cfg');
+process_zones($mode ? @zone_cfg_list : @ARGV);
+debug_dump('%output_contents');
+output_files() if $install;
+
+exit 0;
+
+#-------------------- configuration reading
+
+sub cfg_fail ($) { die "$quis: $where:\n $_[0]\n"; }
+
+sub read_config ($) {
+ my ($if) = @_;
+ my ($fh,$z,@self,$before,
+ $mod,$dir,$prefix,$suffix,$subfile,$lprefix,$lsuffix,$zf);
+ local ($_);
+
+ $fh= new IO::File $if,'r' or cfg_fail("open $if:\n $!");
+ $before= '';
+ for (;;) {
+ if (!defined($_= <$fh>)) {
+ cfg_fail("read config file $if:\n $!") if $fh->error();
+ last;
+ }
+ chomp; s/\s+$//;
+ if (s/\\$//) { $before.= $_; next; }
+ $_= $before.$_;
+ $before= '';
+ s/^\s+//;
+ $where= "$if:$.";
+ next if m/^\#/;
+ last if m/^end$/;
+ next unless m/\S/;
+ if (m/^self(\-ns|\-soa|)\s+(\S.*\S)/) {
+ @self= split /\s+/, $2;
+ @self_ns= @self if $1 ne '-soa';
+ @self_soa= @self if $1 ne '-ns';
+ } elsif (m/^serverless\-glueless\s+(\S.*\S)/) {
+ @conv_glueless= split /\s+/, $1;
+ } elsif (m/^self\-addr\s+([0-9. \t]+)/) {
+ @self_addr= split /\s+/, $1;
+ } elsif (m/^forbid\-addr(?:\s+([0-9. \t]+))?/) {
+ @forbid_addr= defined $1 ? split /\s+/, $1 : ();
+ } elsif (m,^
+ primary\-dir ([*?]?)
+ \s+ (\S+)/([^/ \t]*)
+ (?: \s+ ([^/ \t]*) (?: (/.+) )?
+ )?
+ $,x) {
+ ($mod, $dir, $prefix, $suffix, $subfile) =
+ ($1,qualify($2),$3,$4,$5);
+ $suffix= '' if !defined $suffix;
+ $subfile= '' if !defined $subfile;
+ $suffix= '_db' if !length $suffix && !length $subfile;
+ if (-d "$dir/$prefix") { $dir.='/'; $dir.=$prefix; $prefix=''; }
+ opendir D, $dir or cfg_fail("open primary-dir $dir:\n $!");
+ $lprefix= length $prefix; $lsuffix= length $suffix;
+ while (defined($_= readdir D)) {
+ next if m/^\./ && !$lprefix;
+ next unless length > $lprefix+$lsuffix;
+ next unless substr($_,0,$lprefix) eq $prefix;
+ next unless substr($_,length($_)-$lsuffix) eq $suffix;
+ $z= substr($_,$lprefix,length($_)-($lprefix+$lsuffix));
+ $zf= $dir.'/'.$prefix.$z.$suffix.$subfile;
+ if (!stat $zf) {
+ next if length $subfile && $! == &ENOENT;
+ cfg_fail("cannot stat zonefile $zf:\n $!");
+ }
+ -f _ or cfg_fail("zonefile $zf is not a plain file");
+ zone_conf($z,'primary','p',$mod,$zf);
+ }
+ closedir D or cfg_fail("close primary-dir $dir:\n $!");
+ } elsif (m/^primary([*?]?)\s+(\S+)\s+(\S+)$/) {
+ zone_conf($2,'primary','p',$1,qualify($3));
+ } elsif (m/^published([*?]?)\s+(\S+)\s+([0-9.\t]+)$/) {
+ zone_conf($2,'published','s',$1,'',$3);
+ } elsif (m/^stealth([*?]?)\s+(\S+)\s+([0-9. \t]+)$/) {
+ zone_conf($2,'stealth','u',$1,'',split /\s+/, $3);
+ } elsif (m/^slave\-dir\s+(\S+)(?:(?:\s+(\S+))\s+(\S+))?$/) {
+ ($slave_dir, $slave_prefix, $slave_suffix) = (qualify($1),$2,$3);
+ $slave_prefix='' if !defined $slave_prefix;
+ $slave_suffix='' if !defined $slave_suffix;
+ } elsif (m/^output\s+bind8\+(\S+)$/) {
+ cfg_fail("default output may not apply to only some zones")
+ if @zone_cfg_list && length $default_output;
+ set_output(qualify($1));
+ } elsif (m/^include\s+(\S+)$/) {
+ read_config($1);
+ } else {
+ cfg_fail("unknown configuration directive".
+ " or incorrect syntax or arguments:\n".
+ " \`$_'");
+ }