NativeEnumerated.c vars NULL init and check
[com/asn1c.git] / skeletons / converter-example.c
1 /*
2  * Generic converter template for a selected ASN.1 type.
3  * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>.
4  * All rights reserved.
5  * 
6  * To compile with your own ASN.1 type, redefine the PDU as shown:
7  * 
8  * cc -DPDU=MyCustomType -o myDecoder.o -c converter-example.c
9  */
10 #ifdef    HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13 #define __EXTENSIONS__
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <stdlib.h>    /* for atoi(3) */
17 #include <getopt.h>    /* for getopt(3) */
18 #include <string.h>    /* for strerror(3) */
19 #include <sysexits.h>    /* for EX_* exit codes */
20 #include <errno.h>    /* for errno */
21 #include <unistd.h>    /* for isatty(3) */
22 #include <asn_application.h>
23 #include <asn_internal.h>    /* for ASN__DEFAULT_STACK_MAX */
24
25 /* Convert "Type" defined by -DPDU into "asn_DEF_Type" */
26 #ifdef PDU
27 #define    ASN_DEF_PDU(t)    asn_DEF_ ## t
28 #define    DEF_PDU_Type(t)    ASN_DEF_PDU(t)
29 #define    PDU_Type    DEF_PDU_Type(PDU)
30 extern asn_TYPE_descriptor_t PDU_Type;    /* ASN.1 type to be decoded */
31 #define PDU_Type_Ptr (&PDU_Type)
32 #else   /* !PDU */
33 #define PDU_Type_Ptr    NULL
34 #endif  /* PDU */
35
36 #ifdef ASN_PDU_COLLECTION /* Generated by asn1c -pdu=... */
37 extern asn_TYPE_descriptor_t *asn_pdu_collection[];
38 #endif
39
40 #ifndef NO_ASN_PDU
41 #if !defined(PDU) && !defined(ASN_PDU_COLLECTION)
42 #error Define -DPDU to compile this example converter.
43 #error `asn1c -pdu=...` adds necessary flags automatically.
44 #endif
45 #endif
46
47 /*
48  * Open file and parse its contens.
49  */
50 static void *data_decode_from_file(enum asn_transfer_syntax,
51                                    asn_TYPE_descriptor_t *asnTypeOfPDU,
52                                    FILE *file, const char *name,
53                                    ssize_t suggested_bufsize, int first_pdu);
54 static int write_out(const void *buffer, size_t size, void *key);
55 static FILE *argument_to_file(char *av[], int idx);
56 static char *argument_to_name(char *av[], int idx);
57
58        int opt_debug;   /* -d (or -dd) */
59 static int opt_check;   /* -c (constraints checking) */
60 static int opt_stack;   /* -s (maximum stack size) */
61 static int opt_nopad;   /* -per-nopad (PER input is not padded between msgs) */
62 static int opt_onepdu;  /* -1 (decode single PDU) */
63
64 #ifdef    JUNKTEST        /* Enable -J <probability> */
65 #define JUNKOPT "J:"
66 static double opt_jprob;    /* Junk bit probability */
67 static int    junk_failures;
68 static void   junk_bytes_with_probability(uint8_t *, size_t, double prob);
69
70 #define RANDOPT "R:"
71 static ssize_t random_max_size = 0; /* Size of the random data */
72
73 #if !defined(__FreeBSD__) && !(defined(__APPLE__) && defined(__MACH__))
74 static void
75 srandomdev(void) {
76     FILE *f = fopen("/dev/urandom", "rb");
77     unsigned seed;
78     if(f) {
79         if(fread(&seed, 1, sizeof(seed), f) != sizeof(seed)) {
80             seed = time(NULL);
81         }
82         fclose(f);
83     } else {
84         seed = time(NULL);
85     }
86     srandom(seed);
87 }
88 #endif
89
90 #else   /* !JUNKTEST */
91 #define    JUNKOPT
92 #define    RANDOPT
93 #endif  /* JUNKTEST */
94
95 /* Debug output function */
96 static void CC_PRINTFLIKE(1, 2)
97 DEBUG(const char *fmt, ...) {
98     va_list ap;
99     if(!opt_debug) return;
100     fprintf(stderr, "AD: ");
101     va_start(ap, fmt);
102     vfprintf(stderr, fmt, ap);
103     va_end(ap);
104     fprintf(stderr, "\n");
105 }
106
107 static const char *
108 ats_simple_name(enum asn_transfer_syntax syntax) {
109     switch(syntax) {
110     case ATS_INVALID:
111         return "/dev/null";
112     case ATS_NONSTANDARD_PLAINTEXT:
113         return "plaintext";
114     case ATS_BER:
115         return "BER";
116     case ATS_DER:
117         return "DER";
118     case ATS_CER:
119         return "CER";
120     case ATS_BASIC_OER:
121     case ATS_CANONICAL_OER:
122         return "OER";
123     case ATS_BASIC_XER:
124     case ATS_CANONICAL_XER:
125         return "XER";
126     case ATS_UNALIGNED_BASIC_PER:
127     case ATS_UNALIGNED_CANONICAL_PER:
128         return "PER";
129     default:
130         return "<?>";
131     }
132 }
133
134 #define CODEC_OFFSET(fname)  ((ptrdiff_t)&(((asn_TYPE_operation_t *)0)->fname))
135 typedef struct {
136     const char *name;
137     enum asn_transfer_syntax syntax;
138     ptrdiff_t codec_offset;
139     const char *full_name;
140 } syntax_selector;
141
142 static syntax_selector input_encodings[] = {
143     {"ber", ATS_BER, CODEC_OFFSET(ber_decoder),
144      "Input is in BER (Basic Encoding Rules) or DER"},
145     {"oer", ATS_BASIC_OER, CODEC_OFFSET(oer_decoder),
146      "Input is in OER (Octet Encoding Rules)"},
147     {"per", ATS_UNALIGNED_BASIC_PER, CODEC_OFFSET(uper_decoder),
148      "Input is in Unaligned PER (Packed Encoding Rules)"},
149     {"aper", ATS_ALIGNED_BASIC_PER, CODEC_OFFSET(aper_decoder),
150      "Input is in Aligned PER (Packed Encoding Rules)"},
151     {"xer", ATS_BASIC_XER, CODEC_OFFSET(xer_decoder),
152      "Input is in XER (XML Encoding Rules)"},
153     {0, ATS_INVALID, 0, 0}};
154
155 static syntax_selector output_encodings[] = {
156     {"der", ATS_DER, CODEC_OFFSET(der_encoder),
157      "Output as DER (Distinguished Encoding Rules)"},
158     {"oer", ATS_CANONICAL_OER, CODEC_OFFSET(oer_encoder),
159      "Output as Canonical OER (Octet Encoding Rules)"},
160     {"per", ATS_UNALIGNED_CANONICAL_PER, CODEC_OFFSET(uper_encoder),
161      "Output as Unaligned PER (Packed Encoding Rules)"},
162     {"aper", ATS_ALIGNED_CANONICAL_PER, CODEC_OFFSET(aper_encoder),
163      "Output as Aligned PER (Packed Encoding Rules)"},
164     {"xer", ATS_BASIC_XER, CODEC_OFFSET(xer_encoder),
165      "Output as XER (XML Encoding Rules)"},
166     {"text", ATS_NONSTANDARD_PLAINTEXT, CODEC_OFFSET(print_struct),
167      "Output as plain semi-structured text"},
168     {"null", ATS_INVALID, CODEC_OFFSET(print_struct),
169      "Verify (decode) input, but do not output"},
170     {0, ATS_INVALID, 0, 0}};
171
172 static int
173 has_codec_defined(const asn_TYPE_descriptor_t *td,
174                   const syntax_selector *element) {
175     return *(const void *const *)(const void *)((const char *)td->op
176                                                 + element->codec_offset) != 0;
177 }
178
179 /*
180  * Select ASN.1 Transfer Enocoding Syntax by command line name.
181  */
182 static const syntax_selector *
183 ats_by_name(const char *name, const asn_TYPE_descriptor_t *td,
184             const syntax_selector *first_element) {
185     const syntax_selector *element;
186     for(element = first_element; element->name; element++) {
187         if(strcmp(element->name, name) == 0) {
188             if(td && td->op && has_codec_defined(td, element)) {
189                 return element;
190             }
191         }
192     }
193     return NULL;
194 }
195
196 int
197 main(int ac, char *av[]) {
198     FILE *binary_out;
199     asn_TYPE_descriptor_t *pduType = PDU_Type_Ptr;
200     asn_TYPE_descriptor_t *anyPduType = PDU_Type_Ptr;
201     ssize_t suggested_bufsize = 8192;  /* close or equal to stdio buffer */
202     int number_of_iterations = 1;
203     int num;
204     int ch;
205     const syntax_selector *sel;
206     enum asn_transfer_syntax isyntax = ATS_INVALID;
207     enum asn_transfer_syntax osyntax = ATS_BASIC_XER;
208
209     if(!anyPduType) {
210 #ifdef  ASN_PDU_COLLECTION
211         anyPduType = asn_pdu_collection[0];
212         if(!anyPduType) {
213             fprintf(stderr,
214                     "Empty PDU collection, no reference PDU to choose from.\n");
215             exit(EX_SOFTWARE);
216         }
217 #else
218         fprintf(stderr, "Either asn1c -pdu=... or cc -DPDU should be used.\n");
219         exit(EX_SOFTWARE);
220 #endif
221     }
222
223     /* Figure out if a specialty decoder needs to be default */
224 #ifndef ASN_DISABLE_OER_SUPPORT
225     isyntax = ATS_BASIC_OER;
226 #endif
227 #ifndef ASN_DISABLE_PER_SUPPORT
228     isyntax = ATS_UNALIGNED_BASIC_PER;
229 #endif
230
231     /*
232      * Pocess the command-line argments.
233      */
234     while((ch = getopt(ac, av, "i:o:1b:cdn:p:hs:" JUNKOPT RANDOPT)) != -1)
235     switch(ch) {
236     case 'i':
237         sel = ats_by_name(optarg, anyPduType, input_encodings);
238         if(sel) {
239             isyntax = sel->syntax;
240         } else {
241             fprintf(stderr, "-i<format>: '%s': improper format selector\n",
242                     optarg);
243             exit(EX_UNAVAILABLE);
244         }
245         break;
246     case 'o':
247         sel = ats_by_name(optarg, anyPduType, output_encodings);
248         if(sel) {
249             osyntax = sel->syntax;
250         } else {
251             fprintf(stderr, "-o<format>: '%s': improper format selector\n",
252                     optarg);
253             exit(EX_UNAVAILABLE);
254         }
255         break;
256     case '1':
257         opt_onepdu = 1;
258         break;
259     case 'b':
260         suggested_bufsize = atoi(optarg);
261         if(suggested_bufsize < 1
262             || suggested_bufsize > 16 * 1024 * 1024) {
263             fprintf(stderr,
264                 "-b %s: Improper buffer size (1..16M)\n",
265                 optarg);
266             exit(EX_UNAVAILABLE);
267         }
268         break;
269     case 'c':
270         opt_check = 1;
271         break;
272     case 'd':
273         opt_debug++;    /* Double -dd means ASN.1 debug */
274         break;
275     case 'n':
276         number_of_iterations = atoi(optarg);
277         if(number_of_iterations < 1) {
278             fprintf(stderr,
279                 "-n %s: Improper iterations count\n", optarg);
280             exit(EX_UNAVAILABLE);
281         }
282         break;
283     case 'p':
284         if(strcmp(optarg, "er-nopad") == 0) {
285             opt_nopad = 1;
286             break;
287         }
288 #ifdef    ASN_PDU_COLLECTION
289         if(strcmp(optarg, "list") == 0) {
290             asn_TYPE_descriptor_t **pdu = asn_pdu_collection;
291             fprintf(stderr, "Available PDU types:\n");
292             for(; *pdu; pdu++) printf("%s\n", (*pdu)->name);
293             exit(0);
294         } else if(optarg[0] >= 'A' && optarg[0] <= 'Z') {
295             asn_TYPE_descriptor_t **pdu = asn_pdu_collection;
296             while(*pdu && strcmp((*pdu)->name, optarg)) pdu++;
297             if(*pdu) { pduType = *pdu; break; }
298             fprintf(stderr, "-p %s: Unrecognized PDU. Try '-p list'.\n",
299                     optarg);
300             exit(EX_USAGE);
301         }
302 #else   /* Without -pdu=auto there's just a single type */
303         if(strcmp(optarg, "list") == 0) {
304             fprintf(stderr, "Available PDU types:\n");
305             if(pduType) {
306                 printf("%s\n", pduType->name);
307             }
308             exit(0);
309         } else if(optarg[0] >= 'A' && optarg[0] <= 'Z') {
310             if(pduType && strcmp(optarg, pduType->name) == 0) {
311                 break;
312             }
313             fprintf(stderr, "-p %s: Unrecognized PDU. Try '-p list'.\n",
314                     optarg);
315             exit(EX_USAGE);
316         }
317 #endif    /* ASN_PDU_COLLECTION */
318         fprintf(stderr, "-p %s: Unrecognized option\n", optarg);
319         exit(EX_UNAVAILABLE);
320     case 's':
321         opt_stack = atoi(optarg);
322         if(opt_stack < 0) {
323             fprintf(stderr,
324                 "-s %s: Non-negative value expected\n",
325                 optarg);
326             exit(EX_UNAVAILABLE);
327         }
328         break;
329 #ifdef    JUNKTEST
330     case 'J':
331         opt_jprob = strtod(optarg, 0);
332         if(opt_jprob <= 0.0 || opt_jprob > 1.0) {
333             fprintf(stderr,
334                 "-J %s: Probability range 0..1 expected \n",
335                 optarg);
336             exit(EX_UNAVAILABLE);
337         }
338         break;
339     case 'R':
340         isyntax = ATS_RANDOM;
341         random_max_size = atoi(optarg);
342         if(random_max_size < 0) {
343             fprintf(stderr,
344                 "-R %s: Non-negative value expected\n",
345                 optarg);
346             exit(EX_UNAVAILABLE);
347         }
348         srandomdev();
349         break;
350 #endif    /* JUNKTEST */
351     case 'h':
352     default:
353 #ifdef    ASN_CONVERTER_TITLE
354 #define    _AXS(x)    #x
355 #define    _ASX(x)    _AXS(x)
356         fprintf(stderr, "%s\n", _ASX(ASN_CONVERTER_TITLE));
357 #endif
358         fprintf(stderr, "Usage: %s [options] <datafile> ...\n", av[0]);
359         fprintf(stderr, "Where options are:\n");
360         for(sel = input_encodings; sel->name; sel++) {
361             if(ats_by_name(sel->name, anyPduType, sel)) {
362                 fprintf(stderr, "  -i%s        %s%s\n", sel->name,
363                         sel->full_name,
364                         (sel->syntax == isyntax) ? " (DEFAULT)" : "");
365             }
366         }
367         for(sel = output_encodings; sel->name; sel++) {
368             if(ats_by_name(sel->name, anyPduType, sel)) {
369                 fprintf(stderr, "  -o%s%s       %s%s\n", sel->name,
370                         strlen(sel->name) > 3 ? "" : " ",
371                         sel->full_name,
372                         (sel->syntax == osyntax) ? " (DEFAULT)" : "");
373             }
374         }
375         if(anyPduType->op->uper_decoder) {
376             fprintf(stderr,
377                     "  -per-nopad   Assume PER PDUs are not padded (-iper)\n");
378         }
379 #ifdef    ASN_PDU_COLLECTION
380         fprintf(stderr,
381         "  -p <PDU>     Specify PDU type to decode\n"
382         "  -p list      List available PDUs\n");
383 #endif    /* ASN_PDU_COLLECTION */
384         fprintf(stderr,
385         "  -1           Decode only the first PDU in file\n"
386         "  -b <size>    Set the i/o buffer size (default is %ld)\n"
387         "  -c           Check ASN.1 constraints after decoding\n"
388         "  -d           Enable debugging (-dd is even better)\n"
389         "  -n <num>     Process files <num> times\n"
390         "  -s <size>    Set the stack usage limit (default is %d)\n"
391 #ifdef    JUNKTEST
392         "  -J <prob>    Set random junk test bit garbaging probability\n"
393         "  -R <size>    Generate a random value of roughly the given size,\n"
394         "               instead of parsing the value from file.\n"
395 #endif
396         , (long)suggested_bufsize, ASN__DEFAULT_STACK_MAX);
397         exit(EX_USAGE);
398     }
399
400     ac -= optind;
401     av += optind;
402
403     if(!pduType) {
404 #ifdef  NO_ASN_PDU
405         fprintf(stderr, "No -DPDU defined during compilation.\n");
406         exit(0);
407 #else
408         fprintf(stderr, "Use '-p <Type>' or '-p list' to select ASN.1 type.\n");
409         exit(EX_USAGE);
410 #endif  /* NO_ASN_PDU */
411     }
412
413     if(ac < 1 && isyntax != ATS_RANDOM) {
414         fprintf(stderr, "%s: No input files specified. "
415                 "Try '-h' for more information\n",
416                 av[-optind]);
417         exit(EX_USAGE);
418     }
419
420     if(isatty(1)) {
421         const int is_text_output = osyntax == ATS_NONSTANDARD_PLAINTEXT
422                                    || osyntax == ATS_BASIC_XER
423                                    || osyntax == ATS_CANONICAL_XER;
424         if(is_text_output) {
425             binary_out = stdout;
426         } else {
427             fprintf(stderr, "(Suppressing binary output to a terminal.)\n");
428             binary_out = fopen("/dev/null", "wb");
429             if(!binary_out) {
430                 fprintf(stderr, "Can't open /dev/null: %s\n", strerror(errno));
431                 exit(EX_OSERR);
432             }
433         }
434     } else {
435         binary_out = stdout;
436     }
437     setvbuf(stdout, 0, _IOLBF, 0);
438
439     for(num = 0; num < number_of_iterations; num++) {
440       int ac_i;
441       /*
442        * Process all files in turn.
443        */
444       for(ac_i = (isyntax == ATS_RANDOM) ? -1 : 0; ac_i < ac; ac_i++) {
445         asn_enc_rval_t erv = {0,0,0};
446         void *structure;    /* Decoded structure */
447         FILE *file;
448         char *name;
449         int first_pdu;
450
451         if(ac_i == -1) {
452             file = NULL;
453             name = "<random value generator>";
454             opt_onepdu = 1;
455         } else {
456             file = argument_to_file(av, ac_i);
457             name = argument_to_name(av, ac_i);
458         }
459
460         for(first_pdu = 1; (first_pdu || !opt_onepdu); first_pdu = 0) {
461             /*
462              * Decode the encoded structure from file.
463              */
464 #ifdef  JUNKTEST
465             if(isyntax == ATS_RANDOM) {
466                 structure = NULL;
467                 if(asn_random_fill(pduType, &structure, random_max_size) != 0) {
468                     fprintf(stderr, "Cannot generate a random value.\n");
469                     assert(structure == NULL);
470                     errno = EINVAL;
471                 }
472             } else {
473 #endif
474                 structure = data_decode_from_file(isyntax, pduType, file, name,
475                                                   suggested_bufsize, first_pdu);
476 #ifdef  JUNKTEST
477             }
478 #endif
479             if(!structure) {
480                 if(errno) {
481                     /* Error message is already printed */
482                     exit(EX_DATAERR);
483                 } else {
484                     /* EOF */
485                     break;
486                 }
487             }
488
489             /* Check ASN.1 constraints */
490             if(opt_check) {
491                 char errbuf[128];
492                 size_t errlen = sizeof(errbuf);
493                 if(asn_check_constraints(pduType, structure, errbuf, &errlen)) {
494                     fprintf(stderr,
495                             "%s: ASN.1 constraint "
496                             "check failed: %s\n",
497                             name, errbuf);
498                     exit(EX_DATAERR);
499                 }
500             }
501
502             if(osyntax == ATS_INVALID) {
503 #ifdef JUNKTEST
504                 if(opt_jprob == 0.0) {
505                     fprintf(stderr, "%s: decoded successfully\n", name);
506                 }
507 #else
508                 fprintf(stderr, "%s: decoded successfully\n", name);
509 #endif
510             } else {
511                 erv = asn_encode(NULL, osyntax, pduType, structure, write_out,
512                                  binary_out);
513
514                 if(erv.encoded == -1) {
515                     fprintf(stderr, "%s: Cannot convert %s into %s\n", name,
516                             pduType->name, ats_simple_name(osyntax));
517                     DEBUG("Conversion failed for %s:\n", pduType->name);
518                     asn_fprint(stderr, pduType, structure);
519                     exit(EX_UNAVAILABLE);
520                 }
521                 DEBUG("Encoded in %" ASN_PRI_SSIZE " bytes of %s", erv.encoded,
522                       ats_simple_name(osyntax));
523             }
524
525             ASN_STRUCT_FREE(*pduType, structure);
526         }
527
528         if(file && file != stdin) {
529             fclose(file);
530         }
531       }
532     }
533
534 #ifdef    JUNKTEST
535     if(opt_jprob > 0.0) {
536         fprintf(stderr, "Junked %f OK (%d/%d)\n",
537             opt_jprob, junk_failures, number_of_iterations);
538     }
539 #endif    /* JUNKTEST */
540
541     return 0;
542 }
543
544 static struct dynamic_buffer {
545     uint8_t *data;        /* Pointer to the data bytes */
546     size_t offset;        /* Offset from the start */
547     size_t length;        /* Length of meaningful contents */
548     size_t unbits;        /* Unused bits in the last byte */
549     size_t allocated;    /* Allocated memory for data */
550     int    nreallocs;    /* Number of data reallocations */
551     off_t  bytes_shifted;    /* Number of bytes ever shifted */
552 } DynamicBuffer;
553
554 static void
555 buffer_dump() {
556     uint8_t *p = DynamicBuffer.data + DynamicBuffer.offset;
557     uint8_t *e = p + DynamicBuffer.length - (DynamicBuffer.unbits ? 1 : 0);
558     if(!opt_debug) return;
559     DEBUG("Buffer: { d=%p, o=%" ASN_PRI_SIZE ", l=%" ASN_PRI_SIZE
560           ", u=%" ASN_PRI_SIZE ", a=%" ASN_PRI_SIZE ", s=%" ASN_PRI_SIZE " }",
561         (const void *)DynamicBuffer.data,
562         DynamicBuffer.offset,
563         DynamicBuffer.length,
564         DynamicBuffer.unbits,
565         DynamicBuffer.allocated,
566         (size_t)DynamicBuffer.bytes_shifted);
567     for(; p < e; p++) {
568         fprintf(stderr, " %c%c%c%c%c%c%c%c",
569             ((*p >> 7) & 1) ? '1' : '0',
570             ((*p >> 6) & 1) ? '1' : '0',
571             ((*p >> 5) & 1) ? '1' : '0',
572             ((*p >> 4) & 1) ? '1' : '0',
573             ((*p >> 3) & 1) ? '1' : '0',
574             ((*p >> 2) & 1) ? '1' : '0',
575             ((*p >> 1) & 1) ? '1' : '0',
576             ((*p >> 0) & 1) ? '1' : '0');
577     }
578     if(DynamicBuffer.unbits) {
579         unsigned int shift;
580         fprintf(stderr, " ");
581         for(shift = 7; shift >= DynamicBuffer.unbits; shift--)
582             fprintf(stderr, "%c", ((*p >> shift) & 1) ? '1' : '0');
583         fprintf(stderr, " %" ASN_PRI_SSIZE ":%" ASN_PRI_SSIZE "\n",
584                 (ssize_t)DynamicBuffer.length - 1,
585                 (ssize_t)8 - DynamicBuffer.unbits);
586     } else {
587         fprintf(stderr, " %ld\n", (long)DynamicBuffer.length);
588     }
589 }
590
591 /*
592  * Move the buffer content left N bits, possibly joining it with
593  * preceeding content.
594  */
595 static void
596 buffer_shift_left(size_t offset, int bits) {
597     uint8_t *ptr = DynamicBuffer.data + DynamicBuffer.offset + offset;
598     uint8_t *end = DynamicBuffer.data + DynamicBuffer.offset
599             + DynamicBuffer.length - 1;
600     
601     if(!bits) return;
602
603     DEBUG("Shifting left %d bits off %ld (o=%ld, u=%ld, l=%ld)",
604         bits, (long)offset,
605         (long)DynamicBuffer.offset,
606         (long)DynamicBuffer.unbits,
607         (long)DynamicBuffer.length);
608
609     if(offset) {
610         int right;
611         right = ptr[0] >> (8 - bits);
612
613         DEBUG("oleft: %c%c%c%c%c%c%c%c",
614             ((ptr[-1] >> 7) & 1) ? '1' : '0',
615             ((ptr[-1] >> 6) & 1) ? '1' : '0',
616             ((ptr[-1] >> 5) & 1) ? '1' : '0',
617             ((ptr[-1] >> 4) & 1) ? '1' : '0',
618             ((ptr[-1] >> 3) & 1) ? '1' : '0',
619             ((ptr[-1] >> 2) & 1) ? '1' : '0',
620             ((ptr[-1] >> 1) & 1) ? '1' : '0',
621             ((ptr[-1] >> 0) & 1) ? '1' : '0');
622
623         DEBUG("oriht: %c%c%c%c%c%c%c%c",
624             ((ptr[0] >> 7) & 1) ? '1' : '0',
625             ((ptr[0] >> 6) & 1) ? '1' : '0',
626             ((ptr[0] >> 5) & 1) ? '1' : '0',
627             ((ptr[0] >> 4) & 1) ? '1' : '0',
628             ((ptr[0] >> 3) & 1) ? '1' : '0',
629             ((ptr[0] >> 2) & 1) ? '1' : '0',
630             ((ptr[0] >> 1) & 1) ? '1' : '0',
631             ((ptr[0] >> 0) & 1) ? '1' : '0');
632
633         DEBUG("mriht: %c%c%c%c%c%c%c%c",
634             ((right >> 7) & 1) ? '1' : '0',
635             ((right >> 6) & 1) ? '1' : '0',
636             ((right >> 5) & 1) ? '1' : '0',
637             ((right >> 4) & 1) ? '1' : '0',
638             ((right >> 3) & 1) ? '1' : '0',
639             ((right >> 2) & 1) ? '1' : '0',
640             ((right >> 1) & 1) ? '1' : '0',
641             ((right >> 0) & 1) ? '1' : '0');
642
643         ptr[-1] = (ptr[-1] & (0xff << bits)) | right;
644
645         DEBUG("after: %c%c%c%c%c%c%c%c",
646             ((ptr[-1] >> 7) & 1) ? '1' : '0',
647             ((ptr[-1] >> 6) & 1) ? '1' : '0',
648             ((ptr[-1] >> 5) & 1) ? '1' : '0',
649             ((ptr[-1] >> 4) & 1) ? '1' : '0',
650             ((ptr[-1] >> 3) & 1) ? '1' : '0',
651             ((ptr[-1] >> 2) & 1) ? '1' : '0',
652             ((ptr[-1] >> 1) & 1) ? '1' : '0',
653             ((ptr[-1] >> 0) & 1) ? '1' : '0');
654     }
655
656     buffer_dump();
657
658     for(; ptr < end; ptr++) {
659         int right = ptr[1] >> (8 - bits);
660         *ptr = (*ptr << bits) | right;
661     }
662     *ptr <<= bits;
663
664     DEBUG("Unbits [%" ASN_PRI_SIZE "=>", DynamicBuffer.unbits);
665     if(DynamicBuffer.unbits == 0) {
666         DynamicBuffer.unbits += bits;
667     } else {
668         DynamicBuffer.unbits += bits;
669         if(DynamicBuffer.unbits > 7) {
670             DynamicBuffer.unbits -= 8;
671             DynamicBuffer.length--;
672             DynamicBuffer.bytes_shifted++;
673         }
674     }
675     DEBUG("Unbits =>%" ASN_PRI_SIZE "]", DynamicBuffer.unbits);
676
677     buffer_dump();
678
679     DEBUG("Shifted. Now (o=%" ASN_PRI_SIZE ", u=%" ASN_PRI_SIZE
680           " l=%" ASN_PRI_SIZE ")",
681         DynamicBuffer.offset,
682         DynamicBuffer.unbits,
683         DynamicBuffer.length);
684 }
685
686 /*
687  * Ensure that the buffer contains at least this amount of free space.
688  */
689 static void add_bytes_to_buffer(const void *data2add, size_t bytes) {
690
691     if(bytes == 0) return;
692
693     DEBUG("=> add_bytes(%" ASN_PRI_SIZE ") { o=%" ASN_PRI_SIZE
694           " l=%" ASN_PRI_SIZE " u=%" ASN_PRI_SIZE ", s=%" ASN_PRI_SIZE " }",
695         bytes,
696         DynamicBuffer.offset,
697         DynamicBuffer.length,
698         DynamicBuffer.unbits,
699         DynamicBuffer.allocated);
700
701     if(DynamicBuffer.allocated
702        >= (DynamicBuffer.offset + DynamicBuffer.length + bytes)) {
703         DEBUG("\tNo buffer reallocation is necessary");
704     } else if(bytes <= DynamicBuffer.offset) {
705         DEBUG("\tContents shifted by %ld", DynamicBuffer.offset);
706
707         /* Shift the buffer contents */
708         memmove(DynamicBuffer.data,
709                 DynamicBuffer.data + DynamicBuffer.offset,
710             DynamicBuffer.length);
711         DynamicBuffer.bytes_shifted += DynamicBuffer.offset;
712         DynamicBuffer.offset = 0;
713     } else {
714         size_t newsize = (DynamicBuffer.allocated << 2) + bytes;
715         void *p = MALLOC(newsize);
716         if(!p) {
717             perror("malloc()");
718             exit(EX_OSERR);
719         }
720                 if (DynamicBuffer.length) {
721                         memcpy(p,
722                                         DynamicBuffer.data + DynamicBuffer.offset,
723                                         DynamicBuffer.length);
724                 }
725         FREEMEM(DynamicBuffer.data);
726         DynamicBuffer.data = (uint8_t *)p;
727         DynamicBuffer.offset = 0;
728         DynamicBuffer.allocated = newsize;
729         DynamicBuffer.nreallocs++;
730         DEBUG("\tBuffer reallocated to %ld (%d time)",
731             newsize, DynamicBuffer.nreallocs);
732     }
733
734     memcpy(DynamicBuffer.data
735         + DynamicBuffer.offset + DynamicBuffer.length,
736         data2add, bytes);
737     DynamicBuffer.length += bytes;
738     if(DynamicBuffer.unbits) {
739         int bits = DynamicBuffer.unbits;
740         DynamicBuffer.unbits = 0;
741         buffer_shift_left(DynamicBuffer.length - bytes, bits);
742     }
743
744     DEBUG("<= add_bytes(%" ASN_PRI_SIZE ") { o=%" ASN_PRI_SIZE
745           " l=%" ASN_PRI_SIZE " u=%" ASN_PRI_SIZE ", s=%" ASN_PRI_SIZE " }",
746         bytes,
747         DynamicBuffer.offset,
748         DynamicBuffer.length,
749         DynamicBuffer.unbits,
750         DynamicBuffer.allocated);
751 }
752
753 static int
754 is_syntax_PER(enum asn_transfer_syntax syntax) {
755     return (syntax == ATS_UNALIGNED_BASIC_PER
756             || syntax == ATS_UNALIGNED_CANONICAL_PER
757             || syntax == ATS_ALIGNED_BASIC_PER
758             || syntax == ATS_ALIGNED_CANONICAL_PER);
759 }
760
761 static int
762 restartability_supported(enum asn_transfer_syntax syntax) {
763     return !is_syntax_PER(syntax);
764 }
765
766 static void *
767 data_decode_from_file(enum asn_transfer_syntax isyntax, asn_TYPE_descriptor_t *pduType, FILE *file, const char *name, ssize_t suggested_bufsize, int on_first_pdu) {
768     static uint8_t *fbuf;
769     static ssize_t fbuf_size;
770     static asn_codec_ctx_t s_codec_ctx;
771     asn_codec_ctx_t *opt_codec_ctx = 0;
772     void *structure = 0;
773     asn_dec_rval_t rval;
774     size_t old_offset;    
775     size_t new_offset;
776     int tolerate_eof;
777     size_t rd;
778
779     if(!file) {
780         fprintf(stderr, "%s: %s\n", name, strerror(errno));
781         errno = EINVAL;
782         return 0;
783     }
784
785     if(opt_stack) {
786         s_codec_ctx.max_stack_size = opt_stack;
787         opt_codec_ctx = &s_codec_ctx;
788     }
789
790     DEBUG("Processing %s", name);
791
792     /* prepare the file buffer */
793     if(fbuf_size != suggested_bufsize) {
794         fbuf = (uint8_t *)REALLOC(fbuf, suggested_bufsize);
795         if(!fbuf) {
796             perror("realloc()");
797             exit(EX_OSERR);
798         }
799         fbuf_size = suggested_bufsize;
800     }
801
802     if(on_first_pdu) {
803         DynamicBuffer.offset = 0;
804         DynamicBuffer.length = 0;
805         DynamicBuffer.unbits = 0;
806         DynamicBuffer.allocated = 0;
807         DynamicBuffer.bytes_shifted = 0;
808         DynamicBuffer.nreallocs = 0;
809     }
810
811     old_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset;
812
813     /* Pretend immediate EOF */
814     rval.code = RC_WMORE;
815     rval.consumed = 0;
816
817     for(tolerate_eof = 1;    /* Allow EOF first time buffer is non-empty */
818         (rd = fread(fbuf, 1, fbuf_size, file))
819         || feof(file) == 0
820         || (tolerate_eof && DynamicBuffer.length)
821         ;) {
822         int      ecbits = 0;    /* Extra consumed bits in case of PER */
823         uint8_t *i_bptr;
824         size_t   i_size;
825
826         /*
827          * Copy the data over, or use the original buffer.
828          */
829         if(DynamicBuffer.allocated) {
830             /* Append new data into the existing dynamic buffer */
831             add_bytes_to_buffer(fbuf, rd);
832             i_bptr = DynamicBuffer.data + DynamicBuffer.offset;
833             i_size = DynamicBuffer.length;
834         } else {
835             i_bptr = fbuf;
836             i_size = rd;
837         }
838
839         DEBUG("Decoding %" ASN_PRI_SIZE " bytes", i_size);
840
841 #ifdef    JUNKTEST
842         junk_bytes_with_probability(i_bptr, i_size, opt_jprob);
843 #endif
844
845         if(is_syntax_PER(isyntax) && opt_nopad) {
846 #ifdef  ASN_DISABLE_PER_SUPPORT
847             rval.code = RC_FAIL;
848             rval.consumed = 0;
849 #else
850             if(isyntax == ATS_UNALIGNED_BASIC_PER)
851                 rval = uper_decode(opt_codec_ctx, pduType, (void **)&structure,
852                                    i_bptr, i_size, 0, DynamicBuffer.unbits);
853             else
854                 rval = aper_decode(opt_codec_ctx, pduType, (void **)&structure,
855                                    i_bptr, i_size, 0, DynamicBuffer.unbits);
856             /* uper_decode() returns bits! */
857             ecbits = rval.consumed % 8; /* Bits consumed from the last byte */
858             rval.consumed >>= 3;    /* Convert bits into bytes. */
859 #endif
860             /* Non-padded PER decoder */
861         } else {
862             rval = asn_decode(opt_codec_ctx, isyntax, pduType,
863                               (void **)&structure, i_bptr, i_size);
864         }
865         if(rval.code == RC_WMORE && !restartability_supported(isyntax)) {
866             /* PER does not support restartability */
867             ASN_STRUCT_FREE(*pduType, structure);
868             structure = 0;
869             rval.consumed = 0;
870             /* Continue accumulating data */
871         }
872
873         DEBUG("decode(%" ASN_PRI_SIZE ") consumed %" ASN_PRI_SIZE
874               "+%db (%" ASN_PRI_SIZE "), code %d",
875               DynamicBuffer.length, rval.consumed, ecbits, i_size, rval.code);
876
877         if(DynamicBuffer.allocated == 0) {
878             /*
879              * Flush remainder into the intermediate buffer.
880              */
881             if(rval.code != RC_FAIL && rval.consumed < rd) {
882                 add_bytes_to_buffer(fbuf + rval.consumed,
883                     rd - rval.consumed);
884                 buffer_shift_left(0, ecbits);
885                 DynamicBuffer.bytes_shifted = rval.consumed;
886                 rval.consumed = 0;
887                 ecbits = 0;
888             }
889         }
890
891         /*
892          * Adjust position inside the source buffer.
893          */
894         if(DynamicBuffer.allocated) {
895             DynamicBuffer.offset += rval.consumed;
896             DynamicBuffer.length -= rval.consumed;
897         } else {
898             DynamicBuffer.bytes_shifted += rval.consumed;
899         }
900
901         switch(rval.code) {
902         case RC_OK:
903             if(ecbits) buffer_shift_left(0, ecbits);
904             DEBUG("RC_OK, finishing up with %ld+%d",
905                 (long)rval.consumed, ecbits);
906             return structure;
907         case RC_WMORE:
908             DEBUG("RC_WMORE, continuing read=%ld, cons=%ld "
909                 " with %ld..%ld-%ld..%ld",
910                 (long)rd,
911                 (long)rval.consumed,
912                 (long)DynamicBuffer.offset,
913                 (long)DynamicBuffer.length,
914                 (long)DynamicBuffer.unbits,
915                 (long)DynamicBuffer.allocated);
916             if(!rd) tolerate_eof--;
917             continue;
918         case RC_FAIL:
919             break;
920         }
921         break;
922     }
923
924     DEBUG("Clean up partially decoded %s", pduType->name);
925     ASN_STRUCT_FREE(*pduType, structure);
926
927     new_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset;
928
929     /*
930      * Print a message and return failure only if not EOF,
931      * unless this is our first PDU (empty file).
932      */
933     if(on_first_pdu
934     || DynamicBuffer.length
935     || new_offset - old_offset > ((isyntax == ATS_BASIC_XER)?sizeof("\r\n")-1:0)
936     ) {
937
938 #ifdef    JUNKTEST
939         /*
940          * Nothing's wrong with being unable to decode junk.
941          * Simulate EOF.
942          */
943         if(opt_jprob != 0.0) {
944             junk_failures++;
945             errno = 0;
946             return 0;
947         }
948 #endif
949
950         DEBUG("ofp %d, no=%ld, oo=%ld, dbl=%ld",
951             on_first_pdu, (long)new_offset, (long)old_offset,
952             (long)DynamicBuffer.length);
953         fprintf(stderr, "%s: "
954             "Decode failed past byte %ld: %s\n",
955             name, (long)new_offset,
956             (rval.code == RC_WMORE)
957                 ? "Unexpected end of input"
958                 : "Input processing error");
959 #ifndef    ENOMSG
960 #define    ENOMSG EINVAL
961 #endif
962 #ifndef    EBADMSG
963 #define    EBADMSG EINVAL
964 #endif
965         errno = (rval.code == RC_WMORE) ? ENOMSG : EBADMSG;
966     } else {
967         /* Got EOF after a few successful PDUs */
968         errno = 0;
969     }
970
971     return 0;
972 }
973
974 /* Dump the buffer out to the specified FILE */
975 static int write_out(const void *buffer, size_t size, void *key) {
976     FILE *fp = (FILE *)key;
977     return (fwrite(buffer, 1, size, fp) == size) ? 0 : -1;
978 }
979
980 static int argument_is_stdin(char *av[], int idx) {
981     if(strcmp(av[idx], "-")) {
982         return 0; /* Certainly not <stdin> */
983     } else {
984         /* This might be <stdin>, unless `./program -- -` */
985         if(strcmp(av[-1], "--"))
986             return 1;
987         else
988             return 0;
989     }
990 }
991
992 static FILE *argument_to_file(char *av[], int idx) {
993     return argument_is_stdin(av, idx) ? stdin : fopen(av[idx], "rb");
994 }
995
996 static char *argument_to_name(char *av[], int idx) {
997     return argument_is_stdin(av, idx) ? "standard input" : av[idx];
998 }
999
1000 #ifdef    JUNKTEST
1001 /*
1002  * Fill bytes with some garbage with specified probability (more or less).
1003  */
1004 static void
1005 junk_bytes_with_probability(uint8_t *buf, size_t size, double prob) {
1006     static int junkmode;
1007     uint8_t *ptr;
1008     uint8_t *end;
1009     if(opt_jprob <= 0.0) return;
1010     for(ptr = buf, end = ptr + size; ptr < end; ptr++) {
1011         int byte = *ptr;
1012         if(junkmode++ & 1) {
1013             if((((double)random() / RAND_MAX) < prob))
1014                 byte = random() & 0xff;
1015         } else {
1016 #define    BPROB(b)    ((((double)random() / RAND_MAX) < prob) ? b : 0)
1017             byte ^= BPROB(0x80);
1018             byte ^= BPROB(0x40);
1019             byte ^= BPROB(0x20);
1020             byte ^= BPROB(0x10);
1021             byte ^= BPROB(0x08);
1022             byte ^= BPROB(0x04);
1023             byte ^= BPROB(0x02);
1024             byte ^= BPROB(0x01);
1025         }
1026         if(byte != *ptr) {
1027             *ptr = byte;
1028         }
1029     }
1030 }
1031 #endif    /* JUNKTEST */
1032