X-Git-Url: http://git.indexdata.com/?a=blobdiff_plain;f=util%2Fyaz-comp;fp=util%2Fyaz-comp;h=5e1199f908b0a61ba5ad23b5e8173e0a8a3f6d69;hb=d90d31f39aba4c7256d5b83eaf4192630b18ae02;hp=0000000000000000000000000000000000000000;hpb=988421f953de4623a11b90008c52e135631078ba;p=yaz-moved-to-github.git diff --git a/util/yaz-comp b/util/yaz-comp new file mode 100755 index 0000000..5e1199f --- /dev/null +++ b/util/yaz-comp @@ -0,0 +1,1438 @@ +#!/bin/sh +# the next line restarts using tclsh \ +exec tclsh "$0" "$@" +# +# yaz-comp: ASN.1 Compiler for YAZ +# (c) Index Data 1996-2000 +# See the file LICENSE for details. +# +# $Log: yaz-comp,v $ +# Revision 1.1 2000-03-02 08:48:20 adam +# Renamed ASN.1 compiler to yaz-comp (used to be yc.tcl). +# +# Revision 1.6 2000/02/10 13:44:02 adam +# Tcl command clock not used if unavailable (Tcl7.4 and earlier). +# +# Revision 1.5 2000/01/15 09:18:42 adam +# Bug fix: some elements where treated as OPTIONAL when they shouldn't. +# +# Revision 1.4 1999/12/16 23:36:19 adam +# Implemented ILL protocol. Minor updates ASN.1 compiler. +# +# Revision 1.3 1999/11/30 13:47:12 adam +# Improved installation. Moved header files to include/yaz. +# +# Revision 1.2 1999/06/09 09:43:11 adam +# Added option -I and variable h-path to specify path for header files. +# +# Revision 1.1 1999/06/08 10:10:16 adam +# New sub directory zutil. Moved YAZ Compiler to be part of YAZ tree. +# +# Revision 1.8 1999/04/20 10:37:04 adam +# Updated for ODR - added name parameter. +# +# Revision 1.7 1998/04/03 14:44:20 adam +# Small fix. +# +# Revision 1.6 1998/04/03 13:21:17 adam +# Yet another fix. +# +# Revision 1.5 1998/04/03 12:48:17 adam +# Fixed bug: missed handling of constructed tags for CHOICE. +# +# Revision 1.4 1998/03/31 15:47:45 adam +# First compiled ASN.1 code for YAZ. +# +# Revision 1.3 1998/03/23 17:13:20 adam +# Implemented SET OF and ENUM. The Compiler now eats ILL (ISO10161) and +# LDAP (RFC1777). +# +# Revision 1.2 1997/10/07 10:31:01 adam +# Added facility to specify tag type (CONTEXT, APPLICATION, ...). +# +# Revision 1.1.1.1 1996/10/31 14:04:40 adam +# First version of the compiler for YAZ. +# +# + +set yc_version 0.3 + +# Syntax for the ASN.1 supported: +# file -> file module +# | module +# module -> name skip DEFINITIONS ::= mbody END +# mbody -> EXPORTS { nlist } +# | IMPORTS { imlist } +# | name ::= tmt +# | skip +# tmt -> tag mod type +# type -> SEQUENCE { sqlist } +# | SEQUENCE OF type +# | CHOICE { chlist } +# | basic enlist +# +# basic -> INTEGER +# | BOOLEAN +# | OCTET STRING +# | BIT STRING +# | EXTERNAL +# | name +# sqlist -> sqlist , name tmt opt +# | name tmt opt +# chlist -> chlist , name tmt +# | name tmt +# enlist -> enlist , name (n) +# | name (n) +# imlist -> nlist FROM name +# imlist nlist FROM name +# nlist -> name +# | nlist , name +# mod -> IMPLICIT | EXPLICIT | e +# tag -> [tagtype n] | [n] | e +# opt -> OPTIONAL | e +# +# name identifier/token +# e epsilon/empty +# skip one token skipped +# n number +# tagtype APPLICATION, CONTEXT, etc. + +# lex: moves input file pointer and returns type of token. +# The globals $type and $val are set. $val holds name if token +# is normal identifier name. +# sets global var type to one of: +# {} eof-of-file +# \{ left curly brace +# \} right curly brace +# , comma +# ; semicolon +# ( (n) +# [ [n] +# : ::= +# n other token n +proc lex {} { + global inf val type + while {![string length $inf(str)]} { + incr inf(lineno) + set inf(cnt) [gets $inf(inf) inf(str)] + if {$inf(cnt) < 0} { + set type {} + return {} + } + lappend inf(asn,$inf(asndef)) $inf(str) + set l [string first -- $inf(str)] + if {$l >= 0} { + incr l -1 + set inf(str) [string range $inf(str) 0 $l] + } + set inf(str) [string trim $inf(str)] + } + set s [string index $inf(str) 0] + set type $s + set val {} + switch -- $s { + \{ { } + \} { } + , { } + ; { } + \( { } + \) { } + \[ { regexp {^\[[ ]*(.+)[ ]*\]} $inf(str) s val } + : { regexp {^::=} $inf(str) s } + default { + regexp "^\[^,\t :\{\}();\]+" $inf(str) s + set type n + set val $s + } + } + set off [string length $s] + set inf(str) [string trim [string range $inf(str) $off end]] + return $type +} + +# lex-expect: move pointer and expect token $t +proc lex-expect {t} { + global type val + lex + if {[string compare $t $type]} { + asnError "Got $type '$val', expected $t" + } +} + +# lex-name-move: see if token is $name; moves pointer and returns +# 1 if it is; returns 0 otherwise. +proc lex-name-move {name} { + global type val + if {![string compare $type n] && ![string compare $val $name]} { + lex + return 1 + } + return 0 +} + +# asnError: Report error and die +proc asnError {msg} { + global inf + + puts "Error in line $inf(lineno) in module $inf(module)" + puts " $msg" + error + exit 1 +} + +# asnWarning: Report warning and return +proc asnWarning {msg} { + global inf + + puts "Warning in line $inf(lineno) in module $inf(module)" + puts " $msg" +} + +# asnEnum: parses enumerated list - { name1 (n), name2 (n), ... } +# Uses $name as prefix. If there really is a list, $lx holds the C +# preprocessor definitions on return; otherwise lx isn't set. +proc asnEnum {name lx} { + global type val inf + + if {[string compare $type \{]} return + upvar $lx l + while {1} { + set pq [asnName $name] + set id [lindex $pq 0] + set id ${name}_$id + lex-expect n + lappend l "#define $inf(dprefix)$id $val" + lex-expect ")" + lex + if {[string compare $type ,]} break + } + if {[string compare $type \}]} { + asnError "Missing \} in enum list got $type '$val'" + } + lex +} + +# asnMod: parses tag and modifier. +# $xtag and $ximplicit holds tag and implicit-indication on return. +# $xtag is empty if no tag was specified. $ximplicit is 1 on implicit +# tagging; 0 otherwise. +proc asnMod {xtag ximplicit xtagtype} { + global type val inf + + upvar $xtag tag + upvar $ximplicit implicit + upvar $xtagtype tagtype + + set tag {} + set tagtype {} + if {![string compare $type \[]} { + if {[regexp {^([a-zA-Z]+)[ ]+([0-9]+)$} $val x tagtype tag]} { + set tagtype ODR_$tagtype + } elseif {[regexp {^([0-9]+)$} $val x tag]} { + set tagtype ODR_CONTEXT + } else { + asnError "bad tag specification: $val" + } + lex + } + set implicit $inf(implicit-tags) + if {![string compare $type n]} { + if {![string compare $val EXPLICIT]} { + lex + set implicit 0 + } elseif {![string compare $val IMPLICIT]} { + lex + set implicit 1 + } + } +} + +# asnName: moves pointer and expects name. Returns C-validated name. +proc asnName {name} { + global val inf + lex-expect n + if {[info exists inf(membermap,$inf(module),$name,$val)]} { + set nval $inf(membermap,$inf(module),$name,$val) + if {$inf(verbose)} { + puts " mapping member $name,$val to $nval" + } + lex + } else { + set nval $val + if {![string match {[A-Z]*} $val]} { + lex + } + } + return [join [split $nval -] _] +} + +# asnOptional: parses optional modifier. Returns 1 if OPTIONAL was +# specified; 0 otherwise. +proc asnOptional {} { + global type val + if {[lex-name-move OPTIONAL]} { + return 1 + } elseif {[lex-name-move DEFAULT]} { + lex + return 0 + } + return 0 +} + +# asnSizeConstraint: parses the optional SizeConstraint. +# Currently not used for anything. +proc asnSizeConstraint {} { + global type val + if {[lex-name-move SIZE]} { + asnSubtypeSpec + } +} + +# asnSubtypeSpec: parses the SubtypeSpec ... +# Currently not used for anything. We now it's balanced however, i.e. +# (... ( ... ) .. ) +proc asnSubtypeSpec {} { + global type val + + if {[string compare $type "("]} { + return + } + lex + set level 1 + while {$level > 0} { + if {![string compare $type "("]} { + incr level + } elseif {![string compare $type ")"]} { + incr level -1 + } + lex + } +} + +# asnType: parses ASN.1 type. +# On entry $name should hold the name we are currently defining. +# Returns type indicator: +# SequenceOf SEQUENCE OF +# Sequence SEQUENCE +# SetOf SET OF +# Set SET +# Choice CHOICE +# Simple Basic types. +# In this casecalling procedure's $tname variable is a list holding: +# {C-Function C-Type} if the type is IMPORTed or ODR defined. +# or +# {C-Function C-Type 1} if the type should be defined in this module +proc asnType {name} { + global type val inf + upvar tname tname + + set tname {} + if {[string compare $type n]} { + asnError "Expects type specifier, but got $type" + } + set v $val + lex + switch -- $v { + SEQUENCE { + asnSizeConstraint + if {[lex-name-move OF]} { + asnSubtypeSpec + return SequenceOf + } else { + asnSubtypeSpec + return Sequence + } + } + SET { + asnSizeConstraint + if {[lex-name-move OF]} { + asnSubtypeSpec + return SetOf + } else { + asnSubtypeSpec + return Set + } + } + CHOICE { + asnSubtypeSpec + return Choice + } + } + if {[string length [info commands asnBasic$v]]} { + set tname [asnBasic$v] + } else { + if {[info exists inf(map,$inf(module),$v)]} { + set v $inf(map,$inf(module),$v) + } + if {[info exists inf(imports,$v)]} { + set tname $inf(imports,$v) + } else { + set w [join [split $v -] _] + set tname [list $inf(fprefix)$w $inf(vprefix)$w 1] + } + } + if {[lex-name-move DEFINED]} { + if {[lex-name-move BY]} { + lex + } + } + asnSubtypeSpec + return Simple +} + +proc mapName {name} { + global inf + if {[info exists inf(map,$inf(module),$name)]} { + set name $inf(map,$inf(module),$name) + if {$inf(verbose)} { + puts -nonewline " $name ($inf(lineno))" + puts " mapping to $name" + } + } else { + if {$inf(verbose)} { + puts " $name ($inf(lineno))" + } + } + return $name +} + +# asnDef: parses type definition (top-level) and generates C code +# On entry $name holds the type we are defining. +proc asnDef {name} { + global inf file + + set name [mapName $name] + if {[info exist inf(defined,$inf(fprefix)$name)]} { + incr inf(definedl,$name) + if {$inf(verbose) > 1} { + puts "set map($inf(module),$name) $name$inf(definedl,$name)" + } + } else { + set inf(definedl,$name) 0 + } + set mname [join [split $name -] _] + asnMod tag implicit tagtype + set t [asnType $mname] + asnSub $mname $t $tname $tag $implicit $tagtype +} + + +# asnSub: parses type and generates C-code +# On entry, +# $name holds the type we are defining. +# $t is the type returned by the asnType procedure. +# $tname is the $tname set by the asnType procedure. +# $tag is the tag as returned by asnMod +# $implicit is the implicit indicator as returned by asnMod +proc asnSub {name t tname tag implicit tagtype} { + global file inf + + set ignore 0 + set defname defined,$inf(fprefix)$name + if {[info exist inf($defname)]} { + asnWarning "$name already defined in line $inf($defname)" + set ignore 1 + } + set inf($defname) $inf(lineno) + switch -- $t { + Sequence { set l [asnSequence $name $tag $implicit $tagtype] } + SequenceOf { set l [asnOf $name $tag $implicit $tagtype 0] } + SetOf { set l [asnOf $name $tag $implicit $tagtype 1] } + Choice { set l [asnChoice $name $tag $implicit $tagtype] } + Simple { set l [asnSimple $name $tname $tag $implicit $tagtype] } + default { asnError "switch asnType case not handled" } + } + if {$ignore} return + + puts $file(outc) {} + puts $file(outc) "int $inf(fprefix)$name (ODR o, $inf(vprefix)$name **p, int opt, const char *name)" + puts $file(outc) \{ + puts $file(outc) [lindex $l 0] + puts $file(outc) \} + set ok 1 + set fdef "$inf(cprefix)int $inf(fprefix)$name (ODR o, $inf(vprefix)$name **p, int opt, const char *name);" + switch -- $t { + Simple { + set decl "typedef [lindex $l 1] $inf(vprefix)$name;" + if {![string compare [lindex $tname 2] 1]} { + if {![info exist inf(defined,[lindex $tname 0])]} { + set ok 0 + } + } + set inf(var,$inf(nodef)) [join [lindex $l 2] \n] + incr inf(nodef) + } + default { + set decl "typedef struct $inf(vprefix)$name $inf(vprefix)$name;" + set inf(var,$inf(nodef)) "[lindex $l 1];" + incr inf(nodef) + } + } + if {$ok} { + puts $file(outh) {} + puts $file(outh) $decl + puts $file(outh) $fdef + asnForwardTypes $name + } else { + lappend inf(forward,code,[lindex $tname 0]) {} $decl $fdef + lappend inf(forward,ref,[lindex $tname 0]) $name + } +} + +proc asnForwardTypes {name} { + global inf file + + if {![info exists inf(forward,code,$inf(fprefix)$name)]} { + return 0 + } + foreach r $inf(forward,code,$inf(fprefix)$name) { + puts $file(outh) $r + } + unset inf(forward,code,$inf(fprefix)$name) + + while {[info exists inf(forward,ref,$inf(fprefix)$name)]} { + set n $inf(forward,ref,$inf(fprefix)$name) + set m [lrange $n 1 end] + if {[llength $m]} { + set inf(forward,ref,$inf(fprefix)$name) $m + } else { + unset inf(forward,ref,$inf(fprefix)$name) + } + asnForwardTypes [lindex $n 0] + } +} + +# asnSimple: parses simple type definition and generates C code +# On entry, +# $name is the name we are defining +# $tname is the tname as returned by asnType +# $tag is the tag as returned by asnMod +# $implicit is the implicit indicator as returned by asnMod +# Returns, +# {c-code, h-code} +# Note: Doesn't take care of enum lists yet. +proc asnSimple {name tname tag implicit tagtype} { + global inf + + set j "[lindex $tname 1] " + + if {[info exists inf(unionmap,$inf(module),$name)]} { + set uName $inf(unionmap,$inf(module),$name) + } else { + set uName $name + } + + asnEnum $uName jj + if {![string length $tag]} { + set l "\treturn [lindex $tname 0] (o, p, opt, name);" + } elseif {$implicit} { + set l \ + "\treturn odr_implicit_tag (o, [lindex $tname 0], p, $tagtype, $tag, opt, name);" + } else { + set l \ + "\treturn odr_explicit_tag (o, [lindex $tname 0], p, $tagtype, $tag, opt, name);" \ + } + if {[info exists jj]} { + return [list $l $j $jj] + } else { + return [list $l $j] + } +} + +# asnSequence: parses "SEQUENCE { s-list }" and generates C code. +# On entry, +# $name is the type we are defining +# $tag tag +# $implicit +# Returns, +# {c-code, h-code} +proc asnSequence {name tag implicit tagtype} { + global val type inf + + lappend j "struct $inf(vprefix)$name \{" + set level 0 + set nchoice 0 + if {![string length $tag]} { + lappend l "\tif (!odr_sequence_begin (o, p, sizeof(**p), name))" + lappend l "\t\treturn opt && odr_ok (o);" + } elseif {$implicit} { + lappend l "\tif (!odr_implicit_settag (o, $tagtype, $tag) ||" + lappend l "\t\t!odr_sequence_begin (o, p, sizeof(**p), name))" + lappend l "\t\treturn opt && odr_ok(o);" + } else { + lappend l "\tif (!odr_constructed_begin (o, p, $tagtype, $tag, name))" + lappend l "\t\treturn opt && odr_ok(o);" + lappend l "\tif (o->direction == ODR_DECODE)" + lappend l "\t\t*p = odr_malloc (o, sizeof(**p));" + + lappend l "\tif (!odr_sequence_begin (o, p, sizeof(**p), 0))" + lappend l "\t\{" + lappend l "\t\t*p = 0;" + lappend l "\t\treturn 0;" + lappend l "\t\}" + } + lappend l "\treturn" + while {1} { + set p [lindex [asnName $name] 0] + asnMod ltag limplicit ltagtype + set t [asnType $p] + + set uName { } + if {[info exists inf(unionmap,$inf(module),$name,$p)]} { + set uName $inf(unionmap,$inf(module),$name,$p) + } + + if {![string compare $t Simple]} { + if {[string compare $uName { }]} { + set enumName $uName + } else { + set enumName $name + } + asnEnum $enumName j + set opt [asnOptional] + if {![string length $ltag]} { + lappend l "\t\t[lindex $tname 0](o, &(*p)->$p, $opt, \"$p\") &&" + } elseif {$limplicit} { + lappend l "\t\todr_implicit_tag (o, [lindex $tname 0]," + lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&" + } else { + lappend l "\t\todr_explicit_tag (o, [lindex $tname 0]," + lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&" + } + set dec "\t[lindex $tname 1] *$p;" + } elseif {![string compare $t SequenceOf] && [string length $uName] &&\ + (![string length $ltag] || $limplicit)} { + set u [asnType $p] + + if {[llength $uName] < 2} { + set uName [list num_$p $p] + } + if {[string length $ltag]} { + if {!$limplicit} { + asnError explicittag + } + lappend l "\t\todr_implicit_settag (o, $ltagtype, $ltag) &&" + } + switch -- $u { + Simple { + asnEnum $name j + set tmpa "odr_sequence_of(o, (Odr_fun) [lindex $tname 0], &(*p)->$p," + set tmpb "&(*p)->[lindex $uName 0], \"$p\")" + lappend j "\tint [lindex $uName 0];" + set dec "\t[lindex $tname 1] **[lindex $uName 1];" + } + default { + set subName [mapName ${name}_$level] + asnSub $subName $u {} {} 0 {} + + set tmpa "odr_sequence_of(o, (Odr_fun) $inf(fprefix)$subName, &(*p)->$p," + set tmpb "&(*p)->[lindex $uName 0], \"$p\")" + lappend j "\tint [lindex $uName 0];" + set dec "\t$inf(vprefix)$subName **[lindex $uName 1];" + incr level + } + } + set opt [asnOptional] + if {$opt} { + lappend l "\t\t($tmpa" + lappend l "\t\t $tmpb || odr_ok(o)) &&" + } else { + lappend l "\t\t$tmpa" + lappend l "\t\t $tmpb &&" + } + } elseif {!$nchoice && ![string compare $t Choice] && \ + [string length $uName]} { + if {[llength $uName] < 3} { + set uName [list which u $name] + incr nchoice + } + lappend j "\tint [lindex $uName 0];" + lappend j "\tunion \{" + lappend v "\tstatic Odr_arm arm\[\] = \{" + asnArm $name [lindex $uName 2] v j + lappend v "\t\};" + set dec "\t\} [lindex $uName 1];" + set opt [asnOptional] + set oa {} + set ob {} + if {[string length $ltag]} { + if {$limplicit} { + lappend l "\t\todr_implicit_settag (o, $ltagtype, $ltag) &&" + if {$opt} { + asnWarning "optional handling missing in CHOICE in SEQUENCE" + asnWarning " set unionmap($inf(module),$name,$p) to {}" + } + } else { + if {$opt} { + set la "((" + } else { + set la "" + } + lappend l "\t\t${la}odr_constructed_begin (o, &(*p)->[lindex $uName 1], $ltagtype, $ltag, \"$p\") &&" + } + } else { + if {$opt} { + set oa "(" + set ob " || odr_ok(o))" + } + } + lappend l "\t\t${oa}odr_choice (o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], 0)${ob} &&" + if {[string length $ltag]} { + if {!$limplicit} { + if {$opt} { + set lb ") || odr_ok(o))" + } else { + set lb "" + } + lappend l "\t\todr_constructed_end (o)${lb} &&" + } + } + } else { + set subName [mapName ${name}_$level] + asnSub $subName $t {} {} 0 {} + set opt [asnOptional] + if {![string length $ltag]} { + lappend l "\t\t$inf(fprefix)${subName} (o, &(*p)->$p, $opt, \"$p\") &&" + } elseif {$limplicit} { + lappend l "\t\todr_implicit_tag (o, $inf(fprefix)${subName}," + lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&" + } else { + lappend l "\t\todr_explicit_tag (o, $inf(fprefix)${subName}," + lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&" + } + set dec "\t$inf(vprefix)${subName} *$p;" + incr level + } + if {$opt} { + lappend j "$dec /* OPT */" + } else { + lappend j $dec + } + if {[string compare $type ,]} break + } + lappend j "\}" + if {[string length $tag] && !$implicit} { + lappend l "\t\todr_sequence_end (o) &&" + lappend l "\t\todr_constructed_end (o);" + } else { + lappend l "\t\todr_sequence_end (o);" + } + if {[string compare $type \}]} { + asnError "Missing \} got $type '$val'" + } + lex + if {[info exists v]} { + set l [concat $v $l] + } + return [list [join $l \n] [join $j \n]] +} + +# asnOf: parses "SEQUENCE/SET OF type" and generates C code. +# On entry, +# $name is the type we are defining +# $tag tag +# $implicit +# Returns, +# {c-code, h-code} +proc asnOf {name tag implicit tagtype isset} { + global inf + + if {$isset} { + set func odr_set_of + } else { + set func odr_sequence_of + } + + if {[info exists inf(unionmap,$inf(module),$name)]} { + set numName $inf(unionmap,$inf(module),$name) + } else { + set numName {num elements} + } + + lappend j "struct $inf(vprefix)$name \{" + lappend j "\tint [lindex $numName 0];" + + lappend l "\tif (!odr_initmember (o, p, sizeof(**p)))" + lappend l "\t\treturn opt && odr_ok(o);" + if {[string length $tag]} { + if {$implicit} { + lappend l "\todr_implicit_settag (o, $tagtype, $tag);" + } else { + asnWarning "Constructed SEQUENCE/SET OF not handled" + } + } + set t [asnType $name] + switch -- $t { + Simple { + asnEnum $name j + lappend l "\tif ($func (o, (Odr_fun) [lindex $tname 0], &(*p)->[lindex $numName 1]," + lappend l "\t\t&(*p)->[lindex $numName 0], name))" + lappend j "\t[lindex $tname 1] **[lindex $numName 1];" + } + default { + set subName [mapName ${name}_s] + lappend l "\tif ($func (o, (Odr_fun) $inf(fprefix)$subName, &(*p)->[lindex $numName 1]," + lappend l "\t\t&(*p)->[lindex $numName 0], name))" + lappend j "\t$inf(vprefix)$subName **[lindex $numName 1];" + asnSub $subName $t {} {} 0 {} + } + } + lappend j "\}" + lappend l "\t\treturn 1;" + lappend l "\t*p = 0;" + lappend l "\treturn opt && odr_ok(o);" + return [list [join $l \n] [join $j \n]] +} + +# asnArm: parses c-list in choice +proc asnArm {name defname lx jx} { + global type val inf + + upvar $lx l + upvar $jx j + while {1} { + set pq [asnName $name] + set p [lindex $pq 0] + set q [lindex $pq 1] + if {![string length $q]} { + set q $p + set p ${defname}_$p + } + asnMod ltag limplicit ltagtype + set t [asnType $q] + + lappend enums "$inf(dprefix)$p" + if {![string compare $t Simple]} { + asnEnum $name j + if {![string length $ltag]} { + lappend l "\t\t\{-1, -1, -1, $inf(dprefix)$p," + lappend l "\t\t (Odr_fun) [lindex $tname 0], \"$q\"\}," + } elseif {$limplicit} { + lappend l "\t\t\{ODR_IMPLICIT, $ltagtype, $ltag, $inf(dprefix)$p," + lappend l "\t\t(Odr_fun) [lindex $tname 0], \"$q\"\}," + } else { + lappend l "\t\t\{ODR_EXPLICIT, $ltagtype, $ltag, $inf(dprefix)$p," + lappend l "\t\t(Odr_fun) [lindex $tname 0], \"$q\"\}," + } + lappend j "\t\t[lindex $tname 1] *$q;" + } else { + set subName [mapName ${name}_$q] + if {![string compare $inf(dprefix)${name}_$q \ + $inf(vprefix)$subName]} { + set po [string toupper [string index $q 0]][string \ + range $q 1 end] + set subName [mapName ${name}${po}] + } + asnSub $subName $t $tname {} 0 {} + if {![string length $ltag]} { + lappend l "\t\t\{-1, -1, -1, $inf(dprefix)$p," + lappend l "\t\t (Odr_fun) $inf(fprefix)$subName, \"$q\"\}," + } elseif {$limplicit} { + lappend l "\t\t\{ODR_IMPLICIT, $ltagtype, $ltag, $inf(dprefix)$p," + lappend l "\t\t(Odr_fun) $inf(fprefix)$subName, \"$q\"\}," + } else { + lappend l "\t\t\{ODR_EXPLICIT, $ltagtype, $ltag, $inf(dprefix)$p," + lappend l "\t\t(Odr_fun) $inf(fprefix)$subName, \"$q\"\}," + } + lappend j "\t\t$inf(vprefix)$subName *$q;" + } + if {[string compare $type ,]} break + } + if {[string compare $type \}]} { + asnError "Missing \} got $type '$val'" + } + lex + set level 1 + foreach e $enums { + lappend j "#define $e $level" + incr level + } + lappend l "\t\t\{-1, -1, -1, -1, (Odr_fun) 0, 0\}" +} + +# asnChoice: parses "CHOICE {c-list}" and generates C code. +# On entry, +# $name is the type we are defining +# $tag tag +# $implicit +# Returns, +# {c-code, h-code} +proc asnChoice {name tag implicit tagtype} { + global type val inf + + if {[info exists inf(unionmap,$inf(module),$name)]} { + set uName $inf(unionmap,$inf(module),$name) + } else { + set uName [list which u $name] + } + + lappend j "struct $inf(vprefix)$name \{" + lappend j "\tint [lindex $uName 0];" + lappend j "\tunion \{" + lappend l "\tstatic Odr_arm arm\[\] = \{" + asnArm $name [lindex $uName 2] l j + lappend j "\t\} [lindex $uName 1];" + lappend j "\}" + lappend l "\t\};" + if {![string length $tag]} { + lappend l "\tif (!odr_initmember(o, p, sizeof(**p)))" + lappend l "\t\treturn opt && odr_ok(o);" + lappend l "\tif (odr_choice(o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], name))" + } elseif {$implicit} { + lappend l "\tif (!odr_initmember(o, p, sizeof(**p)))" + lappend l "\t\treturn opt && odr_ok(o);" + lappend l "\todr_implicit_settag(o, $tagtype, $tag);" + lappend l "\tif (odr_choice(o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], name))" + } else { + lappend l "\tif (!*p && o->direction != ODR_DECODE)" + lappend l "\t\treturn opt;" + lappend l "\tif (!odr_constructed_begin(o, p, $tagtype, $tag, 0))" + lappend l "\t\treturn opt && odr_ok(o);" + lappend l "\tif (!odr_initmember(o, p, sizeof(**p)))" + lappend l "\t\treturn opt && odr_ok(o);" + lappend l "\tif (odr_choice(o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], name) &&" + lappend l "\t\todr_constructed_end(o))" + } + lappend l "\t\treturn 1;" + lappend l "\t*p = 0;" + lappend l "\treturn opt && odr_ok(o);" + return [list [join $l \n] [join $j \n]] +} + +# asnImports: parses i-list in "IMPORTS {i-list}" +# On return inf(import,..)-array is updated. +# inf(import,"module") is a list of {C-handler, C-type} elements. +# The {C-handler, C-type} is compatible with the $tname as is used by the +# asnType procedure to solve external references. +proc asnImports {} { + global type val inf file + + while {1} { + if {[string compare $type n]} { + asnError "Missing name in IMPORTS list" + } + lappend nam $val + lex + if {![string compare $type n] && ![string compare $val FROM]} { + lex + + if {[info exists inf(filename,$val)]} { + set fname $inf(filename,$val) + } else { + set fname $val + } + puts $file(outh) "\#include <$inf(h-dir)${fname}.h>" + + if {[info exists inf(prefix,$val)]} { + set prefix $inf(prefix,$val) + } else { + set prefix $inf(prefix) + } + foreach n $nam { + if {[info exists inf(map,$val,$n)]} { + set v $inf(map,$val,$n) + } else { + set v $n + } + set w [join [split $v -] _] + set inf(imports,$n) [list [lindex $prefix 0]$w \ + [lindex $prefix 1]$w] + } + unset nam + lex + if {[string compare $type n]} break + } elseif {![string compare $type ,]} { + lex + } else break + } + if {[string compare $type \;]} { + asnError "Missing ; after IMPORTS list - got $type '$val'" + } + lex +} + +# asnExports: parses e-list in "EXPORTS {e-list}" +# This function does nothing with elements in the list. +proc asnExports {} { + global type val inf + + while {1} { + if {[string compare $type n]} { + asnError "Missing name in EXPORTS list" + } + set inf(exports,$val) 1 + lex + if {[string compare $type ,]} break + lex + } + if {[string compare $type \;]} { + asnError "Missing ; after EXPORTS list - got $type ($val)" + } + lex +} + +# asnModuleBody: parses a module specification and generates C code. +# Exports lists, imports lists, and type definitions are handled; +# other things are silently ignored. +proc asnModuleBody {} { + global type val file inf + + if {[info exists inf(prefix,$inf(module))]} { + set prefix $inf(prefix,$inf(module)) + } else { + set prefix $inf(prefix) + } + set inf(fprefix) [lindex $prefix 0] + set inf(vprefix) [lindex $prefix 1] + set inf(dprefix) [lindex $prefix 2] + if {[llength $prefix] > 3} { + set inf(cprefix) [lindex $prefix 3] + } else { + set inf(cprefix) {YAZ_EXPORT } + } + + if {$inf(verbose)} { + puts "Module $inf(module), $inf(lineno)" + } + + set defblock 0 + if {[info exists inf(init,$inf(module),c)]} { + puts $file(outc) $inf(init,$inf(module),c) + } + if {[info exists inf(init,$inf(module),h)]} { + puts $file(outh) "\#ifdef __cplusplus" + puts $file(outh) "extern \"C\" \{" + puts $file(outh) "\#endif" + set defblock 1 + puts $file(outh) $inf(init,$inf(module),h) + } + if {[info exists inf(init,$inf(module),p)]} { + puts $file(outp) $inf(init,$inf(module),p) + } + + while {[string length $type]} { + if {[string compare $type n]} { + lex + continue + } + if {![string compare $val END]} { + break + } elseif {![string compare $val EXPORTS]} { + lex + asnExports + } elseif {![string compare $val IMPORTS]} { + if {$defblock} { + puts $file(outh) "\#ifdef __cplusplus" + puts $file(outh) "\}" + puts $file(outh) "\#endif" + set defblock 0 + } + lex + asnImports + } else { + if {!$defblock} { + puts $file(outh) "\#ifdef __cplusplus" + puts $file(outh) "extern \"C\" \{" + puts $file(outh) "\#endif" + set defblock 1 + } + set inf(asndef) $inf(nodef) + set oval $val + lex + if {![string compare $type :]} { + lex + asnDef $oval + set inf(asndef) 0 + } elseif {![string compare $type n]} { + lex + if {[string length $type]} { + lex + } + } + } + } + if {$defblock} { + puts $file(outh) "\#ifdef __cplusplus" + puts $file(outh) "\}" + puts $file(outh) "\#endif" + set defblock 0 + } + foreach x [array names inf imports,*] { + unset inf($x) + } +} + +# asnTagDefault: parses TagDefault section +proc asnTagDefault {} { + global type val inf file + + set inf(implicit-tags) 0 + while {[string length $type]} { + if {[lex-name-move EXPLICIT]} { + lex + set inf(implicit-tags) 0 + } elseif {[lex-name-move IMPLICIT]} { + lex + set inf(implicit-tags) 1 + } else { + break + } + } +} + +# asnModules: parses a collection of module specifications. +# Depending on the module pattern, $inf(moduleP), a module is either +# skipped or processed. +proc asnModules {} { + global type val inf file yc_version + + set inf(nodef) 0 + set inf(asndef) 0 + lex + while {![string compare $type n]} { + set inf(module) $val + if {[info exists inf(moduleP)] && ![string match $inf(moduleP) $val]} { + if {$inf(verbose)} { + puts "Skipping $id" + } + while {![lex-name-move END]} { + lex + } + } else { + set inf(nodef) 1 + set inf(asndef) 1 + + while {![lex-name-move DEFINITIONS]} { + lex + if {![string length $type]} return + } + if {[info exists inf(filename,$inf(module))]} { + set fname $inf(filename,$inf(module)) + } else { + set fname $inf(module) + } + set ppname [join [split $fname -] _] + + if {![info exists inf(c-file)]} { + set inf(c-file) ${fname}.c + } + set file(outc) [open $inf(c-file) w] + + if {![info exists inf(h-file)]} { + set inf(h-file) ${fname}.h + } + set file(outh) [open $inf(h-path)/$inf(h-dir)$inf(h-file) w] + + if {0} { + if {![info exists inf(p-file)]} { + set inf(p-file) ${fname}-p.h + } + set file(outp) [open $inf(h-path)/$inf(h-dir)$inf(p-file) w] + } + + set greeting {Generated automatically by the YAZ ASN.1 Compiler} + + puts $file(outc) "/* ${greeting} ${yc_version} */" + puts $file(outc) "/* Module-C: $inf(module) */" + puts $file(outc) {} + + puts $file(outh) "/* ${greeting} ${yc_version} */" + puts $file(outh) "/* Module-H $inf(module) */" + puts $file(outh) {} + + if {[info exists file(outp)]} { + puts $file(outp) "/* ${greeting} ${yc_version} */" + puts $file(outp) "/* Module-P: $inf(module) */" + puts $file(outp) {} + } + + if {[info exists inf(p-file)]} { + puts $file(outc) "\#include <$inf(h-dir)$inf(p-file)>" + } else { + puts $file(outc) "\#include <$inf(h-dir)$inf(h-file)>" + } + puts $file(outh) "\#ifndef ${ppname}_H" + puts $file(outh) "\#define ${ppname}_H" + puts $file(outh) {} + puts $file(outh) "\#include <$inf(h-dir)odr.h>" + + if {[info exists file(outp)]} { + puts $file(outp) "\#ifndef ${ppname}_P_H" + puts $file(outp) "\#define ${ppname}_P_H" + puts $file(outp) {} + puts $file(outp) "\#include <$inf(h-dir)$inf(h-file)>" + + } + + asnTagDefault + if {[string compare $type :]} { + asnError "::= expected got $type '$val'" + } + lex + if {![lex-name-move BEGIN]} { + asnError "BEGIN expected" + } + asnModuleBody + lex + + if {[info exists file(outp)]} { + set f $file(outp) + } else { + set f $file(outh) + } + puts $f "\#ifdef __cplusplus" + puts $f "extern \"C\" \{" + puts $f "\#endif" + for {set i 1} {$i < $inf(nodef)} {incr i} { + puts $f $inf(var,$i) + if {[info exists inf(asn,$i)]} { + if {0} { + puts $f "/*" + foreach comment $inf(asn,$i) { + puts $f $comment + } + puts $f " */" + } + unset inf(asn,$i) + } + unset inf(var,$i) + puts $f {} + } + puts $f "\#ifdef __cplusplus" + puts $f "\}" + puts $f "\#endif" + + if {[info exists inf(body,$inf(module),h)]} { + puts $file(outh) $inf(body,$inf(module),h) + } + if {[info exists inf(body,$inf(module),c)]} { + puts $file(outc) $inf(body,$inf(module),c) + } + if {[info exists inf(body,$inf(module),p)]} { + if {[info exists file(outp)]} { + puts $file(outp) $inf(body,$inf(module),p) + } + } + puts $file(outh) "\#endif" + if {[info exists file(outp)]} { + puts $file(outp) "\#endif" + } + foreach f [array names file] { + close $file($f) + } + unset inf(c-file) + unset inf(h-file) + catch {unset inf(p-file)} + } + } +} + +# asnFile: parses an ASN.1 specification file as specified in $inf(iname). +proc asnFile {} { + global inf file + + if {$inf(verbose) > 1} { + puts "Reading ASN.1 file $inf(iname)" + } + set inf(str) {} + set inf(lineno) 0 + set inf(inf) [open $inf(iname) r] + + asnModules + +} + +# The following procedures are invoked by the asnType function. +# Each procedure takes the form: asnBasic and they must return +# two elements: the C function handler and the C type. +# On entry upvar $name is the type we are defining and global, $inf(module), is +# the current module name. + +proc asnBasicEXTERNAL {} { + return {odr_external {Odr_external}} +} + +proc asnBasicINTEGER {} { + return {odr_integer {int}} +} + +proc asnBasicENUMERATED {} { + return {odr_enum {int}} +} + +proc asnBasicNULL {} { + return {odr_null {Odr_null}} +} + +proc asnBasicBOOLEAN {} { + return {odr_bool {bool_t}} +} + +proc asnBasicOCTET {} { + global type val + lex-name-move STRING + return {odr_octetstring {Odr_oct}} +} + +proc asnBasicBIT {} { + global type val + lex-name-move STRING + return {odr_bitstring {Odr_bitmask}} +} + +proc asnBasicOBJECT {} { + global type val + lex-name-move IDENTIFIER + return {odr_oid {Odr_oid}} +} + +proc asnBasicGeneralString {} { + return {odr_generalstring char} +} + +proc asnBasicVisibleString {} { + return {odr_visiblestring char} +} + +proc asnBasicGeneralizedTime {} { + return {odr_generalizedtime char} +} + +proc asnBasicANY {} { + upvar name name + global inf + return [list $inf(fprefix)ANY_$name void] +} + +# userDef: reads user definitions file $name +proc userDef {name} { + global inf + + if {$inf(verbose) > 1} { + puts "Reading definitions file $name" + } + source $name + + if {[info exists default-prefix]} { + set inf(prefix) ${default-prefix} + } + if {[info exists h-path]} { + set inf(h-path) ${h-path} + } + foreach m [array names prefix] { + set inf(prefix,$m) $prefix($m) + } + foreach m [array names body] { + set inf(body,$m) $body($m) + } + foreach m [array names init] { + set inf(init,$m) $init($m) + } + foreach m [array names filename] { + set inf(filename,$m) $filename($m) + } + foreach m [array names map] { + set inf(map,$m) $map($m) + } + foreach m [array names membermap] { + set inf(membermap,$m) $membermap($m) + } + foreach m [array names unionmap] { + set inf(unionmap,$m) $unionmap($m) + } +} + +set inf(verbose) 0 +set inf(prefix) {yc_ Yc_ YC_} +set inf(h-path) . +set inf(h-dir) "" + +# Parse command line +set l [llength $argv] +set i 0 +while {$i < $l} { + set arg [lindex $argv $i] + switch -glob -- $arg { + -v { + incr inf(verbose) + } + -c { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + set inf(c-file) $p + } + -I* { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + set inf(h-path) $p + } + -i* { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + set inf(h-dir) [string trim $p \\/]/ + } + -h* { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + set inf(h-file) $p + } + -p* { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + set inf(p-file) $p + } + -d* { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + userDef $p + } + -m* { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + set inf(moduleP) $p + } + -x* { + set p [string range $arg 2 end] + if {![string length $p]} { + set p [lindex $argv [incr i]] + } + if {[llength $p] == 1} { + set inf(prefix) [list [string tolower $p] \ + [string toupper $p] [string toupper $p]] + } elseif {[llength $p] == 3} { + set inf(prefix) $p + } else { + puts [llength $p] + exit 1 + } + } + default { + set inf(iname) $arg + } + } + incr i +} + +if {![info exists inf(iname)]} { + puts "YAZ ASN.1 Compiler ${yc_version}" + puts -nonewline "Usage: ${argv0}" + puts { [-v] [-c cfile] [-h hfile] [-p hfile] [-d dfile] [-I path]} + puts { [-x prefix] [-m module] file} + exit 1 +} + +asnFile