+
+sub checkxmllint
+{
+ open(FH, "xmllint|") or die "Couldn't start xmllint: $!\n";
+}
+
+sub cmd_checkconf()
+{
+ checkxmllint();
+ validateconf($conf);
+ print "Config check complete\n";
+ exit 0;
+}
+
+my %filechecked;
+
+sub validateconf
+{
+ my ($file) = @_;
+
+ # Are We using a relative path?
+ if ($file !~ /^\//) {
+ # Convert it to a full path..
+ $file = $confpath . $file;
+ }
+
+ # Have we checked this file before?
+ return if $filechecked{$file};
+ $filechecked{$file} = 1;
+
+ # Open the File..
+ open INFILE, "< $file" or die "Unable to open file $file\n";
+ # Grab entire file contents..
+ my(@lines) = <INFILE>;
+ # Close the file
+ close INFILE;
+
+ # remove trailing spaces
+ chomp(@lines);
+
+ my @newlines = ();
+ my @blanks = ();
+ my $conline;
+
+ push @newlines, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
+# push @newlines, "<!DOCTYPE config SYSTEM \"".$confpath."inspircd.dtd\">";
+ push @newlines, "<config>";
+
+ for my $i (@lines)
+ {
+ # remove trailing newlines
+ chomp($i);
+
+ # convert tabs to spaces
+ $i =~ s/\t/ /g;
+
+ # remove leading spaces
+ $i =~ s/^ *//;
+
+ # remove comments
+ $i =~ s/^#.*//;
+
+ # remove trailing #s
+ $i =~ s/(.*)#$/\1/;
+
+ # remove trailing comments
+ my $line = "";
+ my $quote = 0;
+ for (my $j = 0; $j < length($i); $j++)
+ {
+ if (substr($i,$j, 1) eq '"') { $quote = ($quote) ? 0 : 1; } elsif (substr($i,$j, 1) eq "#" && !$quote) { last; }
+ $line .= substr($i,$j, 1);
+ }
+ $i = $line;
+
+ # remove trailing spaces
+ $i =~ s/ *$//;
+
+ # setup incf for include check and clean it up, since this breaks parsing use local var
+ my $incf = $i;
+ $incf =~ s/[^=]+=\s(.*)/\1/;
+
+ # include file?
+ if (($incf =~ s/\<include file=\"(.+?)\"\>//i) && ($incf !~ /^#/))
+ {
+ # yes, process it
+ validateconf($1);
+ }
+
+ if ($i =~ /^<.*/ && $conline =~ /^<.*/)
+ {
+ push @newlines, $conline;
+ push @newlines, @blanks;
+ $conline = $i;
+ }
+
+ if ($i =~ /^<.*>$/)
+ {
+ $i =~ s/(.*)>$/\1 \/>/;
+ push @newlines, $i;
+ }
+ elsif ($i =~ /.*>$/)
+ {
+ $conline .= " $i";
+ $conline =~ s/(.*)>$/\1 \/>/;
+ push @blanks, "";
+ push @newlines, $conline;
+ push @newlines, @blanks;
+ $conline = "";
+ undef @blanks;
+ }
+ elsif ($i =~ /^<.*/)
+ {
+ $conline = $i;
+ }
+ elsif ($conline =~ /^<.*/ && $i)
+ {
+ $conline .= " $i";
+ push @blanks, "";
+ }
+ else
+ {
+ if ($conline)
+ {
+ push @blanks, $i;
+ }
+ else
+ {
+ push @newlines, $i;
+ }
+ }
+ }
+ if ($conline)
+ {
+ push @newlines, $conline;
+ push @newlines, @blanks;
+ }
+
+ push @newlines, "</config>";
+
+ my $tmpfile;
+ do
+ {
+ $tmpfile = tmpnam();
+ } until sysopen(TF, $tmpfile, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0700);
+
+ for my $n (@newlines)
+ {
+ print TF "$n\n";
+ }
+ close TF;
+
+ my @result = `xmllint -noout $tmpfile 2>&1`;
+ chomp(@result);
+
+ my $skip = 0;
+ for my $n (@result)
+ {
+ if ($skip)
+ {
+ $skip = 0;
+ next;
+ }
+ $n =~ s/$tmpfile\:\d*\: *//g;
+ if ($n =~ /.*config>.*/)
+ {
+ $n = "";
+ $skip = 1;
+ }
+
+ if ($n && !$skip)
+ {
+ if ($n =~ /line \d*/)
+ {
+ my $lineno = $n;
+ $lineno =~ s/.*line (\d*).*/\1/;
+ $lineno = $lineno-2;
+ $n =~ s/line (\d*)/line $lineno/;
+ }
+ print "$file : $n\n";
+ }
+ }
+ unlink($tmpfile);
+}