NativeEnumerated.c vars NULL init and check
[com/asn1c.git] / libasn1fix / asn1fix_retrieve.c
1 #include "asn1fix_internal.h"
2
3 enum ftt_what {
4         FTT_TYPE,       /* Find the type of the given expression */
5         FTT_VALUE,      /* Find the value of the given expression */
6         FTT_CONSTR_TYPE /* Find the type of the given expression having constraint */ 
7 };
8
9 static asn1p_expr_t *asn1f_find_terminal_thing(arg_t *arg, asn1p_expr_t *expr, enum ftt_what);
10 static int asn1f_compatible_with_exports(arg_t *arg, asn1p_module_t *mod, const char *name);
11
12
13 /*
14  * Lookup a child by its name.
15  */
16 asn1p_expr_t *
17 asn1f_lookup_child(asn1p_expr_t *tc, const char *name) {
18         asn1p_expr_t *child_tc;
19
20         TQ_FOR(child_tc, &(tc->members), next) {
21                 if(child_tc->Identifier
22                 && strcmp(child_tc->Identifier, name) == 0) {
23                         return child_tc;
24                 }
25         }
26
27         errno = ESRCH;
28         return NULL;
29 }
30
31 static asn1p_expr_t *
32 asn1f_lookup_in_module(asn1p_module_t *mod, const char *name) {
33     asn1p_expr_t *expr = genhash_get(mod->members_hash, name);
34     if(!expr) {
35         asn1p_expr_t *memb;
36         TQ_FOR(memb, &mod->members, next) {
37             if(memb->expr_type == ASN_BASIC_ENUMERATED) {
38                 asn1p_expr_t *v = asn1f_lookup_child(memb, name);
39                 if(v) return v;
40             }
41         }
42     }
43
44     return expr;
45 }
46
47 asn1p_module_t *
48 asn1f_lookup_in_imports(arg_t *arg, asn1p_module_t *mod, const char *name) {
49         asn1p_xports_t *xp;
50
51         /*
52          * Search in which exactly module this name is defined.
53          */
54         TQ_FOR(xp, &(mod->imports), xp_next) {
55         asn1p_module_t *fromModule =
56             asn1f_lookup_module(arg, xp->fromModuleName, NULL);
57         asn1p_expr_t *tc = (asn1p_expr_t *)0;
58
59         TQ_FOR(tc, &(xp->xp_members), next) {
60             if(strcmp(name, tc->Identifier) == 0)
61                                 break;
62
63                         if(!fromModule)
64                                 continue;
65
66             asn1p_expr_t *v =
67                 asn1f_lookup_in_module(fromModule, name);
68             if(v) break;
69                 }
70                 if(tc) break;
71         }
72         if(xp == NULL) {
73                 errno = ESRCH;
74                 return NULL;
75         }
76
77         /*
78          * Okay, right now we have a module name and, hopefully, an OID.
79          * Search the arg->asn for the specified module.
80          */
81         mod = asn1f_lookup_module(arg, xp->fromModuleName, xp->identifier.oid);
82         if(mod == NULL) {
83                 /* Conditional debug */
84                 if(!(arg->expr->_mark & TM_BROKEN)) {
85                         arg->expr->_mark |= TM_BROKEN;
86                         FATAL("Cannot find external module \"%s\" "
87                                 "mentioned for "
88                                 "\"%s\" at line %d. "
89                                 "Obtain this module and instruct compiler to process it too.",
90                                 xp->fromModuleName, name, arg->expr->_lineno);
91                 }
92                 /* ENOENT/ETOOMANYREFS */
93                 return NULL;
94         }
95
96         /*
97          * Check that the EXPORTS section of this module contains
98          * the symbol we care about, or it is EXPORTS ALL.
99          */
100         if(asn1f_compatible_with_exports(arg, mod, name)) {
101                 errno = EPERM;
102                 return NULL;
103         }
104
105         return mod;
106 }
107
108 asn1p_module_t *
109 asn1f_lookup_module(arg_t *arg, const char *module_name, const asn1p_oid_t *oid) {
110         asn1p_module_t *mod;
111
112         assert(module_name);
113
114         /*
115          * If OID is given, the module_name is unused.
116          * If OID is not given, the module_name may mean
117          * either the real module's name, or the symbol which is the
118          * result of renaming. Check this first.
119          */
120         if(oid == 0) {
121                 asn1p_xports_t *xp;
122                 /*
123                  * Check inside the IMPORTS section for possible renaming.
124                  * Renaming practically means that a module_name is mentioned
125                  * somewhere in the IMPORTS section AND OID is given.
126                  */
127                 TQ_FOR(xp, &(arg->mod->imports), xp_next) {
128                         if(strcmp(module_name, xp->fromModuleName))
129                                 continue;
130                         if(oid) {
131                                 FATAL("Ambiguous reference: "
132                                         "%s "
133                                         "matches several modules",
134                                         module_name);
135                                 errno = ETOOMANYREFS;
136                                 return NULL;
137                         }
138                         /*
139                          * Yes, there is a renaming.
140                          * Make lookup use OID instead.
141                          */
142                         oid = xp->identifier.oid;
143                 }
144         }
145
146         /*
147          * Perform lookup using OID or module_name.
148          */
149         TQ_FOR(mod, &(arg->asn->modules), mod_next) {
150                 if(oid) {
151                         if(mod->module_oid) {
152                                 if(asn1p_oid_compare(oid,
153                                         mod->module_oid)) {
154                                         continue;
155                                 } else {
156                                         /* Match! Even if name doesn't. */
157                                         return mod;
158                                 }
159                         } else {
160                                 /* Not match, even if name is the same. */
161                                 continue;
162                         }
163                 }
164         
165                 if(strcmp(module_name, mod->ModuleName) == 0)
166                         return mod;
167         }
168
169         DEBUG("\tModule \"%s\" not found", module_name);
170
171         errno = ENOENT;
172         return NULL;
173 }
174
175 static asn1p_expr_t *
176 asn1f_lookup_symbol_impl(arg_t *arg, asn1p_expr_t *rhs_pspecs, const asn1p_ref_t *ref, int recursion_depth) {
177     asn1_namespace_t *initial_namespace = arg->ns;
178         asn1p_ref_t tmp_ref;
179         asn1p_module_t *imports_from;
180         char *modulename;
181         char *identifier;
182
183     if(ref->module && arg->mod != ref->module) {
184         return WITH_MODULE(
185             ref->module,
186             asn1f_lookup_symbol_impl(arg, rhs_pspecs, ref, recursion_depth));
187     }
188
189         /*
190          * First of all, a reference to a symbol may be specified
191          * using several possible forms:
192          * a) simple identifier
193          *      v INTEGER ::= value
194          * b) external reference
195          *      v INTEGER ::= Module1.value
196          * c) class-related stuff (the most complex stuff)
197          *      v ::= <[A-Z][A-Z0-9a-z-]*>.&<[A-Z0-9a-z-]+>.
198          * All other forms are not implemented at this moment.
199          */
200
201         DEBUG("Lookup (%s) in %s for line %d",
202                 asn1f_printable_reference(ref),
203                 asn1_namespace_string(initial_namespace),
204                 ref->_lineno);
205
206         if(recursion_depth++ > 30 /* Arbitrary constant */) {
207         FATAL(
208             "Excessive circular referencing detected in namespace %s for %s at "
209             "line %d",
210             asn1_namespace_string(initial_namespace),
211             asn1f_printable_reference(ref), ref->_lineno);
212         errno = ETOOMANYREFS;
213                 return NULL;
214         }
215
216         if(ref->comp_count == 1) {
217                 modulename = NULL;
218                 identifier = ref->components[0].name;
219         } else if(ref->comp_count == 2
220                 && ref->components[1].name[0] != '&') {
221                 modulename = ref->components[0].name;
222                 identifier = ref->components[1].name;
223         } else if(ref->comp_count > 1
224                 && isupper(ref->components[0].name[0])
225                 && ref->components[1].name[0] == '&') {
226                 asn1p_expr_t *extract;
227                 /*
228                  * This is a reference to a CLASS-related stuff.
229                  * Employ a separate function for that.
230                  */
231                 extract = asn1f_class_access(arg, rhs_pspecs, ref);
232                 
233                 return extract;
234         } else {
235                 DEBUG("\tToo many components: %d", ref->comp_count);
236                 errno = EINVAL;
237                 return NULL;
238         }
239
240     if(modulename) {
241         /*
242          * The modulename is specified inside this reference.
243          * To avoid recursion, reformat the reference
244          * as it were local to that module.
245          */
246         tmp_ref = *ref;
247         tmp_ref.components++; /* Hide the first element */
248         tmp_ref.comp_count--;
249         assert(tmp_ref.comp_count > 0);
250         ref = &tmp_ref;
251     }
252
253     asn1_namespace_t *my_namespace = initial_namespace;
254
255 #define DISPOSE_OF_MY_NAMESPACE()               \
256     do {                                        \
257         int tmp_errno = errno;                  \
258         if(my_namespace != initial_namespace) { \
259             asn1_namespace_free(my_namespace);  \
260             my_namespace = NULL;                \
261         }                                       \
262         errno = tmp_errno;                      \
263     } while(0)
264
265     /*
266      * If module name is specified explicitly
267      * OR the current module's IMPORTS clause contains the identifier,
268      * switch namespace to that module.
269      */
270     if(modulename) {
271         imports_from = asn1f_lookup_module(arg, modulename, 0);
272         if(imports_from == NULL) {
273             FATAL(
274                 "Module \"%s\" "
275                 "mentioned at line %d is not found",
276                 modulename, ref->_lineno);
277             return NULL;
278         }
279
280         /*
281          * Check that the EXPORTS section of this module contains
282          * the symbol we care about, or it is EXPORTS ALL.
283          */
284         if(asn1f_compatible_with_exports(arg, imports_from, identifier)) {
285             errno = EPERM;
286             return NULL;
287         }
288
289         DISPOSE_OF_MY_NAMESPACE();
290         my_namespace = asn1_namespace_new_from_module(imports_from, 1);
291         DEBUG("Lookup (%s) in %s for line %d", asn1f_printable_reference(ref),
292               asn1_namespace_string(my_namespace), ref->_lineno);
293     }
294
295     size_t namespace_switches = 0;
296
297     /*
298      * Search through all layers of the namespace.
299      */
300     for(ssize_t ns_item = my_namespace->elements_count - 1; ns_item >= 0;
301         ns_item--) {
302         struct asn1_namespace_element_s *ns_el =
303             &my_namespace->elements[ns_item];
304
305         switch(ns_el->selector) {
306         case NAM_SYMBOL:
307             if(modulename) {
308                 /*
309                  * Trying to match a fully specified "Module.Symbol"
310                  * against the "Symbol" parameter. Doesn't match.
311                  */
312                 continue;
313             }
314             if(strcmp(ns_el->u.symbol.identifier, identifier) != 0) {
315                 continue;
316             } else {
317                 DEBUG("Lookup (%s) in %s for line %d => found as parameter",
318                       asn1f_printable_reference(ref),
319                       asn1_namespace_string(my_namespace), ref->_lineno);
320                 DISPOSE_OF_MY_NAMESPACE();
321                 return ns_el->u.symbol.resolution;
322             }
323         case NAM_SPACE: {
324             asn1p_expr_t *ref_tc; /* Referenced tc */
325             /*
326              * Do a direct symbol search in the given module.
327              */
328             ref_tc = asn1f_lookup_in_module(ns_el->u.space.module, identifier);
329             if(ref_tc) {
330                 /* It is acceptable that we don't use input parameters */
331                 if(rhs_pspecs && !ref_tc->lhs_params) {
332                     WARNING(
333                         "Parameterized type %s expected "
334                         "for %s at line %d",
335                         ref_tc->Identifier, asn1f_printable_reference(ref),
336                         ref->_lineno);
337                 }
338                 if(!rhs_pspecs && ref_tc->lhs_params) {
339                     FATAL(
340                         "Type %s expects specialization "
341                         "from %s at line %d",
342                         ref_tc->Identifier, asn1f_printable_reference(ref),
343                         ref->_lineno);
344                     errno = EPERM;
345                     DISPOSE_OF_MY_NAMESPACE();
346                     return NULL;
347                 }
348                 if(rhs_pspecs && ref_tc->lhs_params) {
349                     /* Specialize the target */
350                     ref_tc =
351                         asn1f_parameterization_fork(arg, ref_tc, rhs_pspecs);
352                 }
353
354                 DISPOSE_OF_MY_NAMESPACE();
355                 return ref_tc;
356             }
357         }
358
359     /*
360             if(!expr && !(arg->expr->_mark & TM_BROKEN)
361                && !(imports_from->_tags & MT_STANDARD_MODULE)) {
362                 arg->expr->_mark |= TM_BROKEN;
363                 if(modulename) {
364                 } else {
365                     FATAL(
366                         "Module %s referred in IMPORTS section "
367                         "for %s of module %s does not contain "
368                         "the requested symbol",
369                         imports_from->ModuleName,
370                         asn1f_printable_reference(ref), mod->ModuleName);
371                 }
372             }
373         */
374
375             /* Search inside the IMPORTS section of the given module */
376             imports_from =
377                 asn1f_lookup_in_imports(arg, ns_el->u.space.module, identifier);
378             if(imports_from) {
379
380                 if(namespace_switches++ > 10 /* Arbitrary constant */) {
381                     FATAL(
382                         "Excessive circular referencing detected in namespace "
383                         "%s for %s at "
384                         "line %d",
385                         asn1_namespace_string(arg->ns),
386                         asn1f_printable_reference(ref), ref->_lineno);
387                     errno = ETOOMANYREFS;
388                     DISPOSE_OF_MY_NAMESPACE();
389                     return NULL;
390                 }
391
392                 /* Switch namespace */
393                 DISPOSE_OF_MY_NAMESPACE();
394                 my_namespace = asn1_namespace_new_from_module(imports_from, 1);
395                 DEBUG("Lookup (%s) in %s for line %d",
396                       asn1f_printable_reference(ref),
397                       asn1_namespace_string(my_namespace), ref->_lineno);
398                 ns_item = my_namespace->elements_count;
399
400                 continue;
401             } else if(errno != ESRCH) {
402                 /*
403                  * Return only if the name was not found.
404                  * If module was not found or more serious error
405                  * encountered, just return preserving the errno.
406                  */
407                 DISPOSE_OF_MY_NAMESPACE();
408                 return NULL;
409             }
410
411             if(modulename) {
412                 FATAL(
413                     "Module %s referred by %s in module %s "
414                     "does not contain the requested symbol",
415                     ns_el->u.space.module->ModuleName,
416                     asn1f_printable_reference(ref), modulename);
417                 break;
418             }
419
420             /* Search failed in the given namespace */
421             continue;
422         }
423     }
424
425     DEBUG(
426         "Namespace %s does not contain \"%s\" "
427         "mentioned at line %d: %s",
428         asn1_namespace_string(my_namespace), identifier, ref->_lineno,
429         strerror(errno));
430
431     DISPOSE_OF_MY_NAMESPACE();
432
433     if(asn1f_check_known_external_type(identifier) == 0) {
434                 errno = EEXIST; /* Exists somewhere */
435         } else {
436                 errno = ESRCH;
437         }
438         return NULL;
439 }
440
441
442 asn1p_expr_t *
443 asn1f_lookup_symbol(arg_t *arg, asn1p_expr_t *rhs_pspecs,
444                     const asn1p_ref_t *ref) {
445     return asn1f_lookup_symbol_impl(arg, rhs_pspecs, ref, 0);
446 }
447
448 asn1p_expr_t *
449 asn1f_find_terminal_type(arg_t *arg, asn1p_expr_t *expr) {
450         return asn1f_find_terminal_thing(arg, expr, FTT_TYPE);
451 }
452
453 asn1p_expr_t *
454 asn1f_find_terminal_value(arg_t *arg, asn1p_expr_t *expr) {
455         return asn1f_find_terminal_thing(arg, expr, FTT_VALUE);
456 }
457
458 asn1p_expr_t *
459 asn1f_find_ancestor_type_with_PER_constraint(arg_t *arg, asn1p_expr_t *expr) {
460         return asn1f_find_terminal_thing(arg, expr, FTT_CONSTR_TYPE);
461 }
462
463 static asn1p_expr_t *
464 asn1f_find_terminal_thing(arg_t *arg, asn1p_expr_t *expr, enum ftt_what what) {
465         asn1p_ref_t *ref = 0;
466         asn1p_expr_t *tc;
467
468     if(arg->mod != expr->module) {
469         return WITH_MODULE(expr->module,
470                            asn1f_find_terminal_thing(arg, expr, what));
471     }
472
473     switch(what) {
474     case FTT_TYPE:
475     case FTT_CONSTR_TYPE:
476         /* Expression may be a terminal type itself */
477         if(expr->expr_type != A1TC_REFERENCE) {
478             return expr;
479         }
480         ref = expr->reference;
481         break;
482     case FTT_VALUE:
483
484         DEBUG("%s(%s->%s) meta %d for line %d",
485                 "VALUE", expr->Identifier, asn1f_printable_reference(ref),
486                 expr->meta_type, expr->_lineno);
487
488         assert(expr->meta_type == AMT_VALUE);
489         assert(expr->value);
490         /* Expression may be a terminal type itself */
491         if(expr->value->type != ATV_REFERENCED) {
492             return expr;
493         }
494         ref = expr->value->value.reference;
495         break;
496     }
497
498         DEBUG("%s(%s->%s) for line %d",
499                 (what == FTT_VALUE)?"VALUE":"TYPE",
500                 expr->Identifier, asn1f_printable_reference(ref),
501                 expr->_lineno);
502
503         assert(ref);
504
505         /*
506          * Lookup inside the type itself (ENUMERATED, INTEGER, etc).
507          */
508         if(what == FTT_VALUE) {
509                 asn1p_expr_t *val_type_tc;
510                 val_type_tc = asn1f_find_terminal_type(arg, expr);
511         if(val_type_tc
512            && WITH_MODULE(val_type_tc->module,
513                           asn1f_look_value_in_type(arg, val_type_tc, expr))) {
514             return expr;
515         }
516                 if(expr->value->type != ATV_REFERENCED) {
517             return expr;
518                 }
519                 assert(ref == expr->value->value.reference);
520                 ref = expr->value->value.reference;
521         }
522
523         /*
524          * Lookup inside the default module and its IMPORTS section.
525          */
526     tc = asn1f_lookup_symbol(arg, expr->rhs_pspecs, ref);
527     if(tc == NULL) {
528                 /*
529                  * Lookup inside the ref's module and its IMPORTS section.
530                  */
531         tc = WITH_MODULE(ref->module,
532                          asn1f_lookup_symbol(arg, expr->rhs_pspecs, ref));
533         if(tc == NULL) {
534                         DEBUG("\tSymbol \"%s\" not found: %s",
535                                 asn1f_printable_reference(ref),
536                                 strerror(errno));
537             return NULL;
538                 }
539         }
540
541         /*
542          * Recursive loops detection.
543          */
544     if(tc->_mark & TM_RECURSION) {
545         DEBUG("Recursion loop detected for %s at line %d",
546               asn1f_printable_reference(ref), ref->_lineno);
547         errno = EPERM;
548         return NULL;
549     }
550
551     tc->_type_referenced = 1;
552
553     if((what == FTT_CONSTR_TYPE) && (tc->constraints)) {
554         return tc;
555     }
556
557     tc->_mark |= TM_RECURSION;
558     expr = WITH_MODULE(tc->module, asn1f_find_terminal_thing(arg, tc, what));
559     tc->_mark &= ~TM_RECURSION;
560
561     return expr;
562 }
563
564
565 /*
566  * Make sure that the specified name is present or otherwise does
567  * not contradict with the EXPORTS clause of the specified module.
568  */
569 static int
570 asn1f_compatible_with_exports(arg_t *arg, asn1p_module_t *mod, const char *name) {
571         asn1p_xports_t *exports;
572         asn1p_expr_t *item;
573
574         assert(mod);
575         assert(name);
576
577         exports = TQ_FIRST(&(mod->exports));
578         if(exports == NULL) {
579                 /* No EXPORTS section or EXPORTS ALL; */
580                 return 0;
581         }
582
583         TQ_FOR(item, &(exports->xp_members), next) {
584                 if(strcmp(item->Identifier, name) == 0)
585                         return 0;
586         }
587
588         /* Conditional debug */
589         if(!(arg->expr->_mark & TM_BROKEN)) {
590                 arg->expr->_mark |= TM_BROKEN;
591                 FATAL("EXPORTS section of module %s in %s "
592                         "does not mention %s at line %d",
593                         mod->ModuleName, mod->source_file_name, name,
594                         arg->expr->_lineno);
595         }
596
597         errno = ESRCH;
598         return -1;
599 }