#!/usr/perl5/bin/perl -w
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# auditxml takes the audit record description (.xml file) and
# generates the files needed for the C audit api.
my $prog = $0; $prog =~ s|.*/||g;
my $usage = <<EOF;
Usage: $prog [options] <xml-input-file>
Options:
-d Enable debug output
-e pfx Internal event prefix (default: AUE)
-i pfx Interface prefix (default: adt)
External event prefix is uppercase version of this string.
-o dir Output directory (default: current dir)
EOF
use auditxml;
use Getopt::Std;
use strict;
our $debug = 0; # normal use is to set via the file being parsed.
# <debug set="on"/> or <debug set="off"/> or <debug/>
# if the set attribute is omitted, debug state is toggled
# Override with appDebug, but toggle won't do what you
# want.
my $appDebug = 0; # used after return from "new auditxml";
# Process command-line options
our ($opt_d, $opt_e, $opt_i, $opt_o);
$opt_e = "";
$opt_i = "";
$opt_o = "";
if (!getopts('de:i:o:') || $#ARGV != 0) {
die $usage;
}
my $outdir = $opt_o || ".";
my $pfx_adt = lc($opt_i) || "adt";
my $pfx_ADT = uc($pfx_adt);
my $pfx_AUE = uc($opt_e) || "AUE";
$appDebug = $opt_d;
my $uniLabel = "adr";
my $xlateUniLabelInc = 0;
# where everything comes from and where it goes:
my $xlateFile = "$outdir/${pfx_adt}_xlate.c";
my $headerFile = "$outdir/${pfx_adt}_event_N.h";
my $filename = $ARGV[0]; # input XML file
my $doc = new auditxml ($filename);
$filename =~ s|.*/||g;
$debug = $appDebug;
my $genNotice = "
DO NOT EDIT. This file is auto generated by the Solaris Audit
system from $filename.
See http://opensolaris.org/os/project/audit/
";
# trim leading/trailing newlines
$genNotice =~ s/^\n//s;
$genNotice =~ s/\n$//s;
my %xlateEventTable = ();
my @xlateTypeList = ();
my %xlateTypeList = ();
my %eventAPI = ();
my %eventExtra = ();
my %headers = ();
my %externalIdNo = ();
my @outputState = ();
my %nameTranslation = ();
my @xlateDefaults = ();
my %xlateDefault = ();
my %msg_list = ();
my $event;
while ($event = $doc->getNextEvent()) {
my $eventId = $event->getId();
my $eventHeader = $event->getHeader();
my $idNo = $event->getIdNo();
$externalIdNo{$eventId} = $idNo;
addHeader($eventHeader) if defined ($eventHeader);
my $super;
my $omit = $event->getOmit();
my $eventType = '';
if ($super = $event->getSuperClass()) {
$event = $super;
$eventType = 'instance';
} else {
$eventType = $event->getType();
}
# header file for API use
generateAPIFile($event, $eventId, $eventType, $eventHeader, $idNo)
unless $omit eq 'always';
# c file table for translation
generateTableC($event, $eventId, $eventType, $eventHeader, $omit);
}
my $textList;
while ($textList = $doc->getNextMsgId()) {
generateMsgLists($textList); # enum -> text mappings
}
printTableC($xlateFile);
printAPIFile($headerFile, $doc);
exit 0;
sub printTableC {
my $file = shift;
unless (open(Cfile, ">$file")) {
print STDERR "can't open output file ($file): $!\n";
return;
}
my $notice = $genNotice;
$notice =~ s/\n/\n * /gs;
$notice =~ s/\s+\n/\n/gs;
print Cfile <<EOF;
/*
* $notice
*/
#include <bsm/libbsm.h>
#include <adt_xlate.h>
#include <libintl.h>
EOF
print Cfile "#ifndef _PRAUDIT\n";
print Cfile "/* Internal data type definitions */\n\n";
my $extDef;
foreach $extDef (@xlateTypeList) {
print Cfile "static $extDef\n";
}
@xlateTypeList = ();
print Cfile "\n/* External event structure to internal event structure */\n\n";
my @pointers = ();
foreach my $eventId (sort keys %xlateEventTable) {
if ($xlateEventTable{$eventId}) {
my ($ref1, $eventType, $firstToken, $eventHeader) =
@{$xlateEventTable{$eventId}};
my @entries = @$ref1;
my $entry;
my $entries = $#entries;
my $count = $entries + 1;
my $externalName = $nameTranslation{$eventId};
my $externalRoot = $externalName;
$externalRoot =~ s/${pfx_AUE}_//;
my $structName = "XX_$externalRoot";
my $root = $eventId;
$root =~ s/${pfx_AUE}_//;
my $externalId = $eventId;
$externalId =~ s/${pfx_AUE}_/${pfx_ADT}_/;
unless ($eventType eq 'generic') {
print Cfile "static struct entry $structName\[$count\] = {\n";
foreach $entry (@entries) {
if ($entries--) {
$entry =~ s/EOL/,/;
}
else {
$entry =~ s/EOL//;
}
$entry =~ s/selfReference/$structName/;
print Cfile "\t$entry\n";
}
print Cfile "};\n";
print Cfile "static struct translation X_$externalRoot = {\n";
push (@pointers, "X_$externalRoot");
print Cfile "\t0,\n"; # tx_offsetsCalculated = 0
print Cfile "\t$externalId,\n";
print Cfile "\t$externalName,\n";
print Cfile "\t$count,\n";
print Cfile "\t&XX_$externalRoot\[$firstToken\],\n";
print Cfile "\t&XX_$externalRoot\[0\]\n};\n";
}
} else {
print STDERR "expected entry for $eventId but none found\n";
}
}
my $count = $#pointers + 2;
print Cfile "adt_translation_t *${pfx_adt}_xlate_table[$count] = {\n";
my $firstEvent = 1;
foreach my $eventId (@pointers) {
if ($firstEvent) {
$firstEvent = 0;
}
else {
print Cfile ",\n";
}
print Cfile "\t&$eventId";
}
print Cfile ",\n\tNULL\n};\n";
# generate the Event preload() function
print Cfile <<EOF;
void
${pfx_adt}_preload(au_event_t event_id, adt_event_data_t *event_data)
{
switch (event_id) {
EOF
foreach my $id (@xlateDefaults) {
my $adtID = $id;
$adtID =~ s/${pfx_AUE}/${pfx_ADT}/;
print Cfile <<EOF;
case $adtID:
EOF
my @preloads = @{$xlateDefault{$id}};
while (@preloads) {
my $fieldName = shift @preloads;
my $default = shift @preloads;
$id =~ s/${pfx_AUE}_/${pfx_adt}_/;
print Cfile <<EOF;
event_data->$id.$fieldName = $default;
EOF
}
print Cfile <<EOF;
break;
EOF
}
print Cfile <<EOF;
default:
break;
}
}
#endif
EOF
print Cfile "/* message lists */\n\n";
my $listName;
my @listName;
foreach $listName (sort keys %msg_list) {
my ($listRef, $headref) = @{$msg_list{$listName}};
my ($header, $start, $public, $deprecated) = @$headref;
my @listValue = @$listRef;
my $listValue;
my $listLength = $#listValue + 1;
$listName = 'NULL' if ($#listValue < 0);
push (@listName, [$listName, $listLength - 1, $start, $public]);
next if ($#listValue < 0);
print Cfile "/* Deprecated message list */\n" if ($deprecated);
print Cfile "static char *msg_$listName\[$listLength] = {\n";
my $ffirst = 1;
foreach $listValue (@listValue) {
print Cfile ",\n" unless $ffirst;
$ffirst = 0;
my ($id, $text) = split(/\s*::\s*/, $listValue);
if ($text) {
print Cfile "\t\"$text\"";
}
else {
print Cfile "\tNULL";
}
}
print Cfile "\n};\n";
}
if ($#listName >= 0) {
print Cfile "\nstruct msg_text ${pfx_adt}_msg_text[", $#listName + 1,
"] = {\n";
my $ffirst = 1;
foreach $listName (@listName) {
my ($name, $max, $start) = @$listName;
$start = -$start if $start;
print Cfile ",\n" unless $ffirst;
$ffirst = 0;
$name = "msg_$name" if ($name ne 'NULL');
print Cfile "\t{0, $max, $name, $start}";
}
print Cfile "\n};\n";
}
close Cfile;
}
sub printAPIFile {
my $file = shift;
my $xmlDoc = shift;
my @Hfile;
@Hfile = openHeaderFiles($file);
my $notice = $genNotice;
$notice =~ s/\n/\n * /gs;
$notice =~ s/\s+\n/\n/gs;
foreach my $header (keys %headers) {
next unless $Hfile[$header];
*Hfile = $Hfile[$header];
my $include = "adt.h";
my $adt_event_n = "_${pfx_ADT}_EVENT_H";
if ($header > 0) {
$include = "${pfx_adt}_event.h";
$adt_event_n = "_${pfx_ADT}_EVENT_".$header."_H";
}
print Hfile <<EOF;
/*
* $notice
*/
#ifndef $adt_event_n
#define $adt_event_n
#include <bsm/$include>
#ifdef __cplusplus
extern "C" {
#endif
/*
* adt_put_event() status values. Positive values are for kernel-generated
* failure, -1 for user-space. For ADT_SUCCESS, the adt_put_event() return_val
* is not used; the convention is to set it to ADT_SUCCESS.
*/
#define ADT_SUCCESS 0
#define ADT_FAILURE -1
EOF
}
foreach my $listName (sort keys %msg_list) {
my $shortName = uc $listName;
$shortName =~ s/_TEXT//;
my ($listRef, $headref) = @{$msg_list{$listName}};
my ($header, $start, $public, $deprecated) = @$headref;
next unless $Hfile[$header];
*Hfile = $Hfile[$header];
print Hfile "/* Deprecated message list */\n" if $deprecated;
print Hfile "#define\t${pfx_ADT}_$shortName\t$start\n" if $start;
my @listValue = @$listRef;
next unless ($#listValue >= 0);
print Hfile "enum\t${pfx_adt}_$listName", " {\n";
my $listValue;
my $i = 0;
my $j = $#listValue;
my $comma = ',';
foreach $listValue (@listValue) {
my ($id, $text) = split(/\s*::\s*/, $listValue);
$comma = '' if $i++ == $j;
if ($start) {
$start = " = $start$comma";
} else {
$start = "$comma\t";
}
$text = "(no token will be generated)" unless $text;
my $line = "\t${pfx_ADT}_$shortName"."_$id$start\t/* ";
# ensure whole line does not exceed 80 chars
my $eline = $line.$text;
#expand tabs
1 while $eline =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
if ((length($eline) > 77) && ($line =~ /\t\t/)) {
# 77 = 80 - length(" */")
# strip off double tab so that comment can be longer
$line =~ s/\t\t/\t/;
# shorten eline; don't mind where the spaces are removed, it is
# only $eline length which matters
$eline =~ s/ {8}//;
}
if (length($eline) > 77) { # 80 - length(" */")
# here we use negative length in substr to leave off from the
# right side; 74 = 77 - length("...")
$line .= substr($text, 0, 74 - length($eline));
# strip off part of last word (already cut)
$line =~ s/\s(\S+)$/ /;
$line .= "...";
} else {
$line .= $text;
}
print Hfile "$line */\n";
$start = '';
}
print Hfile "};\n";
}
# generate defines for external event names
foreach my $eventId (sort keys %eventAPI) {
my ($header, $idNo) = @{$eventExtra{$eventId}};
unless (defined ($header)) {
print STDERR "missing header selection for $eventId\n";
next;
}
*Hfile = $Hfile[$header];
next unless $Hfile[$header];
my $l = length($eventId) + 8; # label plus preceding #define\t
$l = 5 - int(($l + 8)/8);
$l = 1 if $l < 1;
my $tab = "\t" x $l;
print STDERR "missing id number for $eventId\n" unless $idNo;
$eventId =~ s/${pfx_AUE}_/${pfx_ADT}_/;
print Hfile "#define\t$eventId$tab$idNo\n";
}
# generate per-event structures
foreach my $eventId (sort keys %eventAPI) {
my ($header, $idNo) = @{$eventExtra{$eventId}};
my $dataId = $eventId;
$dataId =~ s/^${pfx_AUE}_/${pfx_adt}_/;
unless(defined ($header)) {
print STDERR "$eventId is missing the header assignment\n";
next;
}
*Hfile = $Hfile[$header];
next unless $Hfile[$header];
my $externalId = $eventId;
$externalId =~ s/${pfx_AUE}_/${pfx_ADT}_/;
print Hfile "\nstruct $dataId {\t/* $externalId */\n";
my @entries = @{$eventAPI{$eventId}};
my $entry;
if ($#entries < 0) {
print Hfile "\tint\tdummy;\t/* not used */\n";
} else {
foreach $entry (@entries) {
$entry =~ s/termid/adt_termid_t/;
print Hfile "\t$entry\n";
}
}
print Hfile "};\n";
$eventId =~ s/^${pfx_AUE}_/${pfx_adt}_/;
print Hfile "typedef struct $dataId $eventId","_t;\n";
}
foreach my $header (sort keys %headers) {
$outputState[$header] = 0;
}
foreach my $eventId (sort keys %eventAPI) {
my ($header, $idNo) = @{$eventExtra{$eventId}};
unless(defined ($header)) {
# don't print duplicate error message
next;
}
*Hfile = $Hfile[$header];
next unless $Hfile[$header];
if ($outputState[$header] == 0) {
$outputState[$header] = 1;
my $suffix = '';
$suffix = "_$header" if $header;
print Hfile "\nunion adt_event_data$suffix {\n";
}
my $elementName = $eventId;
$elementName =~ s/^${pfx_AUE}_/${pfx_adt}_/;
$eventId =~ s/^${pfx_AUE}_/${pfx_adt}_/;
$elementName =~ s/_t$//;
print Hfile "\t\t$eventId","_t\t$elementName;\n";
}
foreach my $header (sort keys %headers) {
if ($outputState[$header]) {
*Hfile = $Hfile[$header];
next unless $Hfile[$header];
print Hfile "};\n";
}
}
foreach my $header (keys %headers) {
next unless $Hfile[$header];
*Hfile = $Hfile[$header];
my $adt_event_n = "_${pfx_ADT}_EVENT_H";
if ($header > 0) {
$adt_event_n = "_${pfx_ADT}_EVENT_".$header."_H";
}
print Hfile <<EOF;
#ifndef ${pfx_ADT}_PRIVATE
#define ${pfx_ADT}_PRIVATE
/*
* These interfaces are project private and will change without
* notice as needed for the Solaris Audit project.
*/
extern void adt_get_auid(const adt_session_data_t *, au_id_t *);
extern void adt_set_auid(const adt_session_data_t *, const au_id_t);
extern void adt_get_mask(const adt_session_data_t *, au_mask_t *);
extern void adt_set_mask(const adt_session_data_t *, const au_mask_t *);
extern void adt_get_termid(const adt_session_data_t *, au_tid_addr_t *);
extern void adt_set_termid(const adt_session_data_t *,
const au_tid_addr_t *);
extern void adt_get_asid(const adt_session_data_t *, au_asid_t *);
extern void adt_set_asid(const adt_session_data_t *, const au_asid_t);
extern au_asid_t adt_get_unique_id(au_id_t);
extern void adt_load_table(const adt_session_data_t *, adt_translation_t **,
void (*preload)(au_event_t, adt_event_data_t *));
extern void ${pfx_adt}_preload(au_event_t, adt_event_data_t *);
extern adt_translation_t *${pfx_adt}_xlate_table[];
#endif
#ifdef __cplusplus
}
#endif
#endif /* $adt_event_n */
EOF
}
closeHeaderFiles(@Hfile);
}
sub generateTableC {
my $event = shift;
my $eventId = shift;
my $eventType = shift;
my $eventHeader = shift;
my $omit = shift;
my %tokenType = (
#
# tokenTypes are the ones that are actually defined
# for use in adt.xml audit records
#
# 'acl' => 'AUT_ACL', # not defined
# 'arbitrary' => 'AUT_ARBITRARY', # not defined
# 'arg' => 'AUT_ARG', # not defined
# 'attr' => 'AUT_ATTR',
'command' => 'AUT_CMD',
'command_alt' => 'ADT_CMD_ALT', # dummy token id
# 'date' => 'AUT_TEXT', # not used
# 'exec_args' => 'AUT_EXEC_ARGS', # not defined
# 'exec_env' => 'AUT_EXEC_ENV', # not defined
# 'exit' => 'AUT_EXIT', # not defined
'fmri' => 'AUT_FMRI',
# 'groups' => 'AUT_GROUPS', # not defined
# 'header' => 'AUT_HEADER', # not defined
'in_peer' => 'ADT_IN_PEER', # dummy token id
'in_remote' => 'ADT_IN_REMOTE', # dummy token id
# 'ipc' => 'AUT_IPC', # not defined
# 'ipc_perm' => 'AUT_IPC_PERM', # not defined
'iport' => 'AUT_IPORT',
'label' => 'AUT_LABEL',
'newgroups' => 'AUT_NEWGROUPS',
# 'opaque' => 'AUT_OPAQUE', # not defined
'path' => 'AUT_PATH',
'path_list' => '-AUT_PATH', # dummy token id
'process' => 'AUT_PROCESS',
'priv_effective' => 'ADT_AUT_PRIV_E', # dummy token id
'priv_limit' => 'ADT_AUT_PRIV_L', # dummy token id
'priv_inherit' => 'ADT_AUT_PRIV_I', # dummy token id
'return' => 'AUT_RETURN',
'secflags' => 'AUT_SECFLAGS',
# 'seq' => 'AUT_SEQ', # not defined
# 'socket' => 'AUT_SOCKET', # not defined
# 'socket-inet' => 'AUT_SOCKET_INET',
'subject' => 'AUT_SUBJECT',
'text' => 'AUT_TEXT',
'tid' => 'AUT_TID',
# 'trailer' => 'AUT_TRAILER', # not defined
'uauth' => 'AUT_UAUTH',
'user' => 'AUT_USER',
'zonename' => 'AUT_ZONENAME'
);
my @xlateEntryList = ();
my $external = $event->getExternal();
my $internal = $event->getInternal();
unless ($external) {
print STDERR "No external object captured for event $eventId\n";
return;
}
if ($eventType) {
$nameTranslation{$eventId} = $eventId;
} else {
$nameTranslation{$eventId} = $external->getInternalName();
}
unless ($internal) {
print STDERR "No internal object captured for event $eventId\n";
return;
}
my @entryRef = $internal->getEntries();
my $entryRef;
my @tokenOrder = ();
my $firstTokenIndex = 0; # djdj not used yet, djdj BUG!
# needs to be used by translate table
if ($internal->isReorder()) { # prescan the entry list to get the token order
my @inputOrder;
foreach $entryRef (@entryRef) {
my ($intEntry, $entry) = @$entryRef;
push (@inputOrder, $intEntry->getAttr('order'));
}
my $i; # walk down the inputOrder list once
my $k = 1; # discover next in line
my $l = 0; # who should point to next in line
for ($i = 0; $i <= $#inputOrder; $i++) {
my $j;
for ($j = 0; $j <= $#inputOrder; $j++) {
if ($k == $inputOrder[$j]) {
if ($k == 1) {
$firstTokenIndex = $j;
} else {
$tokenOrder[$l] = "&(selfReference[$j])";
}
$l = $j;
last;
}
}
$k++;
}
$tokenOrder[$l] = 'NULL';
}
else { # default order -- input order same as output
my $i;
my $j;
for ($i = 0; $i < $#entryRef; $i++) {
my $j = $i + 1;
$tokenOrder[$i] = "&(selfReference[$j])";
}
$tokenOrder[$#entryRef] = 'NULL';
}
my $sequence = 0;
foreach $entryRef (@entryRef) {
my ($intEntry, $entry) = @$entryRef;
my $entryId = $entry->getAttr('id');
my ($extEntry, $unusedEntry, $tokenId) =
$external->getEntry($entryId);
my $opt = $extEntry->getAttr('opt');
if ($opt eq 'none') {
if (defined ($doc->getToken($tokenId))) {
if (defined ($tokenType{$tokenId})) {
$tokenId = $tokenType{$tokenId};
}
else {
print STDERR "token id $tokenId not implemented\n";
}
}
else {
print STDERR "token = $tokenId is undefined\n";
$tokenId = 'error';
}
my ($xlate, $jni) =
formatTableEntry ('', $tokenId, $eventId, '', 0, 0,
$tokenOrder[$sequence], 'NULL', '', $omit);
push (@xlateEntryList, $xlate);
}
else {
my $dataType = $extEntry->getAttr('type');
$dataType =~ s/\s+//g; # remove blanks (char * => char*)
my $enumGroup = '';
if ($dataType =~ /^msg/i) {
$enumGroup = $dataType;
$enumGroup =~ s/^msg\s*//i;
$enumGroup = "${pfx_adt}_" . $enumGroup;
}
my $required = ($opt eq 'required') ? 1 : 0;
my $tsol = 0;
my $tokenId = $intEntry->getAttr('token');
my $token;
my $tokenName;
my $tokenFormat = $intEntry->getAttr('format');
if (defined ($tokenFormat)) {
$tokenFormat = "\"$tokenFormat\"";
}
else {
$tokenFormat = 'NULL';
}
if (defined ($token = $doc->getToken($tokenId))) {
$tsol = (lc $token->getUsage() eq 'tsol') ? 1 : 0;
if (defined ($tokenType{$tokenId})) {
$tokenName = $tokenType{$tokenId};
}
else {
print STDERR "token id $tokenId not implemented\n";
}
}
else {
print STDERR
"$tokenId is an unimplemented token ($entryId in $eventId)\n";
$tokenName = 'AUT_TEXT';
}
my ($xlate, $jni) =
formatTableEntry($entryId, $tokenName, $eventId, $dataType, $required,
$tsol, $tokenOrder[$sequence], $tokenFormat,
$enumGroup, $omit);
push (@xlateEntryList, $xlate);
}
$sequence++;
}
$xlateEventTable{$eventId} = [\@xlateEntryList, $eventType, $firstTokenIndex,
$eventHeader];
}
sub formatTableEntry {
my ($id, $token, $eventId, $type, $required, $tsol, $sequence, $format,
$enumGroup, $omitEntry) = @_;
# does this map belong in the xml source? (at least the defaults?)
# fill in the default value only if it is other than zero.
# base type adt name, default value
my %entryDef = ( 'au_asid_t' => ['ADT_UINT32', ''],
'uint_t' => ['ADT_UINT32', ''],
'int' => ['ADT_INT', ''],
'int32_t' => ['ADT_INT32', ''],
'uid_t' => ['ADT_UID', 'AU_NOAUDITID'],
'gid_t' => ['ADT_GID', 'AU_NOAUDITID'],
'uid_t*' => ['ADT_UIDSTAR', ''],
'gid_t*' => ['ADT_GIDSTAR', ''],
'char' => ['ADT_CHAR', ''],
'char*' => ['ADT_CHARSTAR', ''],
'char**' => ['ADT_CHAR2STAR', ''],
'long' => ['ADT_LONG', ''],
'pid_t' => ['ADT_PID', ''],
'priv_set_t*' => ['ADT_PRIVSTAR', ''],
'ulong_t' => ['ADT_ULONG', ''],
'uint16_t', => ['ADT_UINT16', ''],
'uint32_t' => ['ADT_UINT32', ''],
'uint32_t*' => ['ADT_UINT32STAR', ''],
'uint32_t[]' => ['ADT_UINT32ARRAY', ''],
'uint64_t' => ['ADT_UINT64', ''],
'uint64_t*' => ['ADT_UINT64STAR', ''],
'm_label_t*' => ['ADT_MLABELSTAR', ''],
'fd_t' => ['ADT_FD', '-1'],
);
my $xlateLabel = $uniLabel.$xlateUniLabelInc;
my $xlateLabelInc = 0;
my $xlateLine = '';
my @jniLine = ();
# the list handling should be a simple loop with a loop of one
# falling out naturally.
unless ($type =~ /,/) { # if list, then generate sequence of entries
my $dataType;
my $dataSize;
my $xlateLabelRef = '';
my $arraySize = '';
$arraySize = $1 if ($type =~ s/\[(\d+)\]/[]/);
my $entryType = ${$entryDef{$type}}[0];
my @xlateType = (); # for adt_xlate.c
my $typeCount = 1;
if ($entryType) {
$dataType = $entryType;
$type =~ s/([^*]+)\s*(\*+)/$1 $2/;
$type =~ s/\[\]//;
$dataSize = "sizeof ($type)";
if ($arraySize) {
$dataSize = "$arraySize * " . $dataSize;
}
$xlateLine = "{{$dataType, $dataSize}}";
push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
} elsif ($type eq '') {
$xlateLabelRef = 'NULL';
} elsif ($type =~ /^msg/i) {
$type =~ s/^msg//i;
$dataType = 'ADT_MSG';
my $dataEnum = 'ADT_LIST_' . uc $type;
$xlateLine = "{{$dataType, $dataEnum}}";
push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
} elsif ($type =~ /time_t/i) {
$dataType = 'ADT_DATE';
$dataSize = "sizeof (time_t)";
$xlateLine = "{{$dataType, $dataSize}}";
push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
} elsif ($type =~ /termid/i) {
$dataType = 'ADT_TERMIDSTAR';
$dataSize = "sizeof (au_tid_addr_t *)";
$xlateLine = "{{$dataType, $dataSize}}";
push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
} elsif (uc $omitEntry eq 'JNI') {
$xlateLabelRef = 'NULL';
} else {
print STDERR "$type is not an implemented data type\n";
$xlateLabelRef = 'NULL';
}
if ($xlateLine && !($xlateTypeList{$xlateLine})) {
$xlateTypeList{$xlateLine} = $xlateLabel;
push (@xlateTypeList, "datadef\t$xlateLabel\[1\] =\t$xlateLine;");
$xlateLabelInc = 1;
} else {
$xlateLabel = $xlateTypeList{$xlateLine};
}
$xlateLabelRef = '&' . $xlateLabel . '[0]'
unless $xlateLabelRef eq 'NULL';
# "EOL" is where a comma should go unless end of list
$xlateLine = "{$token,\t1,\t$xlateLabelRef,\t$sequence,\n" .
"\t\t0,\t$required,\t$tsol,\t$format}EOL";
if (uc $omitEntry ne 'ALWAYS' && ${$entryDef{$type}}[1]) {
my @list = ();
if ($xlateDefault{$eventId}) {
@list = @{$xlateDefault{$eventId}};
} else {
push (@xlateDefaults, $eventId);
}
push (@list, $id, ${$entryDef{$type}}[1]);
$xlateDefault{$eventId} = \@list;
}
} else { # is a list
my @type = split(/,/, $type);
my @arraySize = ();
my @id = split(/,/, $id);
my @jniId = @id;
my $dataType;
my $typeCount = ($#type + 1);
my @xlateType = ();
my @default = ();
foreach my $dtype (@type) {
my $jniId = shift @jniId;
my $id = shift @id;
my $arraySize = '';
$arraySize = $1 if ($dtype =~ s/\[(\d+)\]/[]/);
my $entryType = ${$entryDef{$dtype}}[0];
if ($entryType) {
my $type = $dtype;
$type =~ s/([^*]+)\s*(\*+)/$1 $2/;
$type =~ s/\[\]//;
my $sizeString = "sizeof";
$sizeString = "$arraySize * " . $sizeString if $arraySize;
push (@xlateType, "\{$entryType, $sizeString ($type)\}");
push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
} elsif ($type =~ /^msg/i) {
$type =~ s/^msg//i;
$dataType = 'ADT_MSG';
my $dataEnum = 'ADT_LIST_' . uc $type;
push (@xlateType, "\{$dataType, $dataEnum\}};");
push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
} elsif ($type =~ /time_t/i) {
$dataType = 'ADT_DATE';
push (@xlateType, "\{$entryType, sizeof ($type)\}");
push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
} elsif ($type =~ /termid/i) {
$dataType = 'ADT_TERMIDSTAR';
push (@xlateType, "\{$dataType, sizeof (au_tid_addr_t *)\}");
push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
} elsif (uc $omitEntry eq 'JNI') {
# nothing to do.
} else {
print STDERR "$dtype is not an implemented data type\n";
}
if (uc $omitEntry ne 'ALWAYS' && ${$entryDef{$dtype}}[1]) {
push (@default, $id, ${$entryDef{$dtype}}[1]);
}
}
my $xlateArray = "\[$typeCount\] =\t{" . join(",\n\t\t\t\t", @xlateType) . "};";
unless ($xlateTypeList{$xlateArray}) {
$xlateTypeList{$xlateArray} = $xlateLabel;
$xlateArray = "datadef\t$xlateLabel" . $xlateArray;
push (@xlateTypeList, $xlateArray);
$xlateLabelInc = 1;
} else {
$xlateLabel = $xlateTypeList{$xlateArray};
}
$xlateLine =
"{$token,\t$typeCount,\t&$xlateLabel\[0\],\t$sequence,\n" .
"\t\t0,\t$required,\t$tsol,\t$format}EOL";
if (@default) {
my @list = ();
if ($xlateDefault{$eventId}) {
@list = @{$xlateDefault{$eventId}};
} else {
push (@xlateDefaults, $eventId);
}
push (@list, @default);
$xlateDefault{$eventId} = \@list;
}
}
$xlateUniLabelInc++ if $xlateLabelInc;
return ($xlateLine, \@jniLine);
}
sub generateAPIFile {
my $event = shift;
my $eventId = shift;
my $eventType = shift;
my $eventHeader = shift;
my $idNo = shift;
my @entryList = ();
my $external = $event->getExternal();
if ($eventType && $debug) {
print STDERR "event $eventId is of type $eventType\n";
}
return unless $external;
my ($extEntry, $entry, $tokenId, $format);
while (($extEntry, $entry, $tokenId, $format) = $external->getNextEntry()) {
last unless $entry;
my $entryId = $entry->getAttr('id');
unless (defined $entryId) {
print STDERR "undefined entry id for external $eventId\n";
next;
}
my $option = $extEntry->getAttr('opt');
next if ($option eq 'none');
if (defined (my $token = $doc->getToken($tokenId))) {
$option = 'Trusted Solaris only'
if (lc $token->getUsage() eq 'tsol') ? 1 : 0;
}
$option .= " (format: $format)" if $format;
my $dataType = $extEntry->getAttr('type');
unless (defined $dataType) {
print STDERR "no type defined for external tag for $eventId\n";
$dataType = "error";
}
my $comment = $entry->getContent();
if (($dataType =~ /,/) || ($entryId =~ /,/)) {
my @type = split(/\s*,\s*/, $dataType);
my @id = split(/\s*,\s*/, $entryId);
if ($#type != $#id) {
print STDERR
"number of data types ($dataType) does not match number of ids ($entryId)",
" for event $eventId\n";
if ($#type < $#id) {
$#id = $#type;
}
else {
$#type = $#id;
}
}
my $i;
my $line = '';
$line = "/* $comment */\n\t" if defined $comment;
for ($i = 0; $i <= $#type; $i++) {
my ($primitive, $dereference) =
($type[$i] =~ /([^\*]+)\s*(\**)/);
$id[$i] .= $1 if ($primitive =~ s/(\[\d+\])//);
$line .= "$primitive\t$dereference$id[$i];\t/* $option */";
push (@entryList, $line);
$line = '';
}
}
else {
my $line = '';
$line = "/* $comment */\n\t" if defined $comment;
if ($dataType =~ /^msg/i) {
$dataType =~ s/^msg\s*//i;
$line .= "enum ${pfx_adt}_$dataType" . "\t$entryId;\t/* $option */";
}
elsif ($dataType =~ /time_t/i) {
$line .= "time_t\t$entryId;\t/* $option */";
}
else {
my ($primitive, $dereference) =
($dataType =~ /([^\*]+)\s*(\**)/);
$entryId .= $1 if ($primitive =~ s/(\[\d+\])//);
$line .= "$primitive\t$dereference$entryId;\t/* $option */";
}
push (@entryList, $line);
}
}
$eventExtra{$eventId} = [$eventHeader, $idNo];
$eventAPI{$eventId} = \@entryList;
}
sub generateMsgLists {
my $textList = shift;
my $textName = $textList->getId();
my $header = $textList->getHeader();
my $start = $textList->getMsgStart();
my $public = $textList->getMsgPublic();
my $deprecated = $textList->getDeprecated();
addHeader($header);
print "$textName starts at $start\n" if $debug;
my $entry;
my @entry;
while ($entry = $textList->getNextMsg()) {
if ($debug) {
my ($id, $text) = split(/\s*::\s*/, $entry);
print " $id = $text\n";
}
unshift (@entry, $entry);
}
$msg_list{$textName} =
[\@entry, [$header, $start, $public, $deprecated]];
}
sub addHeader {
my $header_index = shift;
die "invalid adt_event_N.h index: $header_index\n"
unless ($header_index =~ /^\d+$/);
$headers{$header_index} = $header_index;
}
# $header = 0 is a special case; it is for adt_event.h
# $header > 0 creates adt_event_N.h, where N = $header
sub openHeaderFiles {
my $outfile = shift; # path to an adt_event_N.h file
my $header;
my @Hfile = (); # potentially sparse array of file handles
my @HfileName = (); # parallel array to Hfile, file name (not path)
foreach $header (sort keys %headers) {
my $file = $outfile;
if ($header > 0) {
$file =~ s/_N/_$header/;
} else {
$file =~ s/_N//;
}
unless (open($Hfile[$header], ">$file")) {
print STDERR "can't open output ($file): $!\n";
$HfileName[$header] = '';
$Hfile[$header] = '';
} else {
my @tmp = split(/\//, $file);
$HfileName[$header] = $tmp[$#tmp];
}
}
return (@Hfile);
}
sub closeHeaderFiles {
my @Hfile = @_;
my $header;
foreach $header (sort keys %headers) {
close $Hfile[$header] if $Hfile[$header];
}
}