#!/usr/bin/perl use warnings; use strict; my $basedir = "docs/log-message-tags"; my $serial_file = "$basedir/next-number"; my $serial = read_serial($serial_file); my $orig_serial = $serial; my %tags; my $rv = 0; my %intended_duplicate_tags; if (open(my $fh, "<", "$basedir/intended-duplicates")) { while (my $line = <$fh>) { if ($line =~ /^(\d+)\b/) { $intended_duplicate_tags{$1} = "true"; } } } foreach my $file (@ARGV) { if ($file !~ /\.c$/) { print STDERR "Skipping non-C file $file\n"; next; } process($file); } write_file($serial_file, "$serial\n") if $serial != $orig_serial; my $list = ""; foreach my $tag (sort keys %tags) { my $d = $tags{$tag}; $list .= "$tag: $d->{file}:$d->{line}: $d->{msg}\n"; } write_file("$basedir/list", $list); exit $rv; sub process { my $file = shift; open(my $fh, "<", $file) or die "open $file: $!"; #print STDERR "processing $file\n"; my $line = <$fh>; my $modified; my $result = ""; while (defined $line) { if ($line =~ s{APLOGNO\(\)}{gen_tag($file)}e) { $modified = 1; } if ($line =~ /APLOGNO\(\s*(\d{5})\s*\)/ ) { my $lineno = $.; my $tag = $1; while (1) { if ($line =~ s/.*? APLOGNO\(\s* (\d+) \s*\) ( (?: [\s\n]* (?:" (?:\\"|[^"])+ # a string constant " | \w+ # things like APR_SIZE_T_FMT ) )* # zero or more string fragments. We allow # zero because some logging constructs may # use things like: # logno=APLOGNO(...); # ap_log_...(..., "%s...", logno, ...) ) [\s\n]* [,);:\\] # the "," before the next argument, # or the closing brace of ap_log...(), # or the end of a statement (if used # outside of ap_log_...), # or ":" in things like: # cond ? "msg1" : "msg2", # or "\" at the end of a macro line //xs) { my $match = $&; note_tag($file, $lineno, $1, $2); $result .= $match; last; } else { my $next = <$fh>; defined $next or die "can't find end of format string in $file:$lineno"; $line .= $next; if ($next =~ /^#/) { # log format inside preprocessor #if, that's too complicated note_tag($file, $lineno, $tag, ""); $result .= $line; $line = ""; last; } } }; } else { $result .= $line; $line = <$fh>; } } close $fh; write_file($file, $result) if $modified; } sub gen_tag { my $file = shift; my $msg = shift; my $tag = sprintf('%05d', $serial++); return "APLOGNO($tag)"; } sub note_tag { my $file = shift; my $lineno = shift; my $tag = shift; my $msg = shift; my $oneline = ""; while (length $msg) { $msg =~ s/^[\s\n]+//s; if ($msg =~ s{^"((?:\\"|[^"])+)"}{}) { $oneline .= $1; } if ($msg =~ s{^(\w+)}{}) { $oneline .= $1; } } if (exists $tags{$tag} and not exists $intended_duplicate_tags{$tag}) { print STDERR "WARNING: Duplicate tag $tag at $tags{$tag}->{file}:$tags{$tag}->{line} and $file:$lineno\n"; $rv = 1; } if ($tag >= $serial) { print STDERR "WARNING: next-number $serial inconsistent with tag $tag at $file:$lineno, adjusting\n"; $rv = 1; $serial = $tag + 1; } $tags{$tag} = { file => $file, line => $lineno, msg => $oneline }; } sub write_file { my $file = shift; my $data = shift; my $tmpname = "$file.$$.tmp"; open(my $fh, ">", $tmpname) or die "open $tmpname: $!"; print $fh $data or die "write $tmpname: $!"; close($fh) or die "close $tmpname: $!"; rename($tmpname, $file) or die "rename $tmpname -> $file: $!"; print STDERR "Updated $file\n"; } sub read_serial { my $name = shift; open(my $fh, "<", $name) or die "can't open $name, need to be started in the top source dir"; my $num = <$fh>; chomp $num; $num =~ /^\d+$/ or die "invalid serial in $name: $num"; return $num; }