blob: d12aae1cc81adf0407991743c3921f521e74f956 [file] [log] [blame]
Lev Walkinf15320b2004-06-03 03:38:44 +00001%{
2
3#include <string.h>
4#include <errno.h>
5#include <assert.h>
6
7#include "asn1parser.h"
8#include "asn1p_y.h"
9
10int asn1p_lex(void);
11void asn1p_lexer_hack_push_opaque_state(void); /* Used in .y */
12void asn1p_lexer_hack_enable_with_syntax(void); /* Used in .y */
Lev Walkinf59d0752004-08-18 04:59:12 +000013void asn1p_lexer_hack_push_encoding_control(void); /* Used in .y */
Lev Walkinf15320b2004-06-03 03:38:44 +000014
15#define YY_FATAL_ERROR(msg) do { \
16 fprintf(stderr, \
17 "lexer error at line %d, " \
18 "text \"%s\"\n", \
19 yylineno, yytext); \
20 exit(1); \
21 } while(0)
22
23int asn1p_lexer_pedantic_1990 = 0;
24int asn1p_lexer_types_year = 0;
25int asn1p_lexer_constructs_year = 0;
Lev Walkinc603f102005-01-23 09:51:44 +000026static asn1c_integer_t asn1p_atoi(char *ptr); /* errno is either 0 or ERANGE */
Lev Walkinf15320b2004-06-03 03:38:44 +000027
Lev Walkin4696c742005-08-22 12:23:54 +000028int asn1p_as_pointer;
29
Lev Walkinf15320b2004-06-03 03:38:44 +000030/*
31 * Check that the type is defined in the year of the standard choosen.
32 */
33#define TYPE_LIFETIME(fyr, lyr) \
34 (!asn1p_lexer_types_year \
35 || (fyr && fyr <= asn1p_lexer_types_year) \
36 || (lyr && lyr > asn1p_lexer_types_year))
37
38/*
39 * Check the the construction (or concept, i.e. CLASS) is defined in
40 * a given year.
41 */
42#define CONSTRUCT_LIFETIME(fyr, lyr) \
43 (!asn1p_lexer_constructs_year \
44 || (fyr && fyr <= asn1p_lexer_constructs_year) \
45 || (lyr && lyr > asn1p_lexer_constructs_year))
46
47/*
Lev Walkinf15320b2004-06-03 03:38:44 +000048 * Append quoted string.
49 */
50#define QAPPEND(text, tlen) do { \
51 char *prev_text = asn1p_lval.tv_opaque.buf; \
52 int prev_len = asn1p_lval.tv_opaque.len; \
53 char *p; \
54 \
55 p = malloc((tlen) + prev_len + 1); \
56 if(p == NULL) return -1; \
57 \
58 if(prev_text) memcpy(p, prev_text, prev_len); \
59 memcpy(p + prev_len, text, tlen); \
60 p[prev_len + (tlen)] = '\0'; \
61 \
62 free(asn1p_lval.tv_opaque.buf); \
63 asn1p_lval.tv_opaque.buf = p; \
64 asn1p_lval.tv_opaque.len = (tlen) + prev_len; \
65 } while(0)
66
67%}
68
69%option never-interactive
Lev Walkinf59d0752004-08-18 04:59:12 +000070%option noinput
Lev Walkinf15320b2004-06-03 03:38:44 +000071%option noyywrap stack
72/* Performance penalty is OK */
73%option yylineno
74/* Controlled from within application */
75%option debug
76
77%pointer
78
79%x dash_comment
Lev Walkin2535a692005-07-02 21:42:40 +000080%x idash_comment
Lev Walkinf15320b2004-06-03 03:38:44 +000081%x cpp_comment
82%x quoted
83%x opaque
Lev Walkinf59d0752004-08-18 04:59:12 +000084%x encoding_control
Lev Walkinf15320b2004-06-03 03:38:44 +000085%x with_syntax
86
87/* Newline */
88NL [\r\v\f\n]
89/* White-space */
90WSP [\t\r\v\f\n ]
91
92%%
93
Lev Walkin2535a692005-07-02 21:42:40 +000094-{3,}/[\r\n] /* Immediately terminated long comment */
95-{3,}/[^-\r\n] yy_push_state(idash_comment); /* Incorrect, but acceptable */
96<idash_comment>{
97 -{3,} yy_pop_state(); /* Acceptable end of comment */
98}
99
100-- yy_push_state(dash_comment);
101<dash_comment,idash_comment>{
Lev Walkinf15320b2004-06-03 03:38:44 +0000102
103 {NL} yy_pop_state();
104
Lev Walkin4696c742005-08-22 12:23:54 +0000105 "<asn1c:"[^\r\v\f\n>-]{1,80}">" {
106 if(strcmp(yytext, "<asn1c:pointer>") == 0)
107 asn1p_as_pointer = 1;
108 /* Eat modifier */
109 }
110 "<" /* Eat stand-alone left angle brace */
111
Lev Walkinf15320b2004-06-03 03:38:44 +0000112 -- yy_pop_state(); /* End of comment */
113 - /* Eat single dash */
Lev Walkin4696c742005-08-22 12:23:54 +0000114 [^\r\v\f\n<-]+ /* Eat */
Lev Walkinf15320b2004-06-03 03:38:44 +0000115}
Lev Walkin2535a692005-07-02 21:42:40 +0000116
Lev Walkinf15320b2004-06-03 03:38:44 +0000117<INITIAL,cpp_comment>"/*" yy_push_state(cpp_comment);
118<cpp_comment>{
Lev Walkin4696c742005-08-22 12:23:54 +0000119 [^*/<] /* Eat */
Lev Walkinf15320b2004-06-03 03:38:44 +0000120 "*/" yy_pop_state();
Lev Walkin4696c742005-08-22 12:23:54 +0000121 "<asn1c:"[^\r\v\f\n>-]{1,80}">" {
122 if(strcmp(yytext, "<asn1c:pointer>") == 0)
123 asn1p_as_pointer = 1;
124 /* Eat modifier */
125 }
Lev Walkinf15320b2004-06-03 03:38:44 +0000126 . /* Eat */
127}
128
129
130 /*
131 * This is state is being set from corresponding .y module when
132 * higher-level data is necessary to make proper parsing of the
133 * underlying data. Thus, we enter the <opaque> state and save
134 * everything for later processing.
135 */
136<opaque>{
137
138 "{" {
139 yy_push_state(opaque);
140 asn1p_lval.tv_opaque.buf = strdup(yytext);
141 asn1p_lval.tv_opaque.len = yyleng;
142 return TOK_opaque;
143 }
144
145 "}" {
146 yy_pop_state();
147 asn1p_lval.tv_opaque.buf = strdup(yytext);
148 asn1p_lval.tv_opaque.len = yyleng;
149 return TOK_opaque;
150 }
151
152 [^{}:=]+ {
153 asn1p_lval.tv_opaque.buf = strdup(yytext);
154 asn1p_lval.tv_opaque.len = yyleng;
155 return TOK_opaque;
156 }
157
158 "::=" {
159 fprintf(stderr,
160 "ASN.1 Parser syncronization failure: "
161 "\"%s\" at line %d must not appear "
162 "inside value definition\n",
163 yytext, yylineno);
164 return -1;
165 }
166
167 [:=] {
168 asn1p_lval.tv_opaque.buf = strdup(yytext);
169 asn1p_lval.tv_opaque.len = yyleng;
170 return TOK_opaque;
171 }
172
173 }
174
175\"[^\"]* {
176 asn1p_lval.tv_opaque.buf = 0;
177 asn1p_lval.tv_opaque.len = 0;
178 QAPPEND(yytext+1, yyleng-1);
179 yy_push_state(quoted);
180 }
181<quoted>{
182
183 \"\" { QAPPEND(yytext, yyleng-1); } /* Add a single quote */
184 [^\"]+ { QAPPEND(yytext, yyleng); }
185
186 \" {
187 yy_pop_state();
188 /* Do not append last quote:
189 // QAPPEND(yytext, yyleng); */
190
191 if(asn1p_lexer_pedantic_1990
192 && strchr(yytext, '\n')) {
193 fprintf(stderr, "%s: "
194 "Newlines are prohibited by ASN.1:1990\n",
195 asn1p_lval.tv_opaque.buf);
196 return -1;
197 }
198
199 return TOK_cstring;
200 }
201
202 }
203
Lev Walkinf59d0752004-08-18 04:59:12 +0000204<encoding_control>{
205 ENCODING-CONTROL {
206 const char *s = "ENCODING-CONTROL";
207 const char *p = s + sizeof("ENCODING-CONTROL") - 2;
208 for(; p >= s; p--) unput(*p);
209 yy_pop_state();
210 }
211 END unput('D'); unput('N'); unput('E'); yy_pop_state();
212 [^{} \t\r\v\f\n]+
213 [[:alnum:]]+
214 . /* Eat everything else */
215 "\n"
216 }
Lev Walkinf15320b2004-06-03 03:38:44 +0000217
218'[0-9A-F \t\r\v\f\n]+'H {
219 /* " \t\r\n" weren't allowed in ASN.1:1990. */
220 asn1p_lval.tv_str = yytext;
221 return TOK_hstring;
222 }
223
224'[01 \t\r\v\f\n]+'B {
225 /* " \t\r\n" weren't allowed in ASN.1:1990. */
226 asn1p_lval.tv_str = strdup(yytext);
227 return TOK_bstring;
228 }
229
230
231-[1-9][0-9]* {
232 asn1p_lval.a_int = asn1p_atoi(yytext);
233 if(errno == ERANGE)
234 return -1;
235 return TOK_number_negative;
236 }
237
238[1-9][0-9]* {
239 asn1p_lval.a_int = asn1p_atoi(yytext);
240 if(errno == ERANGE)
241 return -1;
242 return TOK_number;
243 }
244
245"0" {
246 asn1p_lval.a_int = asn1p_atoi(yytext);
247 if(errno == ERANGE)
248 return -1;
249 return TOK_number;
250 }
251
Lev Walkinf15320b2004-06-03 03:38:44 +0000252ABSENT return TOK_ABSENT;
Lev Walkin9c974182004-09-15 11:59:51 +0000253 /*
Lev Walkinf15320b2004-06-03 03:38:44 +0000254ABSTRACT-SYNTAX return TOK_ABSTRACT_SYNTAX;
Lev Walkin9c974182004-09-15 11:59:51 +0000255 */
Lev Walkinf15320b2004-06-03 03:38:44 +0000256ALL return TOK_ALL;
257ANY {
258 /* Appeared in 1990, removed in 1997 */
259 if(TYPE_LIFETIME(1990, 1997))
260 return TOK_ANY;
261 fprintf(stderr, "Keyword \"%s\" at line %d "
262 "is obsolete\n", yytext, yylineno);
263 REJECT;
264 }
265APPLICATION return TOK_APPLICATION;
266AUTOMATIC return TOK_AUTOMATIC;
267BEGIN return TOK_BEGIN;
268BIT return TOK_BIT;
269BMPString {
270 if(TYPE_LIFETIME(1994, 0))
271 return TOK_BMPString;
272 REJECT;
273 }
274BOOLEAN return TOK_BOOLEAN;
275BY return TOK_BY;
276CHARACTER return TOK_CHARACTER;
277CHOICE return TOK_CHOICE;
278CLASS return TOK_CLASS;
279COMPONENT return TOK_COMPONENT;
280COMPONENTS return TOK_COMPONENTS;
Lev Walkin1893ddf2005-03-20 14:28:32 +0000281CONSTRAINED return TOK_CONSTRAINED;
Lev Walkinf15320b2004-06-03 03:38:44 +0000282CONTAINING return TOK_CONTAINING;
283DEFAULT return TOK_DEFAULT;
284DEFINED {
285 /* Appeared in 1990, removed in 1997 */
286 if(TYPE_LIFETIME(1990, 1997))
287 return TOK_DEFINED;
288 fprintf(stderr, "Keyword \"%s\" at line %d "
289 "is obsolete\n", yytext, yylineno);
290 /* Deprecated since */
291 REJECT;
292 }
293DEFINITIONS return TOK_DEFINITIONS;
294EMBEDDED return TOK_EMBEDDED;
295ENCODED return TOK_ENCODED;
Lev Walkinf59d0752004-08-18 04:59:12 +0000296ENCODING-CONTROL return TOK_ENCODING_CONTROL;
Lev Walkinf15320b2004-06-03 03:38:44 +0000297END return TOK_END;
298ENUMERATED return TOK_ENUMERATED;
299EXCEPT return TOK_EXCEPT;
300EXPLICIT return TOK_EXPLICIT;
301EXPORTS return TOK_EXPORTS;
302EXTENSIBILITY return TOK_EXTENSIBILITY;
303EXTERNAL return TOK_EXTERNAL;
304FALSE return TOK_FALSE;
305FROM return TOK_FROM;
306GeneralizedTime return TOK_GeneralizedTime;
307GeneralString return TOK_GeneralString;
308GraphicString return TOK_GraphicString;
309IA5String return TOK_IA5String;
310IDENTIFIER return TOK_IDENTIFIER;
311IMPLICIT return TOK_IMPLICIT;
312IMPLIED return TOK_IMPLIED;
313IMPORTS return TOK_IMPORTS;
314INCLUDES return TOK_INCLUDES;
315INSTANCE return TOK_INSTANCE;
Lev Walkinf59d0752004-08-18 04:59:12 +0000316INSTRUCTIONS return TOK_INSTRUCTIONS;
Lev Walkinf15320b2004-06-03 03:38:44 +0000317INTEGER return TOK_INTEGER;
318INTERSECTION return TOK_INTERSECTION;
319ISO646String return TOK_ISO646String;
320MAX return TOK_MAX;
321MIN return TOK_MIN;
322MINUS-INFINITY return TOK_MINUS_INFINITY;
323NULL return TOK_NULL;
324NumericString return TOK_NumericString;
325OBJECT return TOK_OBJECT;
326ObjectDescriptor return TOK_ObjectDescriptor;
327OCTET return TOK_OCTET;
328OF return TOK_OF;
329OPTIONAL return TOK_OPTIONAL;
330PATTERN return TOK_PATTERN;
331PDV return TOK_PDV;
332PLUS-INFINITY return TOK_PLUS_INFINITY;
333PRESENT return TOK_PRESENT;
334PrintableString return TOK_PrintableString;
335PRIVATE return TOK_PRIVATE;
336REAL return TOK_REAL;
337RELATIVE-OID return TOK_RELATIVE_OID;
338SEQUENCE return TOK_SEQUENCE;
339SET return TOK_SET;
340SIZE return TOK_SIZE;
341STRING return TOK_STRING;
342SYNTAX return TOK_SYNTAX;
343T61String return TOK_T61String;
344TAGS return TOK_TAGS;
345TeletexString return TOK_TeletexString;
346TRUE return TOK_TRUE;
347TYPE-IDENTIFIER return TOK_TYPE_IDENTIFIER;
348UNION return TOK_UNION;
349UNIQUE return TOK_UNIQUE;
350UNIVERSAL return TOK_UNIVERSAL;
351UniversalString {
352 if(TYPE_LIFETIME(1994, 0))
353 return TOK_UniversalString;
354 REJECT;
355 }
356UTCTime return TOK_UTCTime;
357UTF8String {
358 if(TYPE_LIFETIME(1994, 0))
359 return TOK_UTF8String;
360 REJECT;
361 }
362VideotexString return TOK_VideotexString;
363VisibleString return TOK_VisibleString;
364WITH return TOK_WITH;
365
366
Lev Walkin2535a692005-07-02 21:42:40 +0000367<INITIAL,with_syntax>&[A-Z][A-Za-z0-9]*([-][A-Za-z0-9]+)* {
Lev Walkinf15320b2004-06-03 03:38:44 +0000368 asn1p_lval.tv_str = strdup(yytext);
369 return TOK_typefieldreference;
370 }
371
Lev Walkin2535a692005-07-02 21:42:40 +0000372<INITIAL,with_syntax>&[a-z][a-zA-Z0-9]*([-][a-zA-Z0-9]+)* {
Lev Walkinf15320b2004-06-03 03:38:44 +0000373 asn1p_lval.tv_str = strdup(yytext);
374 return TOK_valuefieldreference;
375 }
376
377
Lev Walkin2535a692005-07-02 21:42:40 +0000378[a-z][a-zA-Z0-9]*([-][a-zA-Z0-9]+)* {
Lev Walkinf15320b2004-06-03 03:38:44 +0000379 asn1p_lval.tv_str = strdup(yytext);
380 return TOK_identifier;
381 }
382
383 /*
384 * objectclassreference
385 */
Lev Walkin2535a692005-07-02 21:42:40 +0000386[A-Z][A-Z0-9]*([-][A-Z0-9]+)* {
Lev Walkinf15320b2004-06-03 03:38:44 +0000387 asn1p_lval.tv_str = strdup(yytext);
Lev Walkinf59d0752004-08-18 04:59:12 +0000388 return TOK_capitalreference;
Lev Walkinf15320b2004-06-03 03:38:44 +0000389 }
390
391 /*
392 * typereference, modulereference
393 * NOTE: TOK_objectclassreference must be combined
394 * with this token to produce true typereference.
395 */
Lev Walkin2535a692005-07-02 21:42:40 +0000396[A-Z][A-Za-z0-9]*([-][A-Za-z0-9]+)* {
Lev Walkinf15320b2004-06-03 03:38:44 +0000397 asn1p_lval.tv_str = strdup(yytext);
398 return TOK_typereference;
399 }
400
401"::=" return TOK_PPEQ;
402
403"..." return TOK_ThreeDots;
404".." return TOK_TwoDots;
405
Lev Walkinf15320b2004-06-03 03:38:44 +0000406<with_syntax>{
407
408 [^&{} \t\r\v\f\n]+ {
409 asn1p_lval.tv_opaque.buf = strdup(yytext);
410 asn1p_lval.tv_opaque.len = yyleng;
411 return TOK_opaque;
412 }
413
414 {WSP}+ {
415 asn1p_lval.tv_opaque.buf = strdup(yytext);
416 asn1p_lval.tv_opaque.len = yyleng;
417 return TOK_opaque;
418 }
419
420 "}" {
421 yy_pop_state();
422 return '}';
423 }
424
425}
426
Lev Walkind9574ae2005-03-24 16:22:35 +0000427
428{WSP}+ /* Ignore whitespace */
429
430
431[{][\t\r\v\f\n ]*[0-7][,][\t\r\v\f\n ]*[0-9]+[\t\r\v\f\n ]*[}] {
432 asn1c_integer_t v1 = -1, v2 = -1;
433 char *p;
434 for(p = yytext; *p; p++)
435 if(*p >= '0' && *p <= '9')
436 { v1 = asn1p_atoi(p); break; }
437 while(*p >= '0' && *p <= '9') p++; /* Skip digits */
438 for(; *p; p++) if(*p >= '0' && *p <= '9')
439 { v2 = asn1p_atoi(p); break; }
440 if(v1 < 0 || v1 > 7) {
441 fprintf(stderr, "%s at line %d: X.680:2003, #37.14 "
442 "mandates 0..7 range for Tuple's TableColumn\n",
443 yytext, yylineno);
444 return -1;
445 }
446 if(v2 < 0 || v2 > 15) {
447 fprintf(stderr, "%s at line %d: X.680:2003, #37.14 "
448 "mandates 0..15 range for Tuple's TableRow\n",
449 yytext, yylineno);
450 return -1;
451 }
452 asn1p_lval.a_int = (v1 << 4) + v2;
453 return TOK_tuple;
454 }
455
456[{][\t\r\v\f\n ]*[0-9]+[,][\t\r\v\f\n ]*[0-9]+[,][\t\r\v\f\n ]*[0-9]+[,][\t\r\v\f\n ]*[0-9]+[\t\r\v\f\n ]*[}] {
457 asn1c_integer_t v1 = -1, v2 = -1, v3 = -1, v4 = -1;
458 char *p;
459 for(p = yytext; *p; p++)
460 if(*p >= '0' && *p <= '9')
461 { v1 = asn1p_atoi(p); break; }
462 while(*p >= '0' && *p <= '9') p++; /* Skip digits */
463 for(; *p; p++) if(*p >= '0' && *p <= '9')
464 { v2 = asn1p_atoi(p); break; }
465 while(*p >= '0' && *p <= '9') p++;
466 for(; *p; p++) if(*p >= '0' && *p <= '9')
467 { v3 = asn1p_atoi(p); break; }
468 while(*p >= '0' && *p <= '9') p++;
469 for(; *p; p++) if(*p >= '0' && *p <= '9')
470 { v4 = asn1p_atoi(p); break; }
471 if(v1 < 0 || v1 > 127) {
472 fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
473 "mandates 0..127 range for Quadruple's Group\n",
474 yytext, yylineno);
475 return -1;
476 }
477 if(v2 < 0 || v2 > 255) {
478 fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
479 "mandates 0..255 range for Quadruple's Plane\n",
480 yytext, yylineno);
481 return -1;
482 }
483 if(v3 < 0 || v3 > 255) {
484 fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
485 "mandates 0..255 range for Quadruple's Row\n",
486 yytext, yylineno);
487 return -1;
488 }
489 if(v4 < 0 || v4 > 255) {
490 fprintf(stderr, "%s at line %d: X.680:2003, #37.12 "
491 "mandates 0..255 range for Quadruple's Cell\n",
492 yytext, yylineno);
493 return -1;
494 }
495 asn1p_lval.a_int = (v1 << 24) | (v2 << 16) | (v3 << 8) | v4;
496 return TOK_quadruple;
497 }
498
499
500[(){},;:|!.&@\[\]^] return yytext[0];
501
502[^A-Za-z0-9:=,{}<.@()[]'\"|&^*;!-] {
503 if(TYPE_LIFETIME(1994, 0))
504 fprintf(stderr, "ERROR: ");
505 fprintf(stderr,
506 "Symbol '%c' at line %d is prohibited "
507 "by ASN.1:1994 and ASN.1:1997\n",
508 yytext[0], yylineno);
509 if(TYPE_LIFETIME(1994, 0))
510 return -1;
511 }
Lev Walkinf15320b2004-06-03 03:38:44 +0000512
513<*>. {
514 fprintf(stderr,
515 "Unexpected token at line %d: \"%s\"\n",
516 yylineno, yytext);
517 while(YYSTATE != INITIAL)
518 yy_pop_state();
Lev Walkin9c974182004-09-15 11:59:51 +0000519 if(0) {
520 yy_top_state(); /* Just to use this function. */
521 yy_fatal_error("Parse error");
522 }
Lev Walkinf15320b2004-06-03 03:38:44 +0000523 return -1;
524}
525
526<*><<EOF>> {
527 while(YYSTATE != INITIAL)
528 yy_pop_state();
529 yyterminate();
530 }
531
532
533%%
534
535/*
536 * Very dirty but wonderful hack allowing to rule states from within .y file.
537 */
Lev Walkinf59d0752004-08-18 04:59:12 +0000538void asn1p_lexer_hack_push_opaque_state() { yy_push_state(opaque); }
Lev Walkinf15320b2004-06-03 03:38:44 +0000539
540/*
541 * Another hack which disables recognizing some tokens when inside WITH SYNTAX.
542 */
Lev Walkinf59d0752004-08-18 04:59:12 +0000543void asn1p_lexer_hack_enable_with_syntax() { yy_push_state(with_syntax); }
544
545/* Yet another */
546void asn1p_lexer_hack_push_encoding_control() {
547 yy_push_state(encoding_control);
Lev Walkinf15320b2004-06-03 03:38:44 +0000548}
549
Lev Walkinc603f102005-01-23 09:51:44 +0000550static asn1c_integer_t
Lev Walkinf15320b2004-06-03 03:38:44 +0000551asn1p_atoi(char *ptr) {
Lev Walkinc603f102005-01-23 09:51:44 +0000552 asn1c_integer_t value;
Lev Walkinf15320b2004-06-03 03:38:44 +0000553 errno = 0; /* Clear the error code */
554
555 if(sizeof(value) <= sizeof(int)) {
556 value = strtol(ptr, 0, 10);
557 } else {
558#ifdef HAVE_STRTOIMAX
559 value = strtoimax(ptr, 0, 10);
560#elif HAVE_STRTOLL
561 value = strtoll(ptr, 0, 10);
562#else
563 value = strtol(ptr, 0, 10);
564#endif
565 }
566
567 if(errno == ERANGE) {
568 fprintf(stderr,
569 "Value \"%s\" at line %d is too large "
Lev Walkin129a79e2005-04-05 08:46:22 +0000570 "for this compiler! Please contact the asn1c author.\n",
Lev Walkinf15320b2004-06-03 03:38:44 +0000571 ptr, yylineno);
572 errno = ERANGE; /* Restore potentially clobbered errno */
573 }
574
575 return value;
576}
577