1#!/usr/bin/perl -w 2# 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License, Version 1.0 only 7# (the "License"). You may not use this file except in compliance 8# with the License. 9# 10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11# or http://www.opensolaris.org/os/licensing. 12# See the License for the specific language governing permissions 13# and limitations under the License. 14# 15# When distributing Covered Code, include this CDDL HEADER in each 16# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17# If applicable, add the following below this CDDL HEADER, with the 18# fields enclosed by brackets "[]" replaced with your own identifying 19# information: Portions Copyright [yyyy] [name of copyright owner] 20# 21# CDDL HEADER END 22# 23# 24# ident "%Z%%M% %I% %E% SMI" 25# 26# Copyright 2004 Sun Microsystems, Inc. All rights reserved. 27# Use is subject to license terms. 28# 29# 30# dictck -- Sanity check a .dict file and optionally the corresponding .po file 31# 32# example: dickck FMD.dict FMD.po 33# 34# usage: dickck [-vp] [ -b buildcode ] dictfile [ pofile ] 35# 36# -b specify location of "buildcode" command 37# 38# -p print a .po file template to stdout, based on dictfile given 39# 40# -v verbose, show how code is assembled 41# 42# Note: this program requires the "buildcode" program in your search path. 43# 44 45use strict; 46 47use Getopt::Std; 48 49use vars qw($opt_b $opt_p $opt_v); 50 51my $Myname = $0; # save our name for error messages 52$Myname =~ s,.*/,,; 53 54$SIG{HUP} = $SIG{INT} = $SIG{TERM} = $SIG{__DIE__} = sub { 55 # although fatal, we prepend "WARNING:" to make sure the 56 # commonly-used "nightly" script flags this as lint on the .dict file 57 die "$Myname: WARNING: @_"; 58}; 59 60# 61# usage -- print a usage message and exit 62# 63sub usage { 64 my $msg = shift; 65 66 warn "$Myname: $msg\n" if defined($msg); 67 warn "usage: $Myname [-pv] [ -b buildcode ] dictfile [ pofile ]\n"; 68 exit 1; 69} 70 71my %keys2val; 72my %val2keys; 73my %code2val; 74 75my $buildcode = 'buildcode'; 76 77# 78# the "main" for this script... 79# 80getopts('b:pv') or usage; 81 82my $dictfile = shift; 83my $pofile = shift; 84usage unless defined($dictfile); 85usage if @ARGV; 86$buildcode = $opt_b if defined($opt_b); 87dodict($dictfile); 88dopo($pofile) if defined($pofile); 89exit 0; 90 91# 92# dodict -- load up a .dict file, sanity checking it as we go 93# 94sub dodict { 95 my $name = shift; 96 my $dname; 97 my $line = 0; 98 my $lhs; 99 my $rhs; 100 my %props; 101 my $maxkey = 1; 102 103 if ($name =~ m,([^/]+)\.dict$,) { 104 $dname = $1; 105 } else { 106 die "dictname \"$name\" not something.dict as expected\n"; 107 } 108 109 open(F, $name) or die "$name: $!\n"; 110 print "parsing \"$name\"\n" if $opt_v; 111 while (<F>) { 112 $line++; 113 next if /^\s*#/; 114 chomp; 115 next if /^\s*$/; 116 die "$name:$line: first non-comment line must be FMDICT line\n" 117 unless /^FMDICT:/; 118 print "FMDICT keyword found on line $line\n" if $opt_v; 119 s/FMDICT:\s*//; 120 my $s = $_; 121 while ($s =~ /^\s*([^=\s]+)(.*)$/) { 122 $lhs = $1; 123 $rhs = ""; 124 $s = $+; 125 if ($s =~ /^\s*=\s*(.*)$/) { 126 $s = $+; 127 die "$name:$line: property \"$lhs\" incomplete\n" 128 unless $s ne ""; 129 } 130 if ($s =~ /^"((?:[^"]|\\")*)"(.*)$/) { 131 $s = $+; 132 $rhs = $1; 133 } else { 134 $s =~ /^([^\s]*)(.*)$/; 135 $s = $+; 136 $rhs = $1; 137 } 138 $rhs =~ s/\\(.)/dobs($1)/ge; 139 $props{$lhs} = $rhs; 140 print "property \"$lhs\" value \"$rhs\"\n" if $opt_v; 141 } 142 last; 143 } 144 # check for required headers 145 die "$name: no version property in header\n" 146 unless defined($props{'version'}); 147 die "$name: no name property in header\n" 148 unless defined($props{'name'}); 149 die "$name: no maxkey property in header\n" 150 unless defined($props{'maxkey'}); 151 152 # check version 153 die "$name:$line: unexpected version: \"$props{'version'}\"\n" 154 unless $props{'version'} eq "1"; 155 156 # check name 157 die "$name:$line: name \"$props{'name'}\" doesn't match \"$dname\" from filename\n" 158 unless $props{'name'} eq $dname; 159 160 # check format of maxkey (value checked later) 161 die "$name:$line: maxkey property must be a number\n" 162 unless $props{'maxkey'} =~ /^\d+$/; 163 164 # check for old bits property 165 die "$name: obsolete \"bits\" property found in header\n" 166 if defined($props{'bits'}); 167 168 # parse entries 169 while (<F>) { 170 $line++; 171 chomp; 172 s/#.*//; 173 next if /^\s*$/; 174 die "$name:$line: malformed entry\n" 175 unless /^([^=]+)=(\d+)$/; 176 $lhs = $1; 177 $rhs = $2; 178 179 # make sure keys are sorted 180 my $elhs = join(' ', sort split(/\s/, $lhs)); 181 die "$name:$line: keys not in expected format of:\n" . 182 " \"$elhs\"\n" 183 unless $elhs eq $lhs; 184 185 # check for duplicate or unexpected keys 186 my %keys; 187 foreach my $e (split(/\s/, $lhs)) { 188 die "$name:$line: unknown event type \"$e\"\n" 189 unless $e =~ 190 /^(fault|defect|upset|ereport)\..*[^.]$/; 191 die "$name:$line: key repeated: \"$e\"\n" 192 if defined($keys{$e}); 193 $keys{$e} = 1; 194 } 195 $maxkey = keys(%keys) if $maxkey < keys(%keys); 196 197 die "$name:$line: duplicate entry for keys\n" 198 if defined($keys2val{$lhs}); 199 die "$name:$line: duplicate entry for value $rhs\n" 200 if defined($val2keys{$rhs}); 201 $keys2val{$lhs} = $rhs; 202 $val2keys{$rhs} = $lhs; 203 204 open(B, "$buildcode $dname $rhs|") or 205 die "can't run buildcode: $!\n"; 206 my $code = <B>; 207 chomp $code; 208 close(B); 209 print "code: $code keys: $lhs\n" if $opt_v; 210 $code2val{$code} = $rhs; 211 212 if ($opt_p) { 213 print <<EOF; 214# 215# code: $code 216# keys: $lhs 217# 218msgid "$code.type" 219msgstr "XXX" 220msgid "$code.severity" 221msgstr "XXX" 222msgid "$code.description" 223msgstr "XXX" 224msgid "$code.response" 225msgstr "XXX" 226msgid "$code.impact" 227msgstr "XXX" 228msgid "$code.action" 229msgstr "XXX" 230EOF 231 } 232 } 233 234 print "computed maxkey: $maxkey\n" if $opt_v; 235 236 # check maxkey 237 die "$name: maxkey too low, should be $maxkey\n" 238 if $props{'maxkey'} < $maxkey; 239 240 close(F); 241} 242 243# 244# dobs -- handle backslashed sequences 245# 246sub dobs { 247 my $s = shift; 248 249 return "\n" if $s eq 'n'; 250 return "\r" if $s eq 'r'; 251 return "\t" if $s eq 't'; 252 return $s; 253} 254 255# 256# dopo -- sanity check a po file 257# 258sub dopo { 259 my $name = shift; 260 my $line = 0; 261 my $id; 262 my $code; 263 my $suffix; 264 my %ids; 265 266 open(F, $name) or die "$name: $!\n"; 267 print "parsing \"$name\"\n" if $opt_v; 268 while (<F>) { 269 $line++; 270 next if /^\s*#/; 271 chomp; 272 next if /^\s*$/; 273 next unless /^msgid\s*"([^"]+)"$/; 274 $id = $1; 275 next unless $id =~ 276 /^(.*)\.(type|severity|description|response|impact|action)$/; 277 $code = $1; 278 $suffix = $2; 279 die "$name:$line: no dict entry for code \"$code\"\n" 280 unless defined($code2val{$code}); 281 $ids{$id} = $line; 282 } 283 close(F); 284 285 # above checks while reading in file ensured that node code was 286 # mentioned in .po file that didn't exist in .dict file. now 287 # check the other direction: make sure the full set of entries 288 # exist for each code in the .dict file 289 foreach $code (sort keys %code2val) { 290 die "$name: missing entry for \"$code.type\"\n" 291 unless defined($ids{"$code.type"}); 292 die "$name: missing entry for \"$code.severity\"\n" 293 unless defined($ids{"$code.severity"}); 294 die "$name: missing entry for \"$code.description\"\n" 295 unless defined($ids{"$code.description"}); 296 die "$name: missing entry for \"$code.response\"\n" 297 unless defined($ids{"$code.response"}); 298 die "$name: missing entry for \"$code.impact\"\n" 299 unless defined($ids{"$code.impact"}); 300 die "$name: missing entry for \"$code.action\"\n" 301 unless defined($ids{"$code.action"}); 302 } 303} 304