Commit | Line | Data |
---|---|---|
d44e3c4f | 1 | /****************************************************************************** |
2 | * Copyright (c) 2000-2016 Ericsson Telecom AB | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * Balasko, Jeno | |
10 | * Baranyi, Botond | |
11 | * Beres, Szabolcs | |
12 | * Delic, Adam | |
13 | * Forstner, Matyas | |
14 | * Kovacs, Ferenc | |
15 | * Raduly, Csaba | |
16 | * Szabados, Kristof | |
17 | * Szabo, Bence Janos | |
18 | * Szabo, Janos Zoltan – initial implementation | |
19 | * Szalai, Gabor | |
20 | * Tatarka, Gabor | |
21 | * | |
22 | ******************************************************************************/ | |
970ed795 EL |
23 | #include "Objid.hh" |
24 | ||
25 | #include "../common/dbgnew.hh" | |
26 | #include <errno.h> | |
27 | #include <limits.h> | |
28 | #include "../common/static_check.h" | |
29 | #include "Integer.hh" | |
30 | ||
31 | static const size_t MIN_COMPONENTS = 2; | |
32 | ||
33 | struct OBJID::objid_struct { | |
34 | unsigned int ref_count; | |
35 | int n_components; ///< number of elements in \a components_ptr (min. 2) | |
36 | int overflow_idx; ///< index of the first overflow, or -1 | |
37 | objid_element components_ptr[MIN_COMPONENTS]; | |
38 | }; | |
39 | ||
40 | #define OBJID_FMT "%u" | |
41 | //#define OBJID_FMT "%lu" | |
42 | ||
43 | void OBJID::init_struct(int n_components) | |
44 | { | |
45 | if (n_components < 0) { | |
46 | val_ptr = NULL; | |
47 | TTCN_error("Initializing an objid value with a negative number of " | |
48 | "components."); | |
49 | } | |
50 | // TODO check n_components >= 2 | |
51 | val_ptr = (objid_struct*)Malloc(sizeof(objid_struct) | |
52 | + (n_components - MIN_COMPONENTS) * sizeof(objid_element)); | |
53 | val_ptr->ref_count = 1; | |
54 | val_ptr->n_components = n_components; | |
55 | val_ptr->overflow_idx = -1; | |
56 | } | |
57 | ||
58 | void OBJID::copy_value() | |
59 | { | |
60 | if (val_ptr != NULL && val_ptr->ref_count > 1) { | |
61 | objid_struct *old_ptr = val_ptr; | |
62 | old_ptr->ref_count--; | |
63 | init_struct(old_ptr->n_components); // val_ptr reallocated | |
64 | memcpy(val_ptr->components_ptr, old_ptr->components_ptr, | |
65 | old_ptr->n_components * sizeof(objid_element)); | |
66 | val_ptr->overflow_idx = old_ptr->overflow_idx; | |
67 | } | |
68 | } | |
69 | ||
70 | void OBJID::clean_up() | |
71 | { | |
72 | if (val_ptr != NULL) { | |
73 | if (val_ptr->ref_count > 1) val_ptr->ref_count--; | |
74 | else if (val_ptr->ref_count == 1) Free(val_ptr); | |
75 | else TTCN_error("Internal error: Invalid reference counter in an objid " | |
76 | "value."); | |
77 | val_ptr = NULL; | |
78 | } | |
79 | } | |
80 | ||
81 | OBJID::OBJID() | |
82 | { | |
83 | val_ptr = NULL; // unbound | |
84 | } | |
85 | ||
86 | OBJID::OBJID(int init_n_components, ...) | |
87 | { | |
88 | init_struct(init_n_components); | |
89 | va_list ap; | |
90 | va_start(ap, init_n_components); | |
91 | for (int i = 0; i < init_n_components; i++) { | |
92 | val_ptr->components_ptr[i] = va_arg(ap, objid_element); | |
93 | } | |
94 | va_end(ap); | |
95 | } | |
96 | ||
97 | ||
98 | OBJID::OBJID(int init_n_components, const objid_element *init_components) | |
99 | { | |
100 | init_struct(init_n_components); | |
101 | memcpy(val_ptr->components_ptr, init_components, init_n_components * | |
102 | sizeof(objid_element)); | |
103 | } | |
104 | ||
105 | OBJID::OBJID(const OBJID& other_value) | |
106 | : Base_Type(other_value) | |
107 | { | |
108 | if (other_value.val_ptr == NULL) | |
109 | TTCN_error("Copying an unbound objid value."); | |
110 | val_ptr = other_value.val_ptr; | |
111 | val_ptr->ref_count++; | |
112 | } | |
113 | ||
114 | OBJID::~OBJID() | |
115 | { | |
116 | clean_up(); | |
117 | } | |
118 | ||
119 | OBJID& OBJID::operator=(const OBJID& other_value) | |
120 | { | |
121 | if (other_value.val_ptr == NULL) | |
122 | TTCN_error("Assignment of an unbound objid value."); | |
123 | if (&other_value != this) { | |
124 | clean_up(); | |
125 | val_ptr = other_value.val_ptr; | |
126 | val_ptr->ref_count++; | |
127 | } | |
128 | return *this; | |
129 | } | |
130 | ||
131 | boolean OBJID::operator==(const OBJID& other_value) const | |
132 | { | |
133 | if (val_ptr == NULL) TTCN_error("The left operand of comparison is an " | |
134 | "unbound objid value."); | |
135 | if (other_value.val_ptr == NULL) TTCN_error("The right operand of comparison " | |
136 | "is an unbound objid value."); | |
137 | if (val_ptr->n_components != other_value.val_ptr->n_components) return FALSE; | |
138 | if (val_ptr->overflow_idx != other_value.val_ptr->overflow_idx) return FALSE; | |
139 | return !memcmp(val_ptr->components_ptr, | |
140 | other_value.val_ptr->components_ptr, | |
141 | val_ptr->n_components * sizeof(objid_element)); | |
142 | } | |
143 | ||
144 | OBJID::objid_element& OBJID::operator[](int index_value) | |
145 | { | |
146 | if (val_ptr == NULL) { | |
147 | if (index_value != 0) | |
148 | TTCN_error("Accessing a component of an unbound objid value."); | |
149 | init_struct(1); | |
150 | return val_ptr->components_ptr[0]; | |
151 | } else { | |
152 | if (index_value < 0) TTCN_error("Accessing an objid component using " | |
153 | "a negative index (%d).", index_value); | |
154 | int n_components = val_ptr->n_components; | |
155 | if (index_value > n_components) TTCN_error("Index overflow when accessing " | |
156 | "an objid component: the index is %d, but the value has only %d " | |
157 | "components.", index_value, n_components); | |
158 | else if (index_value == n_components) { | |
159 | if (val_ptr->ref_count == 1) { | |
160 | val_ptr = (objid_struct*) | |
161 | Realloc(val_ptr, sizeof(objid_struct) | |
162 | + n_components * sizeof(objid_element)); | |
163 | val_ptr->n_components++; | |
164 | } else { | |
165 | objid_struct *old_ptr = val_ptr; | |
166 | old_ptr->ref_count--; | |
167 | init_struct(n_components + 1); | |
168 | memcpy(val_ptr->components_ptr, old_ptr->components_ptr, | |
169 | n_components * sizeof(objid_element)); | |
170 | } | |
171 | } | |
172 | return val_ptr->components_ptr[index_value]; | |
173 | } | |
174 | } | |
175 | ||
176 | OBJID::objid_element OBJID::operator[](int index_value) const | |
177 | { | |
178 | if (val_ptr == NULL) | |
179 | TTCN_error("Accessing a component of an unbound objid value."); | |
180 | if (index_value < 0) | |
181 | TTCN_error("Accessing an objid component using a negative index (%d).", | |
182 | index_value); | |
183 | if (index_value >= val_ptr->n_components) TTCN_error("Index overflow when " | |
184 | "accessing an objid component: the index is %d, but the value has only %d " | |
185 | "components.", index_value, val_ptr->n_components); | |
186 | return val_ptr->components_ptr[index_value]; | |
187 | } | |
188 | ||
189 | int OBJID::size_of() const | |
190 | { | |
191 | if (val_ptr == NULL) | |
192 | TTCN_error("Getting the size of an unbound objid value."); | |
193 | return val_ptr->n_components; | |
194 | } | |
195 | ||
196 | OBJID::operator const objid_element*() const | |
197 | { | |
198 | if (val_ptr == NULL) | |
199 | TTCN_error("Casting an unbound objid value to const int*."); | |
200 | return val_ptr->components_ptr; | |
201 | } | |
202 | ||
203 | OBJID::objid_element OBJID::from_INTEGER(const INTEGER& p_int) | |
204 | { | |
205 | int_val_t i_val = p_int.get_val(); | |
206 | if (i_val.is_negative()) { | |
207 | TTCN_error("An OBJECT IDENTIFIER component cannot be negative"); | |
208 | } | |
209 | if (!i_val.is_native()) { | |
210 | TTCN_error("The value of an OBJECT IDENTIFIER component cannot exceed %u", | |
211 | INT_MAX); | |
212 | } | |
213 | return (OBJID::objid_element)i_val.get_val(); | |
214 | } | |
215 | ||
216 | void OBJID::log() const | |
217 | { | |
218 | if (val_ptr != NULL) { | |
219 | TTCN_Logger::log_event_str("objid { "); | |
220 | for (int i = 0; i < val_ptr->n_components; i++) { | |
221 | if (i == val_ptr->overflow_idx) { | |
222 | TTCN_Logger::log_event_str("overflow:"); | |
223 | } | |
224 | ||
225 | TTCN_Logger::log_event(OBJID_FMT " ", val_ptr->components_ptr[i]); | |
226 | } | |
227 | TTCN_Logger::log_char('}'); | |
228 | } else TTCN_Logger::log_event_unbound(); | |
229 | } | |
230 | ||
231 | void OBJID::set_param(Module_Param& param) { | |
232 | param.basic_check(Module_Param::BC_VALUE, "objid value"); | |
3abe9331 | 233 | Module_Param_Ptr mp = ¶m; |
234 | if (param.get_type() == Module_Param::MP_Reference) { | |
235 | mp = param.get_referenced_param(); | |
236 | } | |
237 | if (mp->get_type()!=Module_Param::MP_Objid) param.type_error("objid value"); | |
970ed795 EL |
238 | if (sizeof(objid_element)!=sizeof(int)) TTCN_error("Internal error: OBJID::set_param()"); |
239 | clean_up(); | |
3abe9331 | 240 | init_struct(mp->get_string_size()); |
241 | memcpy(val_ptr->components_ptr, mp->get_string_data(), val_ptr->n_components * sizeof(objid_element)); | |
242 | } | |
243 | ||
244 | Module_Param* OBJID::get_param(Module_Param_Name& /* param_name */) const | |
245 | { | |
246 | if (!is_bound()) { | |
247 | return new Module_Param_Unbound(); | |
248 | } | |
249 | int* val_cpy = (int *)Malloc(val_ptr->n_components); | |
250 | memcpy(val_cpy, val_ptr->components_ptr, val_ptr->n_components * sizeof(int)); | |
251 | return new Module_Param_Objid(val_ptr->n_components, val_cpy); | |
970ed795 EL |
252 | } |
253 | ||
254 | void OBJID::encode_text(Text_Buf& text_buf) const | |
255 | { | |
256 | if (val_ptr == NULL) | |
257 | TTCN_error("Text encoder: Encoding an unbound objid value."); | |
258 | text_buf.push_int(val_ptr->n_components); | |
259 | for (int i = 0; i < val_ptr->n_components; i++) | |
260 | text_buf.push_int(val_ptr->components_ptr[i]); | |
261 | } | |
262 | ||
263 | void OBJID::decode_text(Text_Buf& text_buf) | |
264 | { | |
265 | int n_components = text_buf.pull_int().get_val(); | |
266 | if (n_components < 0) TTCN_error("Text decoder: Negative number of " | |
267 | "components was received for an objid value."); | |
268 | clean_up(); | |
269 | init_struct(n_components); | |
270 | for (int i = 0; i < n_components; i++) | |
271 | val_ptr->components_ptr[i] = text_buf.pull_int().get_val(); | |
272 | } | |
273 | ||
274 | void OBJID::encode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, | |
275 | TTCN_EncDec::coding_t p_coding, ...) const | |
276 | { | |
277 | va_list pvar; | |
278 | va_start(pvar, p_coding); | |
279 | switch(p_coding) { | |
280 | case TTCN_EncDec::CT_BER: { | |
281 | TTCN_EncDec_ErrorContext ec("While BER-encoding type '%s': ", p_td.name); | |
282 | unsigned BER_coding=va_arg(pvar, unsigned); | |
283 | BER_encode_chk_coding(BER_coding); | |
284 | ASN_BER_TLV_t *tlv=BER_encode_TLV(p_td, BER_coding); | |
285 | tlv->put_in_buffer(p_buf); | |
286 | ASN_BER_TLV_t::destruct(tlv); | |
287 | break;} | |
288 | case TTCN_EncDec::CT_RAW: { | |
289 | TTCN_EncDec_ErrorContext ec("While RAW-encoding type '%s': ", p_td.name); | |
290 | TTCN_EncDec_ErrorContext::error_internal | |
291 | ("No RAW descriptor available for type '%s'.", p_td.name); | |
292 | break;} | |
293 | case TTCN_EncDec::CT_XER: { | |
294 | TTCN_EncDec_ErrorContext ec("While XER-encoding type '%s': ", p_td.name); | |
295 | unsigned XER_coding=va_arg(pvar, unsigned); | |
af710487 | 296 | XER_encode(*p_td.xer, p_buf, XER_coding, 0, 0); |
297 | break;} | |
298 | case TTCN_EncDec::CT_JSON: { | |
299 | TTCN_EncDec_ErrorContext ec("While JSON-encoding type '%s': ", p_td.name); | |
300 | if(!p_td.json) | |
301 | TTCN_EncDec_ErrorContext::error_internal | |
302 | ("No JSON descriptor available for type '%s'.", p_td.name); | |
303 | JSON_Tokenizer tok(va_arg(pvar, int) != 0); | |
304 | JSON_encode(p_td, tok); | |
305 | p_buf.put_s(tok.get_buffer_length(), (const unsigned char*)tok.get_buffer()); | |
970ed795 EL |
306 | break;} |
307 | default: | |
308 | TTCN_error("Unknown coding method requested to encode type '%s'", | |
309 | p_td.name); | |
310 | } | |
311 | va_end(pvar); | |
312 | } | |
313 | ||
314 | void OBJID::decode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, | |
315 | TTCN_EncDec::coding_t p_coding, ...) | |
316 | { | |
317 | va_list pvar; | |
318 | va_start(pvar, p_coding); | |
319 | switch(p_coding) { | |
320 | case TTCN_EncDec::CT_BER: { | |
321 | TTCN_EncDec_ErrorContext ec("While BER-decoding type '%s': ", p_td.name); | |
322 | unsigned L_form=va_arg(pvar, unsigned); | |
323 | ASN_BER_TLV_t tlv; | |
324 | BER_decode_str2TLV(p_buf, tlv, L_form); | |
325 | BER_decode_TLV(p_td, tlv, L_form); | |
326 | if(tlv.isComplete) p_buf.increase_pos(tlv.get_len()); | |
327 | break;} | |
328 | case TTCN_EncDec::CT_RAW: { | |
329 | TTCN_EncDec_ErrorContext ec("While RAW-decoding type '%s': ", p_td.name); | |
330 | TTCN_EncDec_ErrorContext::error_internal | |
331 | ("No RAW descriptor available for type '%s'.", p_td.name); | |
332 | break;} | |
333 | case TTCN_EncDec::CT_XER: { | |
af710487 | 334 | TTCN_EncDec_ErrorContext ec("While XER-decoding type '%s': ", p_td.name); |
970ed795 EL |
335 | unsigned XER_coding=va_arg(pvar, unsigned); |
336 | XmlReaderWrap reader(p_buf); | |
337 | int success = reader.Read(); | |
338 | for (; success==1; success=reader.Read()) { | |
339 | int type = reader.NodeType(); | |
340 | if (type==XML_READER_TYPE_ELEMENT) | |
341 | break; | |
342 | } | |
feade998 | 343 | XER_decode(*p_td.xer, reader, XER_coding, XER_NONE, 0); |
970ed795 EL |
344 | size_t bytes = reader.ByteConsumed(); |
345 | p_buf.set_pos(bytes); | |
346 | break;} | |
af710487 | 347 | case TTCN_EncDec::CT_JSON: { |
348 | TTCN_EncDec_ErrorContext ec("While JSON-decoding type '%s': ", p_td.name); | |
349 | if(!p_td.json) | |
350 | TTCN_EncDec_ErrorContext::error_internal | |
351 | ("No JSON descriptor available for type '%s'.", p_td.name); | |
352 | JSON_Tokenizer tok((const char*)p_buf.get_data(), p_buf.get_len()); | |
353 | if(JSON_decode(p_td, tok, false)<0) | |
354 | ec.error(TTCN_EncDec::ET_INCOMPL_MSG, | |
355 | "Can not decode type '%s', because invalid or incomplete" | |
356 | " message was received" | |
357 | , p_td.name); | |
358 | p_buf.set_pos(tok.get_buf_pos()); | |
359 | break;} | |
970ed795 EL |
360 | default: |
361 | TTCN_error("Unknown coding method requested to decode type '%s'", | |
362 | p_td.name); | |
363 | } | |
364 | va_end(pvar); | |
365 | } | |
366 | ||
367 | ASN_BER_TLV_t* | |
368 | OBJID::BER_encode_TLV(const TTCN_Typedescriptor_t& p_td, | |
369 | unsigned p_coding) const | |
370 | { | |
371 | BER_chk_descr(p_td); | |
372 | ASN_BER_TLV_t *new_tlv=BER_encode_chk_bound(is_bound()); | |
373 | if(!new_tlv) { | |
374 | size_t V_len=0; | |
375 | switch(p_td.asnbasetype) { | |
376 | case TTCN_Typedescriptor_t::OBJID: | |
377 | if(val_ptr->n_components<2) | |
378 | TTCN_EncDec_ErrorContext::error_internal | |
379 | ("OBJID must have at least 2 components."); | |
380 | V_len=(min_needed_bits(val_ptr->components_ptr[0]*40 | |
381 | +val_ptr->components_ptr[1])+6)/7; | |
382 | for(int i=2; i<val_ptr->n_components; i++) | |
383 | V_len+=(min_needed_bits(val_ptr->components_ptr[i])+6)/7; | |
384 | break; | |
385 | case TTCN_Typedescriptor_t::ROID: | |
386 | for(int i=0; i<val_ptr->n_components; i++) | |
387 | V_len+=(min_needed_bits(val_ptr->components_ptr[i])+6)/7; | |
388 | break; | |
389 | default: | |
390 | TTCN_EncDec_ErrorContext::error_internal | |
391 | ("Missing/wrong basetype info for type '%s'.", p_td.name); | |
392 | } // switch | |
393 | new_tlv=ASN_BER_TLV_t::construct(V_len, NULL); | |
394 | unsigned char *Vptr=new_tlv->V.str.Vstr; | |
395 | for(int i=0; i<val_ptr->n_components; i++) { | |
396 | unsigned long ul; | |
397 | if(i==0 && p_td.asnbasetype==TTCN_Typedescriptor_t::OBJID) { | |
398 | ul=val_ptr->components_ptr[0]*40+val_ptr->components_ptr[1]; | |
399 | i++; | |
400 | } | |
401 | else ul=val_ptr->components_ptr[i]; | |
402 | size_t noo=(min_needed_bits(ul)+6)/7; | |
403 | for(size_t j=noo; j>0; j--) { | |
404 | Vptr[j-1]=(ul & 0x7F) | 0x80; | |
405 | ul>>=7; | |
406 | } | |
407 | Vptr[noo-1]&=0x7F; | |
408 | Vptr+=noo; | |
409 | } // for i | |
410 | } | |
411 | new_tlv=ASN_BER_V2TLV(new_tlv, p_td, p_coding); | |
412 | return new_tlv; | |
413 | } | |
414 | ||
415 | ||
416 | boolean OBJID::BER_decode_TLV(const TTCN_Typedescriptor_t& p_td, | |
417 | const ASN_BER_TLV_t& p_tlv, | |
418 | unsigned L_form) | |
419 | { | |
420 | clean_up(); | |
421 | BER_chk_descr(p_td); | |
422 | ASN_BER_TLV_t stripped_tlv; | |
423 | BER_decode_strip_tags(*p_td.ber, p_tlv, L_form, stripped_tlv); | |
424 | TTCN_EncDec_ErrorContext ec("While decoding OBJID type: "); | |
425 | stripped_tlv.chk_constructed_flag(FALSE); | |
426 | if (!stripped_tlv.isComplete) return FALSE; | |
427 | if (!stripped_tlv.V_tlvs_selected && stripped_tlv.V.str.Vlen==0) { | |
428 | ec.error(TTCN_EncDec::ET_INVAL_MSG, "Length of V-part is 0."); | |
429 | return FALSE; | |
430 | } | |
431 | switch(p_td.asnbasetype) { | |
432 | case TTCN_Typedescriptor_t::OBJID: | |
433 | case TTCN_Typedescriptor_t::ROID: | |
434 | break; | |
435 | default: | |
436 | TTCN_EncDec_ErrorContext::error_internal | |
437 | ("Missing/wrong basetype info for type '%s'.", p_td.name); | |
438 | } // switch | |
439 | unsigned char *Vptr=stripped_tlv.V.str.Vstr; | |
440 | boolean eoc=FALSE; // end-of-component | |
441 | int i=0; | |
442 | unsigned long long ull=0; | |
443 | STATIC_ASSERT(sizeof(ull) > sizeof(objid_element)); | |
444 | ||
445 | boolean err_repr=FALSE; | |
446 | while (Vptr < stripped_tlv.V.str.Vstr + stripped_tlv.V.str.Vlen) { | |
447 | ull |= *Vptr & 0x7F; | |
448 | if ((*Vptr & 0x80) && err_repr==FALSE) { // not-eoc | |
449 | if (ull & unsigned_llong_7msb) { | |
450 | ec.error(TTCN_EncDec::ET_REPR, | |
451 | "Value of the #%d component is too big.", i+1); | |
452 | err_repr=TRUE; | |
453 | } | |
454 | ull<<=7; | |
455 | eoc=FALSE; | |
456 | } | |
457 | else { // eoc | |
458 | if (i==0 && p_td.asnbasetype==TTCN_Typedescriptor_t::OBJID) { | |
459 | // first two component of objid | |
460 | switch(ull/40ul) { | |
461 | case 0: | |
462 | (*this)[0]=0; break; | |
463 | case 1: | |
464 | (*this)[0]=1; break; | |
465 | default: | |
466 | (*this)[0]=2; break; | |
467 | } | |
468 | (*this)[1]=(int)(ull-40*(*this)[0]); | |
469 | i=1; | |
470 | } | |
471 | else { // other components (>2) | |
472 | // objid_element is UINT/ULONG; the result of the cast is Uxxx_MAX. | |
473 | // It's computed at compile time. | |
474 | if(ull > ((objid_element)-1)) { | |
475 | if(err_repr==FALSE) | |
476 | ec.error(TTCN_EncDec::ET_REPR, | |
477 | "Value of the #%d component is too big.", i+1); | |
478 | (*this)[i]=(objid_element)-1; | |
479 | // remember the first overflow | |
480 | if (val_ptr->overflow_idx < 0) val_ptr->overflow_idx = i; | |
481 | } // if ul too big | |
482 | else | |
483 | (*this)[i]=(objid_element)ull; | |
484 | } | |
485 | err_repr=FALSE; | |
486 | ull=0; | |
487 | eoc=TRUE; | |
488 | i++; | |
489 | } // eoc | |
490 | Vptr++; | |
491 | } // while Vptr... | |
492 | if(eoc==FALSE) | |
493 | ec.error(TTCN_EncDec::ET_INVAL_MSG, | |
494 | "The last component (#%d) is unterminated.", i+1); | |
495 | return TRUE; | |
496 | } | |
497 | ||
498 | ||
499 | int OBJID::XER_encode(const XERdescriptor_t& p_td, | |
af710487 | 500 | TTCN_Buffer& p_buf, unsigned int flavor, int indent, embed_values_enc_struct_t*) const |
970ed795 EL |
501 | { |
502 | if(!is_bound()) { | |
503 | TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND, | |
504 | "Encoding an unbound object identifier value."); | |
505 | } | |
506 | int encoded_length=(int)p_buf.get_len(); | |
507 | ||
508 | flavor |= SIMPLE_TYPE; | |
509 | flavor &= ~XER_RECOF; // object identifier doesn't care | |
510 | begin_xml(p_td, p_buf, flavor, indent, false); | |
511 | ||
512 | static char str_buf[64]; | |
513 | for (int i = 0; i < val_ptr->n_components; ++i ) { | |
514 | // output dot before the second and subsequent components | |
515 | if (i > 0) p_buf.put_c('.'); | |
516 | // output current component | |
517 | int str_len = snprintf(str_buf, sizeof(str_buf), OBJID_FMT, | |
518 | val_ptr->components_ptr[i]); | |
519 | if (str_len < 0 || str_len >= (int)sizeof(str_buf)) { | |
520 | TTCN_error("Internal error: system call snprintf() returned " | |
521 | "unexpected status code %d when converting value " OBJID_FMT, | |
522 | str_len, val_ptr->components_ptr[i]); | |
523 | } | |
524 | else p_buf.put_s(str_len, (const unsigned char*)str_buf); | |
525 | } | |
526 | ||
527 | end_xml(p_td, p_buf, flavor, indent, false); | |
528 | ||
529 | return (int)p_buf.get_len() - encoded_length; | |
530 | } | |
531 | ||
af710487 | 532 | void OBJID::from_string(char* p_str) |
533 | { | |
534 | // Count dots to find number of components. (1 dot = 2 components, etc.) | |
535 | unsigned comps = 1; | |
536 | const char *p; | |
537 | for (p = p_str; *p != 0; ++p) { | |
538 | if (*p == '.') ++comps; | |
539 | } | |
540 | // p now points at the end of the string. If it was empty, then there were | |
541 | // no components; compensate the fact that we started at 1. | |
542 | init_struct((p != p_str) ? comps : 0); | |
543 | ||
544 | char *beg, *end = 0; | |
545 | comps = 0; | |
546 | for (beg = p_str; beg < p; ++beg) { | |
547 | errno = 0; | |
548 | long ret = strtol(beg, &end, 10); | |
549 | if (errno) break; | |
550 | ||
551 | // TODO check value for too big ? | |
552 | (*this)[comps++] = ret; | |
553 | beg = end; // move to the dot; will move past it when incremented | |
554 | } | |
555 | } | |
556 | ||
970ed795 | 557 | int OBJID::XER_decode(const XERdescriptor_t& p_td, XmlReaderWrap& reader, |
feade998 | 558 | unsigned int flavor, unsigned int /*flavor2*/, embed_values_dec_struct_t*) |
970ed795 EL |
559 | { |
560 | int exer = is_exer(flavor); | |
561 | int success = reader.Ok(), depth = -1; | |
562 | for (; success == 1; success = reader.Read()) { | |
563 | int type = reader.NodeType(); | |
564 | if (XML_READER_TYPE_ELEMENT == type) { | |
565 | verify_name(reader, p_td, exer); | |
566 | depth = reader.Depth(); | |
567 | break; | |
568 | } | |
569 | } | |
570 | if (success == 1) { | |
571 | char * val = (char *)reader.ReadString(); // We own this (writable) string | |
572 | if (0 == val) { | |
573 | TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INVAL_MSG, "Bogus object identifier"); | |
574 | return 0; | |
575 | } | |
af710487 | 576 | |
577 | from_string(val); | |
970ed795 EL |
578 | |
579 | xmlFree(val); | |
580 | } | |
581 | for (success = reader.Read(); success == 1; success = reader.Read()) { | |
582 | int type = reader.NodeType(); | |
583 | if (XML_READER_TYPE_END_ELEMENT == type) { | |
584 | verify_end(reader, p_td, depth, exer); | |
585 | reader.Read(); | |
586 | break; | |
587 | } | |
588 | } | |
589 | return 1; // decode successful | |
590 | } | |
591 | ||
af710487 | 592 | int OBJID::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) const |
593 | { | |
594 | if (!is_bound()) { | |
595 | TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND, | |
596 | "Encoding an unbound object identifier value."); | |
597 | return -1; | |
598 | } | |
599 | ||
600 | char* objid_str = mcopystrn("\"", 1); | |
601 | for (int i = 0; i < val_ptr->n_components; ++i) { | |
602 | objid_str = mputprintf(objid_str, "%s" OBJID_FMT, (i > 0 ? "." : ""), val_ptr->components_ptr[i]); | |
603 | } | |
604 | objid_str = mputstrn(objid_str, "\"", 1); | |
605 | int enc_len = p_tok.put_next_token(JSON_TOKEN_STRING, objid_str); | |
606 | Free(objid_str); | |
607 | return enc_len; | |
608 | } | |
609 | ||
610 | int OBJID::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) | |
611 | { | |
612 | json_token_t token = JSON_TOKEN_NONE; | |
613 | char* value = 0; | |
614 | size_t value_len = 0; | |
615 | boolean error = false; | |
616 | int dec_len = 0; | |
617 | boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); | |
618 | if (use_default) { | |
619 | // No JSON data in the buffer -> use default value | |
620 | value = (char*)p_td.json->default_value; | |
621 | value_len = strlen(value); | |
622 | } else { | |
623 | dec_len = p_tok.get_next_token(&token, &value, &value_len); | |
624 | } | |
625 | if (JSON_TOKEN_ERROR == token) { | |
626 | JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, ""); | |
627 | return JSON_ERROR_FATAL; | |
628 | } | |
629 | else if (JSON_TOKEN_STRING == token || use_default) { | |
630 | if (use_default || (value_len > 2 && value[0] == '\"' && value[value_len - 1] == '\"')) { | |
631 | if (!use_default) { | |
632 | // The default value doesn't have quotes around it | |
633 | value_len -= 2; | |
634 | ++value; | |
635 | } | |
636 | // need a null-terminated string | |
637 | char* value2 = mcopystrn(value, value_len); | |
638 | from_string(value2); | |
639 | Free(value2); | |
640 | } | |
641 | } | |
642 | else { | |
643 | return JSON_ERROR_INVALID_TOKEN; | |
644 | } | |
645 | ||
646 | if (error) { | |
647 | JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FORMAT_ERROR, "string", "object identifier"); | |
648 | if (p_silent) { | |
649 | clean_up(); | |
650 | } | |
651 | return JSON_ERROR_FATAL; | |
652 | } | |
653 | return dec_len; | |
654 | } | |
970ed795 EL |
655 | |
656 | void OBJID_template::clean_up() | |
657 | { | |
658 | if (template_selection == VALUE_LIST || | |
659 | template_selection == COMPLEMENTED_LIST) delete [] value_list.list_value; | |
660 | template_selection = UNINITIALIZED_TEMPLATE; | |
661 | } | |
662 | ||
663 | void OBJID_template::copy_template(const OBJID_template& other_value) | |
664 | { | |
665 | switch (other_value.template_selection) { | |
666 | case SPECIFIC_VALUE: | |
667 | single_value = other_value.single_value; | |
668 | break; | |
669 | case OMIT_VALUE: | |
670 | case ANY_VALUE: | |
671 | case ANY_OR_OMIT: | |
672 | break; | |
673 | case VALUE_LIST: | |
674 | case COMPLEMENTED_LIST: | |
675 | value_list.n_values = other_value.value_list.n_values; | |
676 | value_list.list_value = new OBJID_template[value_list.n_values]; | |
677 | for (unsigned int i = 0; i < value_list.n_values; i++) | |
678 | value_list.list_value[i].copy_template( | |
679 | other_value.value_list.list_value[i]); | |
680 | break; | |
681 | default: | |
682 | TTCN_error("Copying an uninitialized/unsupported objid template."); | |
683 | } | |
684 | set_selection(other_value); | |
685 | } | |
686 | ||
687 | OBJID_template::OBJID_template() | |
688 | { | |
689 | ||
690 | } | |
691 | ||
692 | OBJID_template::OBJID_template(template_sel other_value) | |
693 | : Base_Template(other_value) | |
694 | { | |
695 | check_single_selection(other_value); | |
696 | } | |
697 | ||
698 | OBJID_template::OBJID_template(const OBJID& other_value) | |
699 | : Base_Template(SPECIFIC_VALUE), single_value(other_value) | |
700 | { | |
701 | ||
702 | } | |
703 | ||
704 | OBJID_template::OBJID_template(const OPTIONAL<OBJID>& other_value) | |
705 | { | |
706 | switch (other_value.get_selection()) { | |
707 | case OPTIONAL_PRESENT: | |
708 | set_selection(SPECIFIC_VALUE); | |
709 | single_value = (const OBJID&)other_value; | |
710 | break; | |
711 | case OPTIONAL_OMIT: | |
712 | set_selection(OMIT_VALUE); | |
713 | break; | |
714 | default: | |
715 | TTCN_error("Creating an objid template from an unbound optional field."); | |
716 | } | |
717 | } | |
718 | ||
719 | OBJID_template::OBJID_template(const OBJID_template& other_value) | |
720 | : Base_Template() | |
721 | { | |
722 | copy_template(other_value); | |
723 | } | |
724 | ||
725 | OBJID_template::~OBJID_template() | |
726 | { | |
727 | clean_up(); | |
728 | } | |
729 | ||
730 | OBJID_template& OBJID_template::operator=(template_sel other_value) | |
731 | { | |
732 | check_single_selection(other_value); | |
733 | clean_up(); | |
734 | set_selection(other_value); | |
735 | return *this; | |
736 | } | |
737 | ||
738 | OBJID_template& OBJID_template::operator=(const OBJID& other_value) | |
739 | { | |
740 | if (!other_value.is_bound()) | |
741 | TTCN_error("Assignment of an unbound objid value to a template."); | |
742 | clean_up(); | |
743 | set_selection(SPECIFIC_VALUE); | |
744 | single_value = other_value; | |
745 | return *this; | |
746 | } | |
747 | ||
748 | OBJID_template& OBJID_template::operator=(const OPTIONAL<OBJID>& other_value) | |
749 | { | |
750 | clean_up(); | |
751 | switch (other_value.get_selection()) { | |
752 | case OPTIONAL_PRESENT: | |
753 | set_selection(SPECIFIC_VALUE); | |
754 | single_value = (const OBJID&)other_value; | |
755 | break; | |
756 | case OPTIONAL_OMIT: | |
757 | set_selection(OMIT_VALUE); | |
758 | break; | |
759 | default: | |
760 | TTCN_error("Assignment of an unbound optional field to an objid template."); | |
761 | } | |
762 | return *this; | |
763 | } | |
764 | ||
765 | OBJID_template& OBJID_template::operator=(const OBJID_template& other_value) | |
766 | { | |
767 | if (&other_value != this) { | |
768 | clean_up(); | |
769 | copy_template(other_value); | |
770 | } | |
771 | return *this; | |
772 | } | |
773 | ||
3abe9331 | 774 | boolean OBJID_template::match(const OBJID& other_value, boolean /* legacy */) const |
970ed795 EL |
775 | { |
776 | if (!other_value.is_bound()) return FALSE; | |
777 | switch (template_selection) { | |
778 | case SPECIFIC_VALUE: | |
779 | return single_value == other_value; | |
780 | case OMIT_VALUE: | |
781 | return FALSE; | |
782 | case ANY_VALUE: | |
783 | case ANY_OR_OMIT: | |
784 | return TRUE; | |
785 | case VALUE_LIST: | |
786 | case COMPLEMENTED_LIST: | |
787 | for (unsigned int i = 0; i < value_list.n_values; i++) | |
788 | if (value_list.list_value[i].match(other_value)) | |
789 | return template_selection == VALUE_LIST; | |
790 | return template_selection == COMPLEMENTED_LIST; | |
791 | default: | |
792 | TTCN_error("Matching with an uninitialized/unsupported objid template."); | |
793 | } | |
794 | return FALSE; | |
795 | } | |
796 | ||
797 | const OBJID& OBJID_template::valueof() const | |
798 | { | |
799 | if (template_selection != SPECIFIC_VALUE || is_ifpresent) | |
800 | TTCN_error("Performing a valueof " | |
801 | "or send operation on a non-specific objid template."); | |
802 | return single_value; | |
803 | } | |
804 | ||
805 | int OBJID_template::size_of() const | |
806 | { | |
807 | switch (template_selection) | |
808 | { | |
809 | case SPECIFIC_VALUE: | |
810 | return single_value.size_of(); | |
811 | case OMIT_VALUE: | |
812 | TTCN_error("Performing sizeof() operation on an objid template " | |
813 | "containing omit value."); | |
814 | case ANY_VALUE: | |
815 | case ANY_OR_OMIT: | |
816 | TTCN_error("Performing sizeof() operation on a */? objid template."); | |
817 | case VALUE_LIST: | |
818 | { | |
819 | if (value_list.n_values<1) | |
820 | TTCN_error("Internal error: " | |
821 | "Performing sizeof() operation on an objid template " | |
822 | "containing an empty list."); | |
823 | int item_size = value_list.list_value[0].size_of(); | |
824 | for (unsigned int i = 1; i < value_list.n_values; i++) { | |
825 | if (value_list.list_value[i].size_of()!=item_size) | |
826 | TTCN_error("Performing sizeof() operation on an objid template " | |
827 | "containing a value list with different sizes."); | |
828 | } | |
829 | return item_size; | |
830 | } | |
831 | case COMPLEMENTED_LIST: | |
832 | TTCN_error("Performing sizeof() operation on an objid template " | |
833 | "containing complemented list."); | |
834 | default: | |
835 | TTCN_error("Performing sizeof() operation on an " | |
836 | "uninitialized/unsupported objid template."); | |
837 | } | |
838 | return 0; | |
839 | } | |
840 | ||
841 | void OBJID_template::set_type(template_sel template_type, | |
842 | unsigned int list_length) | |
843 | { | |
844 | if (template_type != VALUE_LIST && template_type != COMPLEMENTED_LIST) | |
845 | TTCN_error("Setting an invalid list type for an objid template."); | |
846 | clean_up(); | |
847 | set_selection(template_type); | |
848 | value_list.n_values = list_length; | |
849 | value_list.list_value = new OBJID_template[list_length]; | |
850 | } | |
851 | ||
852 | OBJID_template& OBJID_template::list_item(unsigned int list_index) | |
853 | { | |
854 | if (template_selection != VALUE_LIST && | |
855 | template_selection != COMPLEMENTED_LIST) | |
856 | TTCN_error("Accessing a list element of a non-list objid template."); | |
857 | if (list_index >= value_list.n_values) | |
858 | TTCN_error("Index overflow in an objid value list template."); | |
859 | return value_list.list_value[list_index]; | |
860 | } | |
861 | ||
862 | void OBJID_template::log() const | |
863 | { | |
864 | switch (template_selection) { | |
865 | case SPECIFIC_VALUE: | |
866 | single_value.log(); | |
867 | break; | |
868 | case COMPLEMENTED_LIST: | |
869 | TTCN_Logger::log_event_str("complement "); | |
870 | // no break | |
871 | case VALUE_LIST: | |
872 | TTCN_Logger::log_char('('); | |
873 | for(unsigned int i = 0; i < value_list.n_values; i++) { | |
874 | if (i > 0) TTCN_Logger::log_event_str(", "); | |
875 | value_list.list_value[i].log(); | |
876 | } | |
877 | TTCN_Logger::log_char(')'); | |
878 | break; | |
879 | default: | |
880 | log_generic(); | |
881 | break; | |
882 | } | |
883 | log_ifpresent(); | |
884 | } | |
885 | ||
3abe9331 | 886 | void OBJID_template::log_match(const OBJID& match_value, |
887 | boolean /* legacy */) const | |
970ed795 EL |
888 | { |
889 | if (TTCN_Logger::VERBOSITY_COMPACT == TTCN_Logger::get_matching_verbosity() | |
890 | && TTCN_Logger::get_logmatch_buffer_len() != 0) { | |
891 | TTCN_Logger::print_logmatch_buffer(); | |
892 | TTCN_Logger::log_event_str(" := "); | |
893 | } | |
894 | match_value.log(); | |
895 | TTCN_Logger::log_event_str(" with "); | |
896 | log(); | |
897 | if (match(match_value)) TTCN_Logger::log_event_str(" matched"); | |
898 | else TTCN_Logger::log_event_str(" unmatched"); | |
899 | } | |
900 | ||
901 | void OBJID_template::set_param(Module_Param& param) { | |
902 | param.basic_check(Module_Param::BC_TEMPLATE, "objid template"); | |
3abe9331 | 903 | Module_Param_Ptr mp = ¶m; |
904 | if (param.get_type() == Module_Param::MP_Reference) { | |
905 | mp = param.get_referenced_param(); | |
906 | } | |
907 | switch (mp->get_type()) { | |
970ed795 EL |
908 | case Module_Param::MP_Omit: |
909 | *this = OMIT_VALUE; | |
910 | break; | |
911 | case Module_Param::MP_Any: | |
912 | *this = ANY_VALUE; | |
913 | break; | |
914 | case Module_Param::MP_AnyOrNone: | |
915 | *this = ANY_OR_OMIT; | |
916 | break; | |
917 | case Module_Param::MP_List_Template: | |
3abe9331 | 918 | case Module_Param::MP_ComplementList_Template: { |
919 | OBJID_template temp; | |
920 | temp.set_type(mp->get_type() == Module_Param::MP_List_Template ? | |
921 | VALUE_LIST : COMPLEMENTED_LIST, mp->get_size()); | |
922 | for (size_t i=0; i<mp->get_size(); i++) { | |
923 | temp.list_item(i).set_param(*mp->get_elem(i)); | |
970ed795 | 924 | } |
3abe9331 | 925 | *this = temp; |
926 | break; } | |
970ed795 EL |
927 | case Module_Param::MP_Objid: |
928 | if (sizeof(OBJID::objid_element)!=sizeof(int)) TTCN_error("Internal error: OBJID_template::set_param()"); | |
3abe9331 | 929 | *this = OBJID(mp->get_string_size(), (OBJID::objid_element*)mp->get_string_data()); |
970ed795 EL |
930 | break; |
931 | //case Module_Param::MP_Objid_Template: | |
932 | // TODO | |
933 | //break; | |
934 | default: | |
935 | param.type_error("objid template"); | |
936 | } | |
3abe9331 | 937 | is_ifpresent = param.get_ifpresent() || mp->get_ifpresent(); |
938 | } | |
939 | ||
940 | Module_Param* OBJID_template::get_param(Module_Param_Name& param_name) const | |
941 | { | |
942 | Module_Param* mp = NULL; | |
943 | switch (template_selection) { | |
944 | case UNINITIALIZED_TEMPLATE: | |
945 | mp = new Module_Param_Unbound(); | |
946 | break; | |
947 | case OMIT_VALUE: | |
948 | mp = new Module_Param_Omit(); | |
949 | break; | |
950 | case ANY_VALUE: | |
951 | mp = new Module_Param_Any(); | |
952 | break; | |
953 | case ANY_OR_OMIT: | |
954 | mp = new Module_Param_AnyOrNone(); | |
955 | break; | |
956 | case SPECIFIC_VALUE: | |
957 | mp = single_value.get_param(param_name); | |
958 | break; | |
959 | case VALUE_LIST: | |
960 | case COMPLEMENTED_LIST: { | |
961 | if (template_selection == VALUE_LIST) { | |
962 | mp = new Module_Param_List_Template(); | |
963 | } | |
964 | else { | |
965 | mp = new Module_Param_ComplementList_Template(); | |
966 | } | |
967 | for (size_t i = 0; i < value_list.n_values; ++i) { | |
968 | mp->add_elem(value_list.list_value[i].get_param(param_name)); | |
969 | } | |
970 | break; } | |
971 | default: | |
972 | break; | |
973 | } | |
974 | if (is_ifpresent) { | |
975 | mp->set_ifpresent(); | |
976 | } | |
977 | return mp; | |
970ed795 EL |
978 | } |
979 | ||
980 | void OBJID_template::encode_text(Text_Buf& text_buf) const | |
981 | { | |
982 | encode_text_base(text_buf); | |
983 | switch (template_selection) { | |
984 | case OMIT_VALUE: | |
985 | case ANY_VALUE: | |
986 | case ANY_OR_OMIT: | |
987 | break; | |
988 | case SPECIFIC_VALUE: | |
989 | single_value.encode_text(text_buf); | |
990 | break; | |
991 | case VALUE_LIST: | |
992 | case COMPLEMENTED_LIST: | |
993 | text_buf.push_int(value_list.n_values); | |
994 | for (unsigned int i = 0; i < value_list.n_values; i++) | |
995 | value_list.list_value[i].encode_text(text_buf); | |
996 | break; | |
997 | default: | |
998 | TTCN_error("Text encoder: Encoding an undefined/unsupported objid " | |
999 | "template."); | |
1000 | } | |
1001 | } | |
1002 | ||
1003 | void OBJID_template::decode_text(Text_Buf& text_buf) | |
1004 | { | |
1005 | clean_up(); | |
1006 | decode_text_base(text_buf); | |
1007 | switch (template_selection) { | |
1008 | case OMIT_VALUE: | |
1009 | case ANY_VALUE: | |
1010 | case ANY_OR_OMIT: | |
1011 | break; | |
1012 | case SPECIFIC_VALUE: | |
1013 | single_value.decode_text(text_buf); | |
1014 | break; | |
1015 | case VALUE_LIST: | |
1016 | case COMPLEMENTED_LIST: | |
1017 | value_list.n_values = text_buf.pull_int().get_val(); | |
1018 | value_list.list_value = new OBJID_template[value_list.n_values]; | |
1019 | for (unsigned int i = 0; i < value_list.n_values; i++) | |
1020 | value_list.list_value[i].decode_text(text_buf); | |
1021 | break; | |
1022 | default: | |
1023 | TTCN_error("Text decoder: An unknown/unsupported selection was " | |
1024 | "received for an objid template."); | |
1025 | } | |
1026 | } | |
1027 | ||
3abe9331 | 1028 | boolean OBJID_template::is_present(boolean legacy /* = FALSE */) const |
970ed795 EL |
1029 | { |
1030 | if (template_selection==UNINITIALIZED_TEMPLATE) return FALSE; | |
3abe9331 | 1031 | return !match_omit(legacy); |
970ed795 EL |
1032 | } |
1033 | ||
3abe9331 | 1034 | boolean OBJID_template::match_omit(boolean legacy /* = FALSE */) const |
970ed795 EL |
1035 | { |
1036 | if (is_ifpresent) return TRUE; | |
1037 | switch (template_selection) { | |
1038 | case OMIT_VALUE: | |
1039 | case ANY_OR_OMIT: | |
1040 | return TRUE; | |
1041 | case VALUE_LIST: | |
1042 | case COMPLEMENTED_LIST: | |
3abe9331 | 1043 | if (legacy) { |
1044 | // legacy behavior: 'omit' can appear in the value/complement list | |
1045 | for (unsigned int i=0; i<value_list.n_values; i++) | |
1046 | if (value_list.list_value[i].match_omit()) | |
1047 | return template_selection==VALUE_LIST; | |
1048 | return template_selection==COMPLEMENTED_LIST; | |
1049 | } | |
1050 | // else fall through | |
970ed795 EL |
1051 | default: |
1052 | return FALSE; | |
1053 | } | |
1054 | return FALSE; | |
1055 | } | |
1056 | ||
1057 | #ifndef TITAN_RUNTIME_2 | |
3abe9331 | 1058 | void OBJID_template::check_restriction(template_res t_res, const char* t_name, |
1059 | boolean legacy /* = FALSE */) const | |
970ed795 EL |
1060 | { |
1061 | if (template_selection==UNINITIALIZED_TEMPLATE) return; | |
1062 | switch ((t_name&&(t_res==TR_VALUE))?TR_OMIT:t_res) { | |
1063 | case TR_VALUE: | |
1064 | if (!is_ifpresent && template_selection==SPECIFIC_VALUE) return; | |
1065 | break; | |
1066 | case TR_OMIT: | |
1067 | if (!is_ifpresent && (template_selection==OMIT_VALUE || | |
1068 | template_selection==SPECIFIC_VALUE)) return; | |
1069 | break; | |
1070 | case TR_PRESENT: | |
3abe9331 | 1071 | if (!match_omit(legacy)) return; |
970ed795 EL |
1072 | break; |
1073 | default: | |
1074 | return; | |
1075 | } | |
1076 | TTCN_error("Restriction `%s' on template of type %s violated.", | |
1077 | get_res_name(t_res), t_name ? t_name : "objid"); | |
1078 | } | |
1079 | #endif |