Fixed a bug in ASN.1 compiler that caused Type-1 Operator codec to fail
[yaz-moved-to-github.git] / util / yaz-asncomp
1 #!/bin/sh
2 # the next line restarts using tclsh \
3 exec tclsh "$0" "$@"
4 #
5 # yaz-comp: ASN.1 Compiler for YAZ
6 # (c) Index Data 1996-2004
7 # See the file LICENSE for details.
8 #
9 # $Id: yaz-asncomp,v 1.3 2004-02-11 21:39:45 adam Exp $
10 #
11
12 set yc_version 0.3
13
14 # Syntax for the ASN.1 supported:
15 # file   -> file module
16 #         | module
17 # module -> name skip DEFINITIONS ::= mbody END
18 # mbody  -> EXPORTS { nlist }
19 #         | IMPORTS { imlist }
20 #         | name ::= tmt
21 #         | skip
22 # tmt    -> tag mod type
23 # type   -> SEQUENCE { sqlist }
24 #         | SEQUENCE OF type
25 #         | CHOICE { chlist }
26 #         | basic enlist
27 #
28 # basic  -> INTEGER
29 #         | BOOLEAN
30 #         | OCTET STRING
31 #         | BIT STRING
32 #         | EXTERNAL
33 #         | name
34 # sqlist -> sqlist , name tmt opt
35 #         | name tmt opt
36 # chlist -> chlist , name tmt 
37 #         | name tmt 
38 # enlist -> enlist , name (n)
39 #         | name (n)
40 # imlist -> nlist FROM name
41 #           imlist nlist FROM name
42 # nlist  -> name
43 #         | nlist , name
44 # mod   -> IMPLICIT | EXPLICIT | e
45 # tag   -> [tagtype n] | [n] | e
46 # opt   -> OPTIONAL | e
47 #
48 # name    identifier/token 
49 # e       epsilon/empty 
50 # skip    one token skipped
51 # n       number
52 # tagtype APPLICATION, CONTEXT, etc.
53
54 # lex: moves input file pointer and returns type of token.
55 # The globals $type and $val are set. $val holds name if token
56 # is normal identifier name.
57 # sets global var type to one of:
58 #     {}     eof-of-file
59 #     \{     left curly brace 
60 #     \}     right curly brace
61 #     ,      comma
62 #     ;      semicolon
63 #     (      (n)
64 #     [      [n]
65 #     :      ::=
66 #     n      other token n
67 proc lex {} {
68     global inf val type
69     while {![string length $inf(str)]} {
70         incr inf(lineno)
71         set inf(cnt) [gets $inf(inf) inf(str)]
72         if {$inf(cnt) < 0} {
73             set type {}
74             return {}
75         }
76         lappend inf(asn,$inf(asndef)) $inf(str)
77         set l [string first -- $inf(str)]
78         if {$l >= 0} {
79             incr l -1
80             set inf(str) [string range $inf(str) 0 $l]
81         }
82         set inf(str) [string trim $inf(str)]
83     }
84     set s [string index $inf(str) 0]
85     set type $s
86     set val {}
87     switch -- $s {
88         \{ { }
89         \} { }
90         ,  { }
91         ;  { }
92         \(  { }
93         \)  { }
94         \[ { regexp {^\[[ ]*(.+)[ ]*\]} $inf(str) s val }
95         :  { regexp {^::=} $inf(str) s }
96         default {
97              regexp "^\[^,\t :\{\}();\]+" $inf(str) s
98              set type n
99              set val $s
100            }
101     }
102     set off [string length $s]
103     set inf(str) [string trim [string range $inf(str) $off end]]
104     return $type
105 }
106
107 # lex-expect: move pointer and expect token $t
108 proc lex-expect {t} {
109     global type val
110     lex
111     if {[string compare $t $type]} {
112         asnError "Got $type '$val', expected $t"
113     }
114 }
115
116 # lex-name-move: see if token is $name; moves pointer and returns
117 # 1 if it is; returns 0 otherwise.
118 proc lex-name-move {name} {
119     global type val
120     if {![string compare $type n] && ![string compare $val $name]} {
121         lex
122         return 1
123     }
124     return 0
125 }
126
127 # asnError: Report error and die
128 proc asnError {msg} {
129     global inf
130    
131     puts "Error in line $inf(lineno) in module $inf(module)"
132     puts " $msg"
133     error
134     exit 1
135 }
136
137 # asnWarning: Report warning and return
138 proc asnWarning {msg} {
139     global inf
140    
141     puts "Warning in line $inf(lineno) in module $inf(module)"
142     puts " $msg"
143 }
144
145 # asnEnum: parses enumerated list - { name1 (n), name2 (n), ... }
146 # Uses $name as prefix. If there really is a list, $lx holds the C
147 # preprocessor definitions on return; otherwise lx isn't set.
148 proc asnEnum {name lx} {
149     global type val inf
150
151     if {[string compare $type \{]} return
152     upvar $lx l
153     while {1} {
154         set pq [asnName $name]
155         set id [lindex $pq 0]
156         set id ${name}_$id
157         lex-expect n
158         lappend l "#define $inf(dprefix)$id $val"
159         lex-expect ")"
160         lex
161         if {[string compare $type ,]} break
162     }
163     if {[string compare $type \}]} {
164         asnError "Missing \} in enum list got $type '$val'"
165     }
166     lex
167 }
168
169 # asnMod: parses tag and modifier.
170 # $xtag and $ximplicit holds tag and implicit-indication on return.
171 # $xtag is empty if no tag was specified. $ximplicit is 1 on implicit
172 # tagging; 0 otherwise.
173 proc asnMod {xtag ximplicit xtagtype} {
174     global type val inf
175
176     upvar $xtag tag
177     upvar $ximplicit implicit
178     upvar $xtagtype tagtype
179
180     set tag {} 
181     set tagtype {}
182     if {![string compare $type \[]} {
183         if {[regexp {^([a-zA-Z]+)[ ]+([0-9]+)$} $val x tagtype tag]} {
184             set tagtype ODR_$tagtype 
185         } elseif {[regexp {^([0-9]+)$} $val x tag]} {
186             set tagtype ODR_CONTEXT
187         } else {
188             asnError "bad tag specification: $val"
189         }
190         lex
191     }
192     set implicit $inf(implicit-tags)
193     if {![string compare $type n]} {
194         if {![string compare $val EXPLICIT]} {
195             lex
196             set implicit 0
197         } elseif {![string compare $val IMPLICIT]} {
198             lex
199             set implicit 1
200         }
201     }
202 }
203
204 # asnName: moves pointer and expects name. Returns C-validated name.
205 proc asnName {name} {
206     global val inf
207     lex-expect n
208     if {[info exists inf(membermap,$inf(module),$name,$val)]} {
209             set nval $inf(membermap,$inf(module),$name,$val)
210         if {$inf(verbose)} {
211             puts " mapping member $name,$val to $nval"
212         }
213         if {![string match {[A-Z]*} $val]} {
214             lex
215         }
216     } else {
217         set nval $val
218         if {![string match {[A-Z]*} $val]} {
219             lex
220         }
221     }
222     return [join [split $nval -] _]
223 }
224
225 # asnOptional: parses optional modifier. Returns 1 if OPTIONAL was 
226 # specified; 0 otherwise.
227 proc asnOptional {} {
228     global type val
229     if {[lex-name-move OPTIONAL]} {
230         return 1
231     } elseif {[lex-name-move DEFAULT]} {
232         lex
233         return 0
234     }
235     return 0
236 }
237
238 # asnSizeConstraint: parses the optional SizeConstraint.
239 # Currently not used for anything.
240 proc asnSizeConstraint {} {
241     global type val
242     if {[lex-name-move SIZE]} {
243         asnSubtypeSpec
244     }
245 }
246
247 # asnSubtypeSpec: parses the SubtypeSpec ...
248 # Currently not used for anything. We now it's balanced however, i.e.
249 # (... ( ... ) .. )
250 proc asnSubtypeSpec {} {
251     global type val
252
253     if {[string compare $type "("]} {
254         return 
255     }
256     lex
257     set level 1
258     while {$level > 0} {
259         if {![string compare $type "("]} {
260             incr level
261         } elseif {![string compare $type ")"]} {
262             incr level -1
263         }
264         lex
265     }
266 }
267
268 # asnType: parses ASN.1 type.
269 # On entry $name should hold the name we are currently defining.
270 # Returns type indicator:
271 #   SequenceOf     SEQUENCE OF
272 #   Sequence       SEQUENCE 
273 #   SetOf          SET OF
274 #   Set            SET
275 #   Choice         CHOICE
276 #   Simple         Basic types.
277 #   In this casecalling procedure's $tname variable is a list holding:
278 #        {C-Function C-Type} if the type is IMPORTed or ODR defined.
279 #      or
280 #        {C-Function C-Type 1} if the type should be defined in this module
281 proc asnType {name} {
282     global type val inf
283     upvar tname tname
284
285     set tname {}
286     if {[string compare $type n]} {
287         asnError "Expects type specifier, but got $type"
288     }
289     set v $val
290     lex
291     switch -- $v {
292         SEQUENCE {
293             asnSizeConstraint
294             if {[lex-name-move OF]} {
295                 asnSubtypeSpec
296                 return SequenceOf
297             } else {
298                 asnSubtypeSpec
299                 return Sequence
300             }
301         }
302         SET {
303             asnSizeConstraint
304             if {[lex-name-move OF]} {
305                 asnSubtypeSpec
306                 return SetOf
307             } else {
308                 asnSubtypeSpec
309                 return Set
310             }
311         }
312         CHOICE {
313             asnSubtypeSpec
314             return Choice
315         }
316     }
317     if {[string length [info commands asnBasic$v]]} {
318         set tname [asnBasic$v]
319     } else {
320         if {[info exists inf(map,$inf(module),$v)]} {
321             set v $inf(map,$inf(module),$v)
322         }
323         if {[info exists inf(imports,$v)]} {
324             set tname $inf(imports,$v)
325         } else {
326             set w [join [split $v -] _]
327             set tname [list $inf(fprefix)$w $inf(vprefix)$w 1]
328         }
329     }
330     if {[lex-name-move DEFINED]} {
331         if {[lex-name-move BY]} {
332             lex
333         }
334     }
335     asnSubtypeSpec
336     return Simple
337 }
338
339 proc mapName {name} {
340     global inf
341     if {[info exists inf(map,$inf(module),$name)]} {
342         set name $inf(map,$inf(module),$name)
343         if {$inf(verbose)} {
344             puts -nonewline " $name ($inf(lineno))"
345             puts " mapping to $name"
346         }
347     } else {
348         if {$inf(verbose)} {
349             puts " $name ($inf(lineno))"
350         }
351     }
352     return $name
353 }
354
355 # asnDef: parses type definition (top-level) and generates C code
356 # On entry $name holds the type we are defining.
357 proc asnDef {name} {
358     global inf file
359
360     set name [mapName $name]
361     if {[info exist inf(defined,$inf(fprefix)$name)]} {
362         incr inf(definedl,$name)
363         if {$inf(verbose) > 1} {
364             puts "set map($inf(module),$name) $name$inf(definedl,$name)"
365         }
366     } else {
367         set inf(definedl,$name) 0
368     }
369     set mname [join [split $name -] _]
370     asnMod tag implicit tagtype
371     set t [asnType $mname]
372     asnSub $mname $t $tname $tag $implicit $tagtype
373 }
374
375
376 # asnSub: parses type and generates C-code
377 # On entry,
378 #   $name holds the type we are defining.
379 #   $t is the type returned by the asnType procedure.
380 #   $tname is the $tname set by the asnType procedure.
381 #   $tag is the tag as returned by asnMod
382 #   $implicit is the implicit indicator as returned by asnMod
383 proc asnSub {name t tname tag implicit tagtype} {
384     global file inf
385    
386     set ignore 0
387     set defname defined,$inf(fprefix)$name
388     if {[info exist inf($defname)]} {
389         asnWarning "$name already defined in line $inf($defname)"
390         set ignore 1
391     }
392     set inf($defname) $inf(lineno)
393     switch -- $t {
394         Sequence   { set l [asnSequence $name $tag $implicit $tagtype] }
395         SequenceOf { set l [asnOf $name $tag $implicit $tagtype 0] }
396         SetOf      { set l [asnOf $name $tag $implicit $tagtype 1] }
397         Choice     { set l [asnChoice $name $tag $implicit $tagtype] }
398         Simple     { set l [asnSimple $name $tname $tag $implicit $tagtype] }
399         default    { asnError "switch asnType case not handled" }
400     }
401     if {$ignore} return
402
403     puts $file(outc) {}
404     puts $file(outc) "int $inf(fprefix)$name (ODR o, $inf(vprefix)$name **p, int opt, const char *name)"
405     puts $file(outc) \{
406     puts $file(outc) [lindex $l 0]
407     puts $file(outc) \}
408     set ok 1
409     set fdef "$inf(cprefix)int $inf(fprefix)$name (ODR o, $inf(vprefix)$name **p, int opt, const char *name);"
410     switch -- $t {
411         Simple {
412             set decl "typedef [lindex $l 1] $inf(vprefix)$name;"
413             if {![string compare [lindex $tname 2] 1]} {
414                 if {![info exist inf(defined,[lindex $tname 0])]} {
415                     set ok 0
416                 }
417             }
418             set inf(var,$inf(nodef)) [join [lindex $l 2] \n]
419             incr inf(nodef)
420         }
421         default {
422             set decl "typedef struct $inf(vprefix)$name $inf(vprefix)$name;"
423             set inf(var,$inf(nodef)) "[lindex $l 1];"
424             incr inf(nodef)
425         }
426     }
427     if {$ok} {
428         puts $file(outh) {}
429         puts $file(outh) $decl
430         puts $file(outh) $fdef
431         asnForwardTypes $name
432     } else {
433         lappend inf(forward,code,[lindex $tname 0]) {} $decl $fdef
434         lappend inf(forward,ref,[lindex $tname 0]) $name
435     }
436 }
437
438 proc asnForwardTypes {name} {
439     global inf file
440
441     if {![info exists inf(forward,code,$inf(fprefix)$name)]} {
442         return 0
443     }
444     foreach r $inf(forward,code,$inf(fprefix)$name) {
445         puts $file(outh) $r
446     }
447     unset inf(forward,code,$inf(fprefix)$name)
448
449     while {[info exists inf(forward,ref,$inf(fprefix)$name)]} {
450         set n $inf(forward,ref,$inf(fprefix)$name)
451         set m [lrange $n 1 end]
452         if {[llength $m]} {
453             set inf(forward,ref,$inf(fprefix)$name) $m
454         } else {
455             unset inf(forward,ref,$inf(fprefix)$name)
456         }
457         asnForwardTypes [lindex $n 0]
458     }
459 }
460
461 # asnSimple: parses simple type definition and generates C code
462 # On entry,
463 #   $name is the name we are defining
464 #   $tname is the tname as returned by asnType
465 #   $tag is the tag as returned by asnMod
466 #   $implicit is the implicit indicator as returned by asnMod
467 # Returns,
468 #   {c-code, h-code}
469 # Note: Doesn't take care of enum lists yet.
470 proc asnSimple {name tname tag implicit tagtype} {
471     global inf
472
473     set j "[lindex $tname 1] "
474
475     if {[info exists inf(unionmap,$inf(module),$name)]} {
476         set uName $inf(unionmap,$inf(module),$name)
477     } else {
478         set uName $name
479     }
480
481     asnEnum $uName jj
482     if {![string length $tag]} {
483         set l "\treturn [lindex $tname 0] (o, p, opt, name);" 
484     } elseif {$implicit} {
485         set l \
486   "\treturn odr_implicit_tag (o, [lindex $tname 0], p, $tagtype, $tag, opt, name);" 
487     } else {
488         set l \
489   "\treturn odr_explicit_tag (o, [lindex $tname 0], p, $tagtype, $tag, opt, name);" \
490     }
491     if {[info exists jj]} {
492         return [list $l $j $jj]
493     } else {
494         return [list $l $j]
495     }
496 }
497
498 # asnSequence: parses "SEQUENCE { s-list }" and generates C code.
499 # On entry,
500 #   $name is the type we are defining
501 #   $tag tag 
502 #   $implicit
503 # Returns,
504 #   {c-code, h-code}
505 proc asnSequence {name tag implicit tagtype} {
506     global val type inf
507
508     lappend j "struct $inf(vprefix)$name \{"
509     set level 0
510     set nchoice 0
511     if {![string length $tag]} {
512         lappend l "\tif (!odr_sequence_begin (o, p, sizeof(**p), name))"
513         lappend l "\t\treturn odr_missing(o, opt, name) && odr_ok (o);"
514     } elseif {$implicit} {
515         lappend l "\tif (!odr_implicit_settag (o, $tagtype, $tag) ||"
516         lappend l "\t\t!odr_sequence_begin (o, p, sizeof(**p), name))"
517         lappend l "\t\treturn odr_missing(o, opt, name);"
518     } else {
519         lappend l "\tif (!odr_constructed_begin (o, p, $tagtype, $tag, name))"
520         lappend l "\t\treturn odr_missing(o, opt, name);"
521         lappend l "\tif (o->direction == ODR_DECODE)"
522         lappend l "\t\t*p = ($inf(vprefix)$name *) odr_malloc (o, sizeof(**p));"
523
524         lappend l "\tif (!odr_sequence_begin (o, p, sizeof(**p), 0))"
525         lappend l "\t\{"
526         lappend l "\t\tif(o->direction == ODR_DECODE)"
527         lappend l "\t\t\t*p = 0;"
528         lappend l "\t\treturn 0;"
529         lappend l "\t\}"
530     }
531     lappend l "\treturn"
532     while {1} {
533         set p [lindex [asnName $name] 0]
534         asnMod ltag limplicit ltagtype
535         set t [asnType $p]
536
537         set uName { }
538         if {[info exists inf(unionmap,$inf(module),$name,$p)]} {
539             set uName $inf(unionmap,$inf(module),$name,$p)
540         }
541
542         if {![string compare $t Simple]} {
543             if {[string compare $uName { }]} {
544                 set enumName $uName
545             } else {
546                 set enumName $name
547             }
548             asnEnum $enumName j
549             set opt [asnOptional]
550             if {![string length $ltag]} {
551                 lappend l "\t\t[lindex $tname 0](o, &(*p)->$p, $opt, \"$p\") &&"
552             } elseif {$limplicit} {
553                 lappend l "\t\todr_implicit_tag (o, [lindex $tname 0],"
554                 lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&"
555             } else {
556                 lappend l "\t\todr_explicit_tag (o, [lindex $tname 0],"
557                 lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&"
558             }
559             set dec "\t[lindex $tname 1] *$p;"
560         } elseif {![string compare $t SequenceOf] && [string length $uName] &&\
561                       (![string length $ltag] || $limplicit)} {
562             set u [asnType $p]
563            
564             if {[llength $uName] < 2} {
565                 set uName [list num_$p $p]
566             }
567             if {[string length $ltag]} {
568                 if {!$limplicit} {
569                     asnError explicittag
570                 }
571                 lappend l "\t\todr_implicit_settag (o, $ltagtype, $ltag) &&"
572             }
573             switch -- $u {
574                 Simple {
575                     asnEnum $name j
576                     set tmpa "odr_sequence_of(o, (Odr_fun) [lindex $tname 0], &(*p)->$p,"
577                     set tmpb "&(*p)->[lindex $uName 0], \"$p\")"
578                     lappend j "\tint [lindex $uName 0];"
579                     set dec "\t[lindex $tname 1] **[lindex $uName 1];"
580                 }
581                 default {
582                     set subName [mapName ${name}_$level]
583                     asnSub $subName $u {} {} 0 {}
584                     
585                     set tmpa "odr_sequence_of(o, (Odr_fun) $inf(fprefix)$subName, &(*p)->$p,"
586                     set tmpb "&(*p)->[lindex $uName 0], \"$p\")"
587                     lappend j "\tint [lindex $uName 0];"
588                     set dec "\t$inf(vprefix)$subName **[lindex $uName 1];"
589                     incr level
590                 }
591             }
592             set opt [asnOptional]
593             if {$opt} {
594                 lappend l "\t\t($tmpa"
595                 lappend l "\t\t  $tmpb || odr_ok(o)) &&"
596             } else {
597                 lappend l "\t\t$tmpa"
598                 lappend l "\t\t  $tmpb &&"
599             }
600         } elseif {!$nchoice && ![string compare $t Choice] && \
601                       [string length $uName]} {
602             if {[llength $uName] < 3} {
603                 set uName [list which u $name]
604                 incr nchoice
605             }
606             lappend j "\tint [lindex $uName 0];"
607             lappend j "\tunion \{"
608             lappend v "\tstatic Odr_arm arm\[\] = \{"
609             asnArm $name [lindex $uName 2] v j
610             lappend v "\t\};"
611             set dec "\t\} [lindex $uName 1];"
612             set opt [asnOptional]
613             set oa {}
614             set ob {}
615             if {[string length $ltag]} {
616                 if {$limplicit} {
617                     lappend l "\t\todr_implicit_settag (o, $ltagtype, $ltag) &&"
618                     if {$opt} {
619                         asnWarning "optional handling missing in CHOICE in SEQUENCE"
620                         asnWarning " set unionmap($inf(module),$name,$p) to {}"
621                     }
622                 } else {
623                     if {$opt} {
624                         set la "(("
625                     } else {
626                         set la ""
627                     }
628                     lappend l "\t\t${la}odr_constructed_begin (o, &(*p)->[lindex $uName 1], $ltagtype, $ltag, \"$p\") &&"
629                 }
630             } else {
631                 if {$opt} {
632                     set oa "("
633                     set ob " || odr_ok(o))" 
634                 }
635             }
636             lappend l "\t\t${oa}odr_choice (o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], 0)${ob} &&"
637             if {[string length $ltag]} {
638                 if {!$limplicit} {
639                     if {$opt} {
640                         set lb ") || odr_ok(o))"
641                     } else {
642                         set lb ""
643                     }
644                     lappend l "\t\todr_constructed_end (o)${lb} &&"
645                 } 
646             }
647         } else {
648             set subName [mapName ${name}_$level]
649             asnSub $subName $t {} {} 0 {}
650             set opt [asnOptional]
651             if {![string length $ltag]} {
652                 lappend l "\t\t$inf(fprefix)${subName} (o, &(*p)->$p, $opt, \"$p\") &&"
653             } elseif {$limplicit} {
654                 lappend l "\t\todr_implicit_tag (o, $inf(fprefix)${subName},"
655                 lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&"
656             } else {
657                 lappend l "\t\todr_explicit_tag (o, $inf(fprefix)${subName},"
658                 lappend l "\t\t\t&(*p)->$p, $ltagtype, $ltag, $opt, \"$p\") &&"
659             }
660             set dec "\t$inf(vprefix)${subName} *$p;"
661             incr level
662         }
663         if {$opt} {
664             lappend j "$dec /* OPT */"
665         } else {
666             lappend j $dec
667         }
668         if {[string compare $type ,]} break
669     }
670     lappend j "\}"
671     if {[string length $tag] && !$implicit} {
672         lappend l "\t\todr_sequence_end (o) &&"
673         lappend l "\t\todr_constructed_end (o);"
674     } else {
675         lappend l "\t\todr_sequence_end (o);"
676     }
677     if {[string compare $type \}]} {
678         asnError "Missing \} got $type '$val'"
679     }
680     lex
681     if {[info exists v]} {
682         set l [concat $v $l]
683     }
684     return [list [join $l \n] [join $j \n]]
685 }
686
687 # asnOf: parses "SEQUENCE/SET OF type" and generates C code.
688 # On entry,
689 #   $name is the type we are defining
690 #   $tag tag 
691 #   $implicit
692 # Returns,
693 #   {c-code, h-code}
694 proc asnOf {name tag implicit tagtype isset} { 
695     global inf
696
697     if {$isset} {
698         set func odr_set_of
699     } else {
700         set func odr_sequence_of
701     }
702
703     if {[info exists inf(unionmap,$inf(module),$name)]} {
704         set numName $inf(unionmap,$inf(module),$name)
705     } else {
706         set numName {num elements}
707     }
708
709     lappend j "struct $inf(vprefix)$name \{"
710     lappend j "\tint [lindex $numName 0];"
711
712     lappend l "\tif (!odr_initmember (o, p, sizeof(**p)))"
713     lappend l "\t\treturn odr_missing(o, opt, name);"
714     if {[string length $tag]} {
715         if {$implicit} {
716             lappend l "\todr_implicit_settag (o, $tagtype, $tag);"
717         } else {
718             asnWarning "Constructed SEQUENCE/SET OF not handled"
719         }
720     }
721     set t [asnType $name]
722     switch -- $t {
723         Simple {
724             asnEnum $name j
725             lappend l "\tif ($func (o, (Odr_fun) [lindex $tname 0], &(*p)->[lindex $numName 1],"
726             lappend l "\t\t&(*p)->[lindex $numName 0], name))"
727             lappend j "\t[lindex $tname 1] **[lindex $numName 1];"
728         }
729         default {
730             set subName [mapName ${name}_s]
731             lappend l "\tif ($func (o, (Odr_fun) $inf(fprefix)$subName, &(*p)->[lindex $numName 1],"
732             lappend l "\t\t&(*p)->[lindex $numName 0], name))"
733             lappend j "\t$inf(vprefix)$subName **[lindex $numName 1];"
734             asnSub $subName $t {} {} 0 {}
735         }
736     }
737     lappend j "\}"
738     lappend l "\t\treturn 1;"
739     lappend l "\tif(o->direction == ODR_DECODE)"
740     lappend l "\t\t*p = 0;"
741     lappend l "\treturn odr_missing(o, opt, name);"
742     return [list [join $l \n] [join $j \n]]
743 }
744
745 # asnArm: parses c-list in choice
746 proc asnArm {name defname lx jx} {
747     global type val inf
748
749     upvar $lx l
750     upvar $jx j
751     while {1} {
752         set pq [asnName $name]
753         set p [lindex $pq 0]
754         set q [lindex $pq 1]
755         if {![string length $q]} {
756             set q $p
757             set p ${defname}_$p
758         }
759         asnMod ltag limplicit ltagtype
760         set t [asnType $q]
761
762         lappend enums "$inf(dprefix)$p"
763         if {![string compare $t Simple]} {
764             asnEnum $name j
765             if {![string length $ltag]} {
766                 lappend l "\t\t\{-1, -1, -1, $inf(dprefix)$p,"
767                 lappend l "\t\t (Odr_fun) [lindex $tname 0], \"$q\"\},"
768             } elseif {$limplicit} {
769                 lappend l "\t\t\{ODR_IMPLICIT, $ltagtype, $ltag, $inf(dprefix)$p,"
770                 lappend l "\t\t(Odr_fun) [lindex $tname 0], \"$q\"\},"
771             } else {
772                 lappend l "\t\t\{ODR_EXPLICIT, $ltagtype, $ltag, $inf(dprefix)$p,"
773                 lappend l "\t\t(Odr_fun) [lindex $tname 0], \"$q\"\},"
774             }
775             lappend j "\t\t[lindex $tname 1] *$q;"
776         } else {
777             set subName [mapName ${name}_$q]
778             if {![string compare $inf(dprefix)${name}_$q \
779                                  $inf(vprefix)$subName]} {
780                 set po [string toupper [string index $q 0]][string \
781                                                             range $q 1 end]
782                 set subName [mapName ${name}${po}]
783             }
784             asnSub $subName $t $tname {} 0 {}
785             if {![string length $ltag]} {
786                 lappend l "\t\t\{-1, -1, -1, $inf(dprefix)$p,"
787                 lappend l "\t\t (Odr_fun) $inf(fprefix)$subName, \"$q\"\},"
788             } elseif {$limplicit} {
789                 lappend l "\t\t\{ODR_IMPLICIT, $ltagtype, $ltag, $inf(dprefix)$p,"
790                 lappend l "\t\t(Odr_fun) $inf(fprefix)$subName, \"$q\"\},"
791             } else {
792                 lappend l "\t\t\{ODR_EXPLICIT, $ltagtype, $ltag, $inf(dprefix)$p,"
793                 lappend l "\t\t(Odr_fun) $inf(fprefix)$subName, \"$q\"\},"
794             }
795             lappend j "\t\t$inf(vprefix)$subName *$q;"
796         }
797         if {[string compare $type ,]} break
798     }
799     if {[string compare $type \}]} {
800         asnError "Missing \} got $type '$val'"
801     }
802     lex
803     set level 1
804     foreach e $enums {
805         lappend j "#define $e $level"
806         incr level
807     }
808     lappend l "\t\t\{-1, -1, -1, -1, (Odr_fun) 0, 0\}"
809 }
810
811 # asnChoice: parses "CHOICE {c-list}" and generates C code.
812 # On entry,
813 #   $name is the type we are defining
814 #   $tag tag 
815 #   $implicit
816 # Returns,
817 #   {c-code, h-code}
818 proc asnChoice {name tag implicit tagtype} {
819     global type val inf
820
821     if {[info exists inf(unionmap,$inf(module),$name)]} {
822         set uName $inf(unionmap,$inf(module),$name)
823     } else {
824         set uName [list which u $name]
825     }
826
827     lappend j "struct $inf(vprefix)$name \{"
828     lappend j "\tint [lindex $uName 0];"
829     lappend j "\tunion \{"
830     lappend l "\tstatic Odr_arm arm\[\] = \{"
831     asnArm $name [lindex $uName 2] l j
832     lappend j "\t\} [lindex $uName 1];"
833     lappend j "\}"
834     lappend l "\t\};"
835     if {![string length $tag]} {
836         lappend l "\tif (!odr_initmember(o, p, sizeof(**p)))"
837         lappend l "\t\treturn odr_missing(o, opt, name);"
838         lappend l "\tif (odr_choice(o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], name))"
839     } elseif {$implicit} {
840         lappend l "\tif (!odr_initmember(o, p, sizeof(**p)))"
841         lappend l "\t\treturn odr_missing(o, opt, name);"
842         lappend l "\todr_implicit_settag(o, $tagtype, $tag);"
843         lappend l "\tif (odr_choice(o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], name))"
844     } else {
845         lappend l "\tif (!odr_constructed_begin(o, p, $tagtype, $tag, 0))"
846         lappend l "\t\treturn odr_missing(o, opt, name);"
847         lappend l "\tif (!odr_initmember(o, p, sizeof(**p)))"
848         lappend l "\t\treturn odr_missing(o, opt, name);"
849         lappend l "\tif (odr_choice(o, arm, &(*p)->[lindex $uName 1], &(*p)->[lindex $uName 0], name) &&"
850         lappend l "\t\todr_constructed_end(o))"
851     }
852     lappend l "\t\treturn 1;"
853
854     lappend l "\tif(o->direction == ODR_DECODE)"
855     lappend l "\t\t*p = 0;"
856
857     lappend l "\treturn odr_missing(o, opt, name);"
858     return [list [join $l \n] [join $j \n]]
859 }
860
861 # asnImports: parses i-list in "IMPORTS {i-list}" 
862 # On return inf(import,..)-array is updated.
863 # inf(import,"module") is a list of {C-handler, C-type} elements.
864 # The {C-handler, C-type} is compatible with the $tname as is used by the
865 # asnType procedure to solve external references.
866 proc asnImports {} {
867     global type val inf file
868
869     while {1} {
870         if {[string compare $type n]} {
871             asnError "Missing name in IMPORTS list"
872         }
873         lappend nam $val
874         lex
875         if {![string compare $type n] && ![string compare $val FROM]} {
876             lex
877             
878             if {[info exists inf(filename,$val)]} {
879                 set fname $inf(filename,$val)
880             } else {
881                 set fname $val
882             }
883             puts $file(outh) "\#include <$inf(h-dir)${fname}.h>"
884
885             if {[info exists inf(prefix,$val)]} {
886                 set prefix $inf(prefix,$val)
887             } else {
888                 set prefix $inf(prefix)
889             }
890             foreach n $nam {
891                 if {[info exists inf(map,$val,$n)]} {
892                     set v $inf(map,$val,$n)
893                 } else {
894                     set v $n
895                 }
896                 set w [join [split $v -] _]
897                 set inf(imports,$n) [list [lindex $prefix 0]$w \
898                                           [lindex $prefix 1]$w]
899             }
900             unset nam
901             lex
902             if {[string compare $type n]} break
903         } elseif {![string compare $type ,]} {
904             lex
905         } else break
906     }
907     if {[string compare $type \;]} {
908         asnError "Missing ; after IMPORTS list - got $type '$val'"
909     }
910     lex
911 }
912
913 # asnExports: parses e-list in "EXPORTS {e-list}" 
914 # This function does nothing with elements in the list.
915 proc asnExports {} {
916     global type val inf
917
918     while {1} {
919         if {[string compare $type n]} {
920             asnError "Missing name in EXPORTS list"
921         }
922         set inf(exports,$val) 1
923         lex
924         if {[string compare $type ,]} break
925         lex
926     }
927     if {[string compare $type \;]} {
928         asnError "Missing ; after EXPORTS list - got $type ($val)"
929     }
930     lex
931 }
932
933 # asnModuleBody: parses a module specification and generates C code.
934 # Exports lists, imports lists, and type definitions are handled;
935 # other things are silently ignored.
936 proc asnModuleBody {} {
937     global type val file inf
938
939     if {[info exists inf(prefix,$inf(module))]} {
940         set prefix $inf(prefix,$inf(module))
941     } else {
942         set prefix $inf(prefix)
943     }
944     set inf(fprefix) [lindex $prefix 0]
945     set inf(vprefix) [lindex $prefix 1]
946     set inf(dprefix) [lindex $prefix 2]
947     if {[llength $prefix] > 3} {
948         set inf(cprefix) [lindex $prefix 3]
949     } else {
950         set inf(cprefix) {YAZ_EXPORT }
951     }
952
953     if {$inf(verbose)} {
954         puts "Module $inf(module), $inf(lineno)"
955     }
956
957     set defblock 0
958     if {[info exists inf(init,$inf(module),c)]} {
959         puts $file(outc) $inf(init,$inf(module),c)
960     }
961     if {[info exists inf(init,$inf(module),h)]} {
962         puts $file(outh) "\#ifdef __cplusplus"
963         puts $file(outh) "extern \"C\" \{"
964         puts $file(outh) "\#endif"
965         set defblock 1
966         puts $file(outh) $inf(init,$inf(module),h)
967     }
968     if {[info exists inf(init,$inf(module),p)]} {
969         puts $file(outp) $inf(init,$inf(module),p)
970     }
971
972     while {[string length $type]} {
973         if {[string compare $type n]} {
974             lex
975             continue
976         }
977         if {![string compare $val END]} {
978             break
979         } elseif {![string compare $val EXPORTS]} {
980             lex
981             asnExports
982         } elseif {![string compare $val IMPORTS]} {
983             if {$defblock} {
984                 puts $file(outh) "\#ifdef __cplusplus"
985                 puts $file(outh) "\}"
986                 puts $file(outh) "\#endif"
987                 set defblock 0
988             }
989             lex
990             asnImports
991         } else {
992             if {!$defblock} {
993                 puts $file(outh) "\#ifdef __cplusplus"
994                 puts $file(outh) "extern \"C\" \{"
995                 puts $file(outh) "\#endif"
996                 set defblock 1
997             }
998             set inf(asndef) $inf(nodef)
999             set oval $val
1000             lex
1001             if {![string compare $type :]} {
1002                 lex
1003                 asnDef $oval
1004                 set inf(asndef) 0
1005             } elseif {![string compare $type n]} {
1006                 lex
1007                 if {[string length $type]} {
1008                     lex
1009                 }
1010             }
1011         }
1012     }
1013     if {$defblock} {
1014         puts $file(outh) "\#ifdef __cplusplus"
1015         puts $file(outh) "\}"
1016         puts $file(outh) "\#endif"
1017         set defblock 0
1018     }
1019     foreach x [array names inf imports,*] {
1020         unset inf($x)
1021     }
1022 }
1023
1024 # asnTagDefault: parses TagDefault section
1025 proc asnTagDefault {} {
1026     global type val inf file
1027     
1028     set inf(implicit-tags) 0
1029     while {[string length $type]} {
1030         if {[lex-name-move EXPLICIT]} {
1031             lex
1032             set inf(implicit-tags) 0
1033         } elseif {[lex-name-move  IMPLICIT]} {
1034             lex
1035             set inf(implicit-tags) 1
1036         } else {
1037             break
1038         }
1039     }
1040 }
1041
1042 # asnModules: parses a collection of module specifications.
1043 # Depending on the module pattern, $inf(moduleP), a module is either
1044 # skipped or processed.
1045 proc asnModules {} {
1046     global type val inf file yc_version
1047
1048     set inf(nodef) 0
1049     set inf(asndef) 0
1050     lex
1051     while {![string compare $type n]} {
1052         set inf(module) $val
1053         if {[info exists inf(moduleP)] && ![string match $inf(moduleP) $val]} {
1054             if {$inf(verbose)} {
1055                 puts "Skipping $id"
1056             }
1057             while {![lex-name-move END]} {
1058                 lex
1059             }
1060         } else {
1061             set inf(nodef) 1
1062             set inf(asndef) 1
1063
1064             while {![lex-name-move DEFINITIONS]} {
1065                 lex
1066                 if {![string length $type]} return
1067             }
1068             if {[info exists inf(filename,$inf(module))]} {
1069                 set fname $inf(filename,$inf(module))
1070             } else {
1071                 set fname $inf(module)
1072             }
1073             set ppname [join [split $fname -] _]
1074
1075             if {![info exists inf(c-file)]} {
1076                 set inf(c-file) ${fname}.c
1077             }
1078             set file(outc) [open $inf(c-file) w]
1079
1080             if {![info exists inf(h-file)]} {
1081                 set inf(h-file) ${fname}.h
1082             }
1083             set file(outh) [open $inf(h-path)/$inf(h-dir)$inf(h-file) w]
1084
1085             if {0} {
1086                 if {![info exists inf(p-file)]} {
1087                     set inf(p-file) ${fname}-p.h
1088                 }
1089                 set file(outp) [open $inf(h-path)/$inf(h-dir)$inf(p-file) w]
1090             }
1091
1092             set greeting {Generated automatically by the YAZ ASN.1 Compiler}
1093
1094             puts $file(outc) "/* ${greeting} ${yc_version} */"
1095             puts $file(outc) "/* Module-C: $inf(module) */"
1096             puts $file(outc) {}
1097
1098             puts $file(outh) "/* ${greeting} ${yc_version} */"
1099             puts $file(outh) "/* Module-H $inf(module) */"
1100             puts $file(outh) {}
1101
1102             if {[info exists file(outp)]} {
1103                 puts $file(outp) "/* ${greeting} ${yc_version} */"
1104                 puts $file(outp) "/* Module-P: $inf(module) */"
1105                 puts $file(outp) {}
1106             }
1107
1108             if {[info exists inf(p-file)]} {
1109                 puts $file(outc) "\#include <$inf(h-dir)$inf(p-file)>"
1110             } else {
1111                 puts $file(outc) "\#include <$inf(h-dir)$inf(h-file)>"
1112             }
1113             puts $file(outh) "\#ifndef ${ppname}_H"
1114             puts $file(outh) "\#define ${ppname}_H"
1115             puts $file(outh) {}
1116             puts $file(outh) "\#include <yaz/odr.h>"
1117            
1118             if {[info exists file(outp)]} { 
1119                 puts $file(outp) "\#ifndef ${ppname}_P_H"
1120                 puts $file(outp) "\#define ${ppname}_P_H"
1121                 puts $file(outp) {}
1122                 puts $file(outp) "\#include <$inf(h-dir)$inf(h-file)>"
1123
1124             }
1125             
1126             asnTagDefault
1127             if {[string compare $type :]} {
1128                 asnError "::= expected got $type '$val'"
1129             } 
1130             lex
1131             if {![lex-name-move BEGIN]} {
1132                 asnError "BEGIN expected"
1133             }
1134             asnModuleBody
1135             lex
1136
1137             if {[info exists file(outp)]} {
1138                 set f $file(outp)
1139             } else {
1140                 set f $file(outh)
1141             }
1142             puts $f "\#ifdef __cplusplus"
1143             puts $f "extern \"C\" \{"
1144             puts $f "\#endif"
1145             for {set i 1} {$i < $inf(nodef)} {incr i} {
1146                 puts $f $inf(var,$i)
1147                 if {[info exists inf(asn,$i)]} {
1148                     if {0} {
1149                         puts $f "/*"
1150                         foreach comment $inf(asn,$i) {
1151                             puts $f $comment
1152                         }
1153                         puts $f " */"
1154                     }
1155                     unset inf(asn,$i)
1156                 }
1157                 unset inf(var,$i)
1158                 puts $f {}
1159             }
1160             puts $f "\#ifdef __cplusplus"
1161             puts $f "\}"
1162             puts $f "\#endif"
1163
1164             if {[info exists inf(body,$inf(module),h)]} {
1165                 puts $file(outh) $inf(body,$inf(module),h)
1166             }
1167             if {[info exists inf(body,$inf(module),c)]} {
1168                 puts $file(outc) $inf(body,$inf(module),c)
1169             }
1170             if {[info exists inf(body,$inf(module),p)]} {
1171                 if {[info exists file(outp)]} {
1172                     puts $file(outp) $inf(body,$inf(module),p)
1173                 }
1174             }
1175             puts $file(outh) "\#endif"
1176             if {[info exists file(outp)]} {
1177                 puts $file(outp) "\#endif"
1178             }
1179             foreach f [array names file] {
1180                 close $file($f)
1181             }
1182             unset inf(c-file)
1183             unset inf(h-file)
1184             catch {unset inf(p-file)}
1185         }
1186     }
1187 }
1188
1189 # asnFile: parses an ASN.1 specification file as specified in $inf(iname).
1190 proc asnFile {} {
1191     global inf file
1192
1193     if {$inf(verbose) > 1} {
1194         puts "Reading ASN.1 file $inf(iname)"
1195     }
1196     set inf(str) {}
1197     set inf(lineno) 0
1198     set inf(inf) [open $inf(iname) r]
1199     
1200     asnModules
1201     
1202 }
1203
1204 # The following procedures are invoked by the asnType function. 
1205 # Each procedure takes the form: asnBasic<TYPE> and they must return
1206 # two elements: the C function handler and the C type.
1207 # On entry upvar $name is the type we are defining and global, $inf(module), is
1208 # the current module name.
1209
1210 proc asnBasicEXTERNAL {} {
1211     return {odr_external {Odr_external}}
1212 }
1213
1214 proc asnBasicINTEGER {} {
1215     return {odr_integer {int}}
1216 }
1217
1218 proc asnBasicENUMERATED {} {
1219     return {odr_enum {int}}
1220 }
1221
1222 proc asnBasicNULL {} {
1223     return {odr_null {Odr_null}}
1224 }
1225
1226 proc asnBasicBOOLEAN {} {
1227     return {odr_bool {bool_t}}
1228 }
1229
1230 proc asnBasicOCTET {} {
1231     global type val
1232     lex-name-move STRING
1233     return {odr_octetstring {Odr_oct}}
1234 }
1235
1236 proc asnBasicBIT {} {
1237     global type val
1238     lex-name-move STRING
1239     return {odr_bitstring {Odr_bitmask}}
1240 }
1241
1242 proc asnBasicOBJECT {} {
1243     global type val
1244     lex-name-move IDENTIFIER
1245     return {odr_oid {Odr_oid}}
1246 }
1247
1248 proc asnBasicGeneralString {} {
1249     return {odr_generalstring char}
1250 }
1251
1252 proc asnBasicVisibleString {} {
1253     return {odr_visiblestring char}
1254 }
1255
1256 proc asnBasicGeneralizedTime {} {
1257     return {odr_generalizedtime char}
1258 }
1259
1260 proc asnBasicANY {} {
1261     upvar name name
1262     global inf
1263     return [list $inf(fprefix)ANY_$name void]
1264 }
1265
1266 # userDef: reads user definitions file $name
1267 proc userDef {name} {
1268     global inf
1269
1270     if {$inf(verbose) > 1} {
1271         puts "Reading definitions file $name"
1272     }
1273     source $name
1274
1275     if {[info exists default-prefix]} {
1276         set inf(prefix) ${default-prefix}
1277     }
1278     if {[info exists h-path]} {
1279         set inf(h-path) ${h-path}
1280     }
1281     foreach m [array names prefix] {
1282         set inf(prefix,$m) $prefix($m)
1283     }
1284     foreach m [array names body] {
1285         set inf(body,$m) $body($m)
1286     }
1287     foreach m [array names init] {
1288         set inf(init,$m) $init($m)
1289     }
1290     foreach m [array names filename] {
1291         set inf(filename,$m) $filename($m)
1292     }
1293     foreach m [array names map] {
1294         set inf(map,$m) $map($m)
1295     }
1296     foreach m [array names membermap] {
1297         set inf(membermap,$m) $membermap($m)
1298     }
1299     foreach m [array names unionmap] {
1300         set inf(unionmap,$m) $unionmap($m)
1301     }
1302 }
1303
1304 set inf(verbose) 0
1305 set inf(prefix) {yc_ Yc_ YC_}
1306 set inf(h-path) .
1307 set inf(h-dir) ""
1308
1309 # Parse command line
1310 set l [llength $argv]
1311 set i 0
1312 while {$i < $l} {
1313     set arg [lindex $argv $i]
1314     switch -glob -- $arg {
1315         -v {
1316             incr inf(verbose) 
1317         }
1318         -c {
1319             set p [string range $arg 2 end]
1320             if {![string length $p]} {
1321                  set p [lindex $argv [incr i]]
1322              }
1323             set inf(c-file) $p
1324         }
1325         -I* {
1326             set p [string range $arg 2 end]
1327             if {![string length $p]} {
1328                  set p [lindex $argv [incr i]]
1329              }
1330             set inf(h-path) $p
1331         }
1332         -i* {
1333             set p [string range $arg 2 end]
1334             if {![string length $p]} {
1335                  set p [lindex $argv [incr i]]
1336             }
1337             set inf(h-dir) [string trim $p \\/]/
1338         }
1339         -h* {
1340             set p [string range $arg 2 end]
1341             if {![string length $p]} {
1342                  set p [lindex $argv [incr i]]
1343              }
1344             set inf(h-file) $p
1345         }
1346         -p* {
1347             set p [string range $arg 2 end]
1348             if {![string length $p]} {
1349                 set p [lindex $argv [incr i]]
1350             }
1351             set inf(p-file) $p
1352         }
1353         -d* {
1354             set p [string range $arg 2 end]
1355             if {![string length $p]} {
1356                 set p [lindex $argv [incr i]]
1357             }
1358             userDef $p
1359         }
1360         -m* {
1361             set p [string range $arg 2 end]
1362             if {![string length $p]} {
1363                 set p [lindex $argv [incr i]]
1364             }
1365             set inf(moduleP) $p
1366         }
1367         -x* {
1368             set p [string range $arg 2 end]
1369             if {![string length $p]} {
1370                 set p [lindex $argv [incr i]]
1371             }
1372             if {[llength $p] == 1} {
1373                 set inf(prefix) [list [string tolower $p] \
1374                                      [string toupper $p] [string toupper $p]]
1375             } elseif {[llength $p] == 3} {
1376                 set inf(prefix) $p
1377             } else {
1378                 puts [llength $p]
1379                 exit 1
1380             }
1381         }           
1382         default {
1383             set inf(iname) $arg
1384         }
1385     }
1386     incr i
1387 }
1388
1389 if {![info exists inf(iname)]} {
1390     puts "YAZ ASN.1 Compiler ${yc_version}"
1391     puts "Usage:"       
1392     puts -nonewline ${argv0}
1393     puts { [-v] [-c cfile] [-h hfile] [-p hfile] [-d dfile] [-I iout] [-i idir]}
1394     puts {    [-m module] file}
1395     exit 1
1396 }
1397
1398 asnFile