]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/other/smux/asn1/converter-sample.c
Handlers moved to separate file and added to compilation
[stg.git] / projects / stargazer / plugins / other / smux / asn1 / converter-sample.c
1 /*
2  * Generic converter template for a selected ASN.1 type.
3  * Copyright (c) 2005, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
4  * 
5  * To compile with your own ASN.1 type, please redefine the PDU as shown:
6  * 
7  * cc -DPDU=MyCustomType -o myDecoder.o -c converter-sample.c
8  */
9 #ifdef  HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <stdlib.h>     /* for atoi(3) */
15 #include <unistd.h>     /* for getopt(3) */
16 #include <string.h>     /* for strerror(3) */
17 #include <sysexits.h>   /* for EX_* exit codes */
18 #include <assert.h>     /* for assert(3) */
19 #include <errno.h>      /* for errno */
20
21 #include <asn_application.h>
22 #include <asn_internal.h>       /* for _ASN_DEFAULT_STACK_MAX */
23
24 /* Convert "Type" defined by -DPDU into "asn_DEF_Type" */
25 #define ASN_DEF_PDU(t)  asn_DEF_ ## t
26 #define DEF_PDU_Type(t) ASN_DEF_PDU(t)
27 #define PDU_Type        DEF_PDU_Type(PDU)
28
29 extern asn_TYPE_descriptor_t PDU_Type;  /* ASN.1 type to be decoded */
30 #ifdef  ASN_PDU_COLLECTION              /* Generated by asn1c: -pdu=... */
31 extern asn_TYPE_descriptor_t *asn_pdu_collection[];
32 #endif
33
34 /*
35  * Open file and parse its contens.
36  */
37 static void *data_decode_from_file(asn_TYPE_descriptor_t *asnTypeOfPDU,
38         FILE *file, const char *name, ssize_t suggested_bufsize, int first_pdu);
39 static int write_out(const void *buffer, size_t size, void *key);
40 static FILE *argument_to_file(char *av[], int idx);
41 static char *argument_to_name(char *av[], int idx);
42
43        int opt_debug;   /* -d (or -dd) */
44 static int opt_check;   /* -c (constraints checking) */
45 static int opt_stack;   /* -s (maximum stack size) */
46 static int opt_ippad;   /* -per-padded (PER input is byte-padded) */
47 static int opt_onepdu;  /* -1 (decode single PDU) */
48
49 /* Input data format selector */
50 static enum input_format {
51         INP_BER,        /* -iber: BER input */
52         INP_XER,        /* -ixer: XER input */
53         INP_PER         /* -iper: Unaligned PER input */
54 } iform;        /* -i<format> */
55
56 /* Output data format selector */
57 static enum output_format {
58         OUT_XER,        /* -oxer: XER (XML) output */
59         OUT_DER,        /* -oder: DER (BER) output */
60         OUT_PER,        /* -oper: Unaligned PER output */
61         OUT_TEXT,       /* -otext: semi-structured text */
62         OUT_NULL        /* -onull: No pretty-printing */
63 } oform;        /* -o<format> */
64
65 /* Debug output function */
66 static inline void
67 DEBUG(const char *fmt, ...) {
68         va_list ap;
69         if(!opt_debug) return;
70         fprintf(stderr, "AD: ");
71         va_start(ap, fmt);
72         vfprintf(stderr, fmt, ap);
73         va_end(ap);
74         fprintf(stderr, "\n");
75 }
76
77 int
78 main(int ac, char *av[]) {
79         static asn_TYPE_descriptor_t *pduType = &PDU_Type;
80         ssize_t suggested_bufsize = 8192;  /* close or equal to stdio buffer */
81         int number_of_iterations = 1;
82         int num;
83         int ch;
84
85         /* Figure out if Unaligned PER needs to be default */
86         if(pduType->uper_decoder)
87                 iform = INP_PER;
88
89         /*
90          * Pocess the command-line argments.
91          */
92         while((ch = getopt(ac, av, "i:o:1b:cdn:p:hs:")) != -1)
93         switch(ch) {
94         case 'i':
95                 if(optarg[0] == 'b') { iform = INP_BER; break; }
96                 if(optarg[0] == 'x') { iform = INP_XER; break; }
97                 if(pduType->uper_decoder
98                 && optarg[0] == 'p') { iform = INP_PER; break; }
99                 fprintf(stderr, "-i<format>: '%s': improper format selector\n",
100                         optarg);
101                 exit(EX_UNAVAILABLE);
102         case 'o':
103                 if(optarg[0] == 'd') { oform = OUT_DER; break; }
104                 if(pduType->uper_encoder
105                 && optarg[0] == 'p') { oform = OUT_PER; break; }
106                 if(optarg[0] == 'x') { oform = OUT_XER; break; }
107                 if(optarg[0] == 't') { oform = OUT_TEXT; break; }
108                 if(optarg[0] == 'n') { oform = OUT_NULL; break; }
109                 fprintf(stderr, "-o<format>: '%s': improper format selector\n",
110                         optarg);
111                 exit(EX_UNAVAILABLE);
112         case '1':
113                 opt_onepdu = 1;
114                 break;
115         case 'b':
116                 suggested_bufsize = atoi(optarg);
117                 if(suggested_bufsize < 1
118                         || suggested_bufsize > 16 * 1024 * 1024) {
119                         fprintf(stderr,
120                                 "-b %s: Improper buffer size (1..16M)\n",
121                                 optarg);
122                         exit(EX_UNAVAILABLE);
123                 }
124                 break;
125         case 'c':
126                 opt_check = 1;
127                 break;
128         case 'd':
129                 opt_debug++;    /* Double -dd means ASN.1 debug */
130                 break;
131         case 'n':
132                 number_of_iterations = atoi(optarg);
133                 if(number_of_iterations < 1) {
134                         fprintf(stderr,
135                                 "-n %s: Improper iterations count\n", optarg);
136                         exit(EX_UNAVAILABLE);
137                 }
138                 break;
139         case 'p':
140                 if(strcmp(optarg, "er-padded") == 0) {
141                         opt_ippad = 1;
142                         break;
143                 }
144 #ifdef  ASN_PDU_COLLECTION
145                 if(strcmp(optarg, "list") == 0) {
146                         asn_TYPE_descriptor_t **pdu = asn_pdu_collection;
147                         fprintf(stderr, "Available PDU types:\n");
148                         for(; *pdu; pdu++) printf("%s\n", (*pdu)->name);
149                         exit(0);
150                 } else if(optarg[0] >= 'A' && optarg[0] <= 'Z') {
151                         asn_TYPE_descriptor_t **pdu = asn_pdu_collection;
152                         while(*pdu && strcmp((*pdu)->name, optarg)) pdu++;
153                         if(*pdu) { pduType = *pdu; break; }
154                         fprintf(stderr, "-p %s: Unrecognized PDU\n", optarg);
155                 }
156 #endif  /* ASN_PDU_COLLECTION */
157                 fprintf(stderr, "-p %s: Unrecognized option\n", optarg);
158                 exit(EX_UNAVAILABLE);
159         case 's':
160                 opt_stack = atoi(optarg);
161                 if(opt_stack < 0) {
162                         fprintf(stderr,
163                                 "-s %s: Non-negative value expected\n",
164                                 optarg);
165                         exit(EX_UNAVAILABLE);
166                 }
167                 break;
168         case 'h':
169         default:
170                 fprintf(stderr, "Usage: %s [options] <data.ber> ...\n", av[0]);
171                 fprintf(stderr, "Where options are:\n");
172                 if(pduType->uper_decoder)
173                 fprintf(stderr,
174                 "  -iper        Input is in Unaligned PER (Packed Encoding Rules) (DEFAULT)\n");
175                 fprintf(stderr,
176                 "  -iber        Input is in BER (Basic Encoding Rules)%s\n",
177                         iform == INP_PER ? "" : " (DEFAULT)");
178                 fprintf(stderr,
179                 "  -ixer        Input is in XER (XML Encoding Rules)\n");
180                 if(pduType->uper_encoder)
181                 fprintf(stderr,
182                 "  -oper        Output in Unaligned PER (Packed Encoding Rules)\n");
183                 fprintf(stderr,
184                 "  -oder        Output in DER (Distinguished Encoding Rules)\n"
185                 "  -oxer        Output in XER (XML Encoding Rules) (DEFAULT)\n"
186                 "  -otext       Output in plain semi-structured text (dump)\n"
187                 "  -onull       Verify (decode) input, but do not output\n");
188                 if(pduType->uper_decoder)
189                 fprintf(stderr,
190                 "  -per-padded  Assume PER PDUs are byte-padded (-iper)\n");
191 #ifdef  ASN_PDU_COLLECTION
192                 fprintf(stderr,
193                 "  -p <PDU>     Specify PDU type to decode\n"
194                 "  -p list      List available PDUs\n");
195 #endif  /* ASN_PDU_COLLECTION */
196                 fprintf(stderr,
197                 "  -1           Decode only the first PDU in file\n"
198                 "  -b <size>    Set the i/o buffer size (default is %ld)\n"
199                 "  -c           Check ASN.1 constraints after decoding\n"
200                 "  -d           Enable debugging (-dd is even better)\n"
201                 "  -n <num>     Process files <num> times\n"
202                 "  -s <size>    Set the stack usage limit (default is %d)\n"
203                 , (long)suggested_bufsize, _ASN_DEFAULT_STACK_MAX);
204                 exit(EX_USAGE);
205         }
206
207         ac -= optind;
208         av += optind;
209
210         if(ac < 1) {
211                 fprintf(stderr, "%s: No input files specified. "
212                                 "Try '-h' for more information\n",
213                                 av[-optind]);
214                 exit(EX_USAGE);
215         }
216
217         setvbuf(stdout, 0, _IOLBF, 0);
218
219         for(num = 0; num < number_of_iterations; num++) {
220           int ac_i;
221           /*
222            * Process all files in turn.
223            */
224           for(ac_i = 0; ac_i < ac; ac_i++) {
225             asn_enc_rval_t erv;
226             void *structure;    /* Decoded structure */
227             FILE *file = argument_to_file(av, ac_i);
228             char *name = argument_to_name(av, ac_i);
229             int first_pdu;
230
231             for(first_pdu = 1; first_pdu || !opt_onepdu; first_pdu = 0) {
232                 /*
233                  * Decode the encoded structure from file.
234                  */
235                 structure = data_decode_from_file(pduType,
236                                 file, name, suggested_bufsize, first_pdu);
237                 if(!structure) {
238                         if(errno) {
239                                 /* Error message is already printed */
240                                 exit(EX_DATAERR);
241                         } else {
242                                 /* EOF */
243                                 break;
244                         }
245                 }
246
247                 /* Check ASN.1 constraints */
248                 if(opt_check) {
249                         char errbuf[128];
250                         size_t errlen = sizeof(errbuf);
251                         if(asn_check_constraints(pduType, structure,
252                                 errbuf, &errlen)) {
253                                 fprintf(stderr, "%s: ASN.1 constraint "
254                                         "check failed: %s\n", name, errbuf);
255                                 exit(EX_DATAERR);
256                         }
257                 }
258
259                 switch(oform) {
260                 case OUT_NULL:
261                         fprintf(stderr, "%s: decoded successfully\n", name);
262                         break;
263                 case OUT_TEXT:  /* -otext */
264                         asn_fprint(stdout, pduType, structure);
265                         break;
266                 case OUT_XER:   /* -oxer */
267                         if(xer_fprint(stdout, pduType, structure)) {
268                                 fprintf(stderr,
269                                         "%s: Cannot convert %s into XML\n",
270                                         name, pduType->name);
271                                 exit(EX_UNAVAILABLE);
272                         }
273                         break;
274                 case OUT_DER:
275                         erv = der_encode(pduType, structure, write_out, stdout);
276                         if(erv.encoded < 0) {
277                                 fprintf(stderr,
278                                         "%s: Cannot convert %s into DER\n",
279                                         name, pduType->name);
280                                 exit(EX_UNAVAILABLE);
281                         }
282                         DEBUG("Encoded in %ld bytes of DER", (long)erv.encoded);
283                         break;
284                 case OUT_PER:
285                         erv = uper_encode(pduType, structure, write_out, stdout);
286                         if(erv.encoded < 0) {
287                                 fprintf(stderr,
288                                 "%s: Cannot convert %s into Unaligned PER\n",
289                                         name, pduType->name);
290                                 exit(EX_UNAVAILABLE);
291                         }
292                         DEBUG("Encoded in %ld bits of UPER", (long)erv.encoded);
293                         break;
294                 }
295
296                 ASN_STRUCT_FREE(*pduType, structure);
297             }
298
299             if(file && file != stdin)
300                 fclose(file);
301           }
302         }
303
304         return 0;
305 }
306
307 static struct dynamic_buffer {
308         uint8_t *data;          /* Pointer to the data bytes */
309         size_t offset;          /* Offset from the start */
310         size_t length;          /* Length of meaningful contents */
311         size_t unbits;          /* Unused bits in the last byte */
312         size_t allocated;       /* Allocated memory for data */
313         int    nreallocs;       /* Number of data reallocations */
314         off_t  bytes_shifted;   /* Number of bytes ever shifted */
315 } DynamicBuffer;
316
317 static void
318 buffer_dump() {
319         uint8_t *p = DynamicBuffer.data + DynamicBuffer.offset;
320         uint8_t *e = p + DynamicBuffer.length - (DynamicBuffer.unbits ? 1 : 0);
321         if(!opt_debug) return;
322         DEBUG("Buffer: { d=%p, o=%ld, l=%ld, u=%ld, a=%ld, s=%ld }",
323                 DynamicBuffer.data,
324                 (long)DynamicBuffer.offset,
325                 (long)DynamicBuffer.length,
326                 (long)DynamicBuffer.unbits,
327                 (long)DynamicBuffer.allocated,
328                 (long)DynamicBuffer.bytes_shifted);
329         for(; p < e; p++) {
330                 fprintf(stderr, " %c%c%c%c%c%c%c%c",
331                         ((*p >> 7) & 1) ? '1' : '0',
332                         ((*p >> 6) & 1) ? '1' : '0',
333                         ((*p >> 5) & 1) ? '1' : '0',
334                         ((*p >> 4) & 1) ? '1' : '0',
335                         ((*p >> 3) & 1) ? '1' : '0',
336                         ((*p >> 2) & 1) ? '1' : '0',
337                         ((*p >> 1) & 1) ? '1' : '0',
338                         ((*p >> 0) & 1) ? '1' : '0');
339         }
340         if(DynamicBuffer.unbits) {
341                 int shift;
342                 fprintf(stderr, " ");
343                 for(shift = 7; shift >= DynamicBuffer.unbits; shift--)
344                         fprintf(stderr, "%c", ((*p >> shift) & 1) ? '1' : '0');
345                 fprintf(stderr, " %d:%d\n",
346                         DynamicBuffer.length - 1, 8 - DynamicBuffer.unbits);
347         } else {
348                 fprintf(stderr, " %d\n", DynamicBuffer.length);
349         }
350 }
351
352 /*
353  * Move the buffer content left N bits, possibly joining it with
354  * preceeding content.
355  */
356 static void
357 buffer_shift_left(size_t offset, int bits) {
358         uint8_t *ptr = DynamicBuffer.data + DynamicBuffer.offset + offset;
359         uint8_t *end = DynamicBuffer.data + DynamicBuffer.offset
360                         + DynamicBuffer.length - 1;
361         
362         if(!bits) return;
363
364         DEBUG("Shifting left %d bits off %ld (o=%ld, u=%ld, l=%ld)",
365                 bits, (long)offset,
366                 (long)DynamicBuffer.offset,
367                 (long)DynamicBuffer.unbits,
368                 (long)DynamicBuffer.length);
369
370         if(offset) {
371                 int right;
372                 right = ptr[0] >> (8 - bits);
373
374                 DEBUG("oleft: %c%c%c%c%c%c%c%c",
375                         ((ptr[-1] >> 7) & 1) ? '1' : '0',
376                         ((ptr[-1] >> 6) & 1) ? '1' : '0',
377                         ((ptr[-1] >> 5) & 1) ? '1' : '0',
378                         ((ptr[-1] >> 4) & 1) ? '1' : '0',
379                         ((ptr[-1] >> 3) & 1) ? '1' : '0',
380                         ((ptr[-1] >> 2) & 1) ? '1' : '0',
381                         ((ptr[-1] >> 1) & 1) ? '1' : '0',
382                         ((ptr[-1] >> 0) & 1) ? '1' : '0');
383
384                 DEBUG("oriht: %c%c%c%c%c%c%c%c",
385                         ((ptr[0] >> 7) & 1) ? '1' : '0',
386                         ((ptr[0] >> 6) & 1) ? '1' : '0',
387                         ((ptr[0] >> 5) & 1) ? '1' : '0',
388                         ((ptr[0] >> 4) & 1) ? '1' : '0',
389                         ((ptr[0] >> 3) & 1) ? '1' : '0',
390                         ((ptr[0] >> 2) & 1) ? '1' : '0',
391                         ((ptr[0] >> 1) & 1) ? '1' : '0',
392                         ((ptr[0] >> 0) & 1) ? '1' : '0');
393
394                 DEBUG("mriht: %c%c%c%c%c%c%c%c",
395                         ((right >> 7) & 1) ? '1' : '0',
396                         ((right >> 6) & 1) ? '1' : '0',
397                         ((right >> 5) & 1) ? '1' : '0',
398                         ((right >> 4) & 1) ? '1' : '0',
399                         ((right >> 3) & 1) ? '1' : '0',
400                         ((right >> 2) & 1) ? '1' : '0',
401                         ((right >> 1) & 1) ? '1' : '0',
402                         ((right >> 0) & 1) ? '1' : '0');
403
404                 ptr[-1] = (ptr[-1] & (0xff << bits)) | right;
405
406                 DEBUG("after: %c%c%c%c%c%c%c%c",
407                         ((ptr[-1] >> 7) & 1) ? '1' : '0',
408                         ((ptr[-1] >> 6) & 1) ? '1' : '0',
409                         ((ptr[-1] >> 5) & 1) ? '1' : '0',
410                         ((ptr[-1] >> 4) & 1) ? '1' : '0',
411                         ((ptr[-1] >> 3) & 1) ? '1' : '0',
412                         ((ptr[-1] >> 2) & 1) ? '1' : '0',
413                         ((ptr[-1] >> 1) & 1) ? '1' : '0',
414                         ((ptr[-1] >> 0) & 1) ? '1' : '0');
415         }
416
417         buffer_dump();
418
419         for(; ptr < end; ptr++) {
420                 int right = ptr[1] >> (8 - bits);
421                 *ptr = (*ptr << bits) | right;
422         }
423         *ptr <<= bits;
424
425         DEBUG("Unbits [%d=>", (int)DynamicBuffer.unbits);
426         if(DynamicBuffer.unbits == 0) {
427                 DynamicBuffer.unbits += bits;
428         } else {
429                 DynamicBuffer.unbits += bits;
430                 if(DynamicBuffer.unbits > 7) {
431                         DynamicBuffer.unbits -= 8;
432                         DynamicBuffer.length--;
433                         DynamicBuffer.bytes_shifted++;
434                 }
435         }
436         DEBUG("Unbits =>%d]", (int)DynamicBuffer.unbits);
437
438         buffer_dump();
439
440         DEBUG("Shifted. Now (o=%ld, u=%ld l=%ld)",
441                 (long)DynamicBuffer.offset,
442                 (long)DynamicBuffer.unbits,
443                 (long)DynamicBuffer.length);
444         
445
446 }
447
448 /*
449  * Ensure that the buffer contains at least this amount of free space.
450  */
451 static void add_bytes_to_buffer(const void *data2add, size_t bytes) {
452
453         if(bytes == 0) return;
454
455         DEBUG("=> add_bytes(%ld) { o=%ld l=%ld u=%ld, s=%ld }",
456                 (long)bytes,
457                 (long)DynamicBuffer.offset,
458                 (long)DynamicBuffer.length,
459                 (long)DynamicBuffer.unbits,
460                 (long)DynamicBuffer.allocated);
461
462         if(DynamicBuffer.allocated
463         >= (DynamicBuffer.offset + DynamicBuffer.length + bytes)) {
464                 DEBUG("\tNo buffer reallocation is necessary");
465         } else if(bytes <= DynamicBuffer.offset) {
466                 DEBUG("\tContents shifted by %ld", DynamicBuffer.offset);
467
468                 /* Shift the buffer contents */
469                 memmove(DynamicBuffer.data,
470                         DynamicBuffer.data + DynamicBuffer.offset,
471                         DynamicBuffer.length);
472                 DynamicBuffer.bytes_shifted += DynamicBuffer.offset;
473                 DynamicBuffer.offset = 0;
474         } else {
475                 size_t newsize = (DynamicBuffer.allocated << 2) + bytes;
476                 void *p = MALLOC(newsize);
477                 if(!p) {
478                         perror("malloc()");
479                         exit(EX_OSERR);
480                 }
481                 memcpy(p,
482                         DynamicBuffer.data + DynamicBuffer.offset,
483                         DynamicBuffer.length);
484                 FREEMEM(DynamicBuffer.data);
485                 DynamicBuffer.data = (char *)p;
486                 DynamicBuffer.offset = 0;
487                 DynamicBuffer.allocated = newsize;
488                 DynamicBuffer.nreallocs++;
489                 DEBUG("\tBuffer reallocated to %ld (%d time)",
490                         newsize, DynamicBuffer.nreallocs);
491         }
492
493         memcpy(DynamicBuffer.data
494                 + DynamicBuffer.offset + DynamicBuffer.length,
495                 data2add, bytes);
496         DynamicBuffer.length += bytes;
497         if(DynamicBuffer.unbits) {
498                 int bits = DynamicBuffer.unbits;
499                 DynamicBuffer.unbits = 0;
500                 buffer_shift_left(DynamicBuffer.length - bytes, bits);
501         }
502
503         DEBUG("<= add_bytes(%ld) { o=%ld l=%ld u=%ld, s=%ld }",
504                 (long)bytes,
505                 (long)DynamicBuffer.offset,
506                 (long)DynamicBuffer.length,
507                 (long)DynamicBuffer.unbits,
508                 (long)DynamicBuffer.allocated);
509 }
510
511 static void *
512 data_decode_from_file(asn_TYPE_descriptor_t *pduType, FILE *file, const char *name, ssize_t suggested_bufsize, int on_first_pdu) {
513         static uint8_t *fbuf;
514         static ssize_t fbuf_size;
515         static asn_codec_ctx_t s_codec_ctx;
516         asn_codec_ctx_t *opt_codec_ctx = 0;
517         void *structure = 0;
518         asn_dec_rval_t rval;
519         size_t old_offset;      
520         size_t new_offset;
521         int tolerate_eof;
522         size_t rd;
523
524         if(!file) {
525                 fprintf(stderr, "%s: %s\n", name, strerror(errno));
526                 errno = EINVAL;
527                 return 0;
528         }
529
530         if(opt_stack) {
531                 s_codec_ctx.max_stack_size = opt_stack;
532                 opt_codec_ctx = &s_codec_ctx;
533         }
534
535         DEBUG("Processing %s", name);
536
537         /* prepare the file buffer */
538         if(fbuf_size != suggested_bufsize) {
539                 fbuf = (char *)REALLOC(fbuf, suggested_bufsize);
540                 if(!fbuf) {
541                         perror("realloc()");
542                         exit(EX_OSERR);
543                 }
544                 fbuf_size = suggested_bufsize;
545         }
546
547         if(on_first_pdu) {
548                 DynamicBuffer.offset = 0;
549                 DynamicBuffer.length = 0;
550                 DynamicBuffer.unbits = 0;
551                 DynamicBuffer.allocated = 0;
552                 DynamicBuffer.bytes_shifted = 0;
553                 DynamicBuffer.nreallocs = 0;
554         }
555
556         old_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset;
557
558         /* Pretend immediate EOF */
559         rval.code = RC_WMORE;
560         rval.consumed = 0;
561
562         for(tolerate_eof = 1;   /* Allow EOF first time buffer is non-empty */
563             (rd = fread(fbuf, 1, fbuf_size, file))
564                 || feof(file) == 0
565                 || (tolerate_eof && DynamicBuffer.length)
566             ;) {
567                 int    ecbits = 0;      /* Extra consumed bits in case of PER */
568                 char  *i_bptr;
569                 size_t i_size;
570
571                 /*
572                  * Copy the data over, or use the original buffer.
573                  */
574                 if(DynamicBuffer.allocated) {
575                         /* Append new data into the existing dynamic buffer */
576                         add_bytes_to_buffer(fbuf, rd);
577                         i_bptr = DynamicBuffer.data + DynamicBuffer.offset;
578                         i_size = DynamicBuffer.length;
579                 } else {
580                         i_bptr = fbuf;
581                         i_size = rd;
582                 }
583
584                 DEBUG("Decoding %ld bytes", (long)i_size);
585
586                 switch(iform) {
587                 case INP_BER:
588                         rval = ber_decode(opt_codec_ctx, pduType,
589                                 (void **)&structure, i_bptr, i_size);
590                         break;
591                 case INP_XER:
592                         rval = xer_decode(opt_codec_ctx, pduType,
593                                 (void **)&structure, i_bptr, i_size);
594                         break;
595                 case INP_PER:
596                         rval = uper_decode(opt_codec_ctx, pduType,
597                                 (void **)&structure, i_bptr, i_size, 0,
598                                 DynamicBuffer.unbits);
599                         ecbits = rval.consumed % 8;     /* Extra bits */
600                         rval.consumed /= 8; /* Convert to value in bytes! */
601                         /* Check if input is byte-padded at the end */
602                         if(opt_ippad && ecbits && rval.code == RC_OK) {
603                                 rval.consumed++;
604                                 ecbits = 0;
605                         }
606                         break;
607                 }
608                 DEBUG("decode(%ld) consumed %ld+%db (%ld), code %d",
609                         (long)DynamicBuffer.length,
610                         (long)rval.consumed, ecbits, (long)i_size,
611                         rval.code);
612
613                 if(DynamicBuffer.allocated == 0) {
614                         /*
615                          * Flush remainder into the intermediate buffer.
616                          */
617                         if(rval.code != RC_FAIL && rval.consumed < rd) {
618                                 add_bytes_to_buffer(fbuf + rval.consumed,
619                                         rd - rval.consumed);
620                                 buffer_shift_left(0, ecbits);
621                                 DynamicBuffer.bytes_shifted = rval.consumed;
622                                 rval.consumed = 0;
623                                 ecbits = 0;
624                         }
625                 }
626
627                 /*
628                  * Adjust position inside the source buffer.
629                  */
630                 if(DynamicBuffer.allocated) {
631                         DynamicBuffer.offset += rval.consumed;
632                         DynamicBuffer.length -= rval.consumed;
633                 } else {
634                         DynamicBuffer.bytes_shifted += rval.consumed;
635                 }
636
637                 switch(rval.code) {
638                 case RC_OK:
639                         if(ecbits) buffer_shift_left(0, ecbits);
640                         DEBUG("RC_OK, finishing up with %ld+%d",
641                                 (long)rval.consumed, ecbits);
642                         return structure;
643                 case RC_WMORE:
644                         DEBUG("RC_WMORE, continuing read=%ld, cons=%ld "
645                                 " with %ld..%ld-%ld..%ld",
646                                 (long)rd,
647                                 (long)rval.consumed,
648                                 (long)DynamicBuffer.offset,
649                                 (long)DynamicBuffer.length,
650                                 (long)DynamicBuffer.unbits,
651                                 (long)DynamicBuffer.allocated);
652                         if(!rd) tolerate_eof--;
653                         continue;
654                 case RC_FAIL:
655                         break;
656                 }
657                 break;
658         }
659
660         /* Clean up partially decoded structure */
661         ASN_STRUCT_FREE(*pduType, structure);
662
663         new_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset;
664
665         /*
666          * Print a message and return failure only if not EOF,
667          * unless this is our first PDU (empty file).
668          */
669         if(on_first_pdu
670         || DynamicBuffer.length
671         || new_offset - old_offset > ((iform == INP_XER)?sizeof("\r\n")-1:0)
672         ) {
673                 DEBUG("ofp %d, no=%ld, oo=%ld, dbl=%ld",
674                         on_first_pdu, (long)new_offset, (long)old_offset,
675                         (long)DynamicBuffer.length);
676                 fprintf(stderr, "%s: "
677                         "Decode failed past byte %ld: %s\n",
678                         name, (long)new_offset,
679                         (rval.code == RC_WMORE)
680                                 ? "Unexpected end of input"
681                                 : "Input processing error");
682 #ifndef ENOMSG
683 #define ENOMSG EINVAL
684 #endif
685 #ifndef EBADMSG
686 #define EBADMSG EINVAL
687 #endif
688                 errno = (rval.code == RC_WMORE) ? ENOMSG : EBADMSG;
689         } else {
690                 /* Got EOF after a few successful PDUs */
691                 errno = 0;
692         }
693
694         return 0;
695 }
696
697 /* Dump the buffer out to the specified FILE */
698 static int write_out(const void *buffer, size_t size, void *key) {
699         FILE *fp = (FILE *)key;
700         return (fwrite(buffer, 1, size, fp) == size) ? 0 : -1;
701 }
702
703 static int argument_is_stdin(char *av[], int idx) {
704         if(strcmp(av[idx], "-")) {
705                 return 0;       /* Certainly not <stdin> */
706         } else {
707                 /* This might be <stdin>, unless `./program -- -` */
708                 if(strcmp(av[-1], "--"))
709                         return 1;
710                 else
711                         return 0;
712         }
713 }
714
715 static FILE *argument_to_file(char *av[], int idx) {
716         return argument_is_stdin(av, idx)
717                 ? stdin
718                 : fopen(av[idx], "r");
719 }
720
721 static char *argument_to_name(char *av[], int idx) {
722         return argument_is_stdin(av, idx)
723                 ? "standard input"
724                 : av[idx];
725 }