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