]> git.stg.codes - stg.git/blob - stglibs/smux.lib/OBJECT_IDENTIFIER.c
Help updated
[stg.git] / stglibs / smux.lib / OBJECT_IDENTIFIER.c
1 /*-
2  * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
3  * Redistribution and modifications are permitted subject to BSD license.
4  */
5 #include <asn_internal.h>
6 #include <OBJECT_IDENTIFIER.h>
7 #include <limits.h>     /* for CHAR_BIT */
8 #include <errno.h>
9
10 /*
11  * OBJECT IDENTIFIER basic type description.
12  */
13 static ber_tlv_tag_t asn_DEF_OBJECT_IDENTIFIER_tags[] = {
14         (ASN_TAG_CLASS_UNIVERSAL | (6 << 2))
15 };
16 asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER = {
17         "OBJECT IDENTIFIER",
18         "OBJECT_IDENTIFIER",
19         ASN__PRIMITIVE_TYPE_free,
20         OBJECT_IDENTIFIER_print,
21         OBJECT_IDENTIFIER_constraint,
22         ber_decode_primitive,
23         der_encode_primitive,
24         OBJECT_IDENTIFIER_decode_xer,
25         OBJECT_IDENTIFIER_encode_xer,
26         0, 0,
27         0, /* Use generic outmost tag fetcher */
28         asn_DEF_OBJECT_IDENTIFIER_tags,
29         sizeof(asn_DEF_OBJECT_IDENTIFIER_tags)
30             / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]),
31         asn_DEF_OBJECT_IDENTIFIER_tags, /* Same as above */
32         sizeof(asn_DEF_OBJECT_IDENTIFIER_tags)
33             / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]),
34         0,      /* No PER visible constraints */
35         0, 0,   /* No members */
36         0       /* No specifics */
37 };
38
39
40 int
41 OBJECT_IDENTIFIER_constraint(asn_TYPE_descriptor_t *td, const void *sptr,
42                 asn_app_constraint_failed_f *ctfailcb, void *app_key) {
43         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
44
45         if(st && st->buf) {
46                 if(st->size < 1) {
47                         _ASN_CTFAIL(app_key, td,
48                                 "%s: at least one numerical value "
49                                 "expected (%s:%d)",
50                                 td->name, __FILE__, __LINE__);
51                         return -1;
52                 }
53         } else {
54                 _ASN_CTFAIL(app_key, td,
55                         "%s: value not given (%s:%d)",
56                         td->name, __FILE__, __LINE__);
57                 return -1;
58         }
59
60         return 0;
61 }
62
63
64 int
65 OBJECT_IDENTIFIER_get_single_arc(uint8_t *arcbuf, unsigned int arclen, signed int add, void *rvbufp, unsigned int rvsize) {
66         unsigned LE __attribute__ ((unused)) = 1; /* Little endian (x86) */
67         uint8_t *arcend = arcbuf + arclen;      /* End of arc */
68         unsigned int cache = 0; /* No more than 14 significant bits */
69         unsigned char *rvbuf = (unsigned char *)rvbufp;
70         unsigned char *rvstart = rvbuf; /* Original start of the value buffer */
71         int inc;        /* Return value growth direction */
72
73         rvsize *= CHAR_BIT;     /* bytes to bits */
74         arclen *= 7;            /* bytes to bits */
75
76         /*
77          * The arc has the number of bits
78          * cannot be represented using supplied return value type.
79          */
80         if(arclen > rvsize) {
81                 if(arclen > (rvsize + CHAR_BIT)) {
82                         errno = ERANGE; /* Overflow */
83                         return -1;
84                 } else {
85                         /*
86                          * Even if the number of bits in the arc representation
87                          * is higher than the width of supplied * return value
88                          * type, there is still possible to fit it when there
89                          * are few unused high bits in the arc value
90                          * representaion.
91                          * 
92                          * Moreover, there is a possibility that the
93                          * number could actually fit the arc space, given
94                          * that add is negative, but we don't handle
95                          * such "temporary lack of precision" situation here.
96                          * May be considered as a bug.
97                          */
98                         uint8_t mask = (0xff << (7-(arclen - rvsize))) & 0x7f;
99                         if((*arcbuf & mask)) {
100                                 errno = ERANGE; /* Overflow */
101                                 return -1;
102                         }
103                         /* Fool the routine computing unused bits */
104                         arclen -= 7;
105                         cache = *arcbuf & 0x7f;
106                         arcbuf++;
107                 }
108         }
109
110         /* Faster path for common size */
111         if(rvsize == (CHAR_BIT * sizeof(unsigned long))) {
112                 unsigned long accum;
113                 /* Gather all bits into the accumulator */
114                 for(accum = cache; arcbuf < arcend; arcbuf++)
115                         accum = (accum << 7) | (*arcbuf & ~0x80);
116                 if(accum < (unsigned)-add) {
117                         errno = ERANGE; /* Overflow */
118                         return -1;
119                 }
120                 *(unsigned long *)rvbuf = accum + add;  /* alignment OK! */
121                 return 0;
122         }
123
124 #ifndef WORDS_BIGENDIAN
125         if(*(unsigned char *)&LE) {     /* Little endian (x86) */
126                 /* "Convert" to big endian */
127                 rvbuf += rvsize / CHAR_BIT - 1;
128                 rvstart--;
129                 inc = -1;       /* Descending */
130         } else
131 #endif  /* !WORDS_BIGENDIAN */
132                 inc = +1;       /* Big endian is known [at compile time] */
133
134         {
135                 int bits;       /* typically no more than 3-4 bits */
136
137                 /* Clear the high unused bits */
138                 for(bits = rvsize - arclen;
139                         bits > CHAR_BIT;
140                                 rvbuf += inc, bits -= CHAR_BIT)
141                                 *rvbuf = 0;
142
143                 /* Fill the body of a value */
144                 for(; arcbuf < arcend; arcbuf++) {
145                         cache = (cache << 7) | (*arcbuf & 0x7f);
146                         bits += 7;
147                         if(bits >= CHAR_BIT) {
148                                 bits -= CHAR_BIT;
149                                 *rvbuf = (cache >> bits);
150                                 rvbuf += inc;
151                         }
152                 }
153                 if(bits) {
154                         *rvbuf = cache;
155                         rvbuf += inc;
156                 }
157         }
158
159         if(add) {
160                 for(rvbuf -= inc; rvbuf != rvstart; rvbuf -= inc) {
161                         int v = add + *rvbuf;
162                         if(v & (-1 << CHAR_BIT)) {
163                                 *rvbuf = (unsigned char)(v + (1 << CHAR_BIT));
164                                 add = -1;
165                         } else {
166                                 *rvbuf = v;
167                                 break;
168                         }
169                 }
170                 if(rvbuf == rvstart) {
171                         /* No space to carry over */
172                         errno = ERANGE; /* Overflow */
173                         return -1;
174                 }
175         }
176
177         return 0;
178 }
179
180 ssize_t
181 OBJECT_IDENTIFIER__dump_arc(uint8_t *arcbuf, int arclen, int add,
182                 asn_app_consume_bytes_f *cb, void *app_key) {
183         char scratch[64];       /* Conservative estimate */
184         unsigned long accum;    /* Bits accumulator */
185         char *p;                /* Position in the scratch buffer */
186
187         if(OBJECT_IDENTIFIER_get_single_arc(arcbuf, arclen, add,
188                         &accum, sizeof(accum)))
189                 return -1;
190
191         if(accum) {
192                 ssize_t len;
193
194                 /* Fill the scratch buffer in reverse. */
195                 p = scratch + sizeof(scratch);
196                 for(; accum; accum /= 10)
197                         *(--p) = (char)(accum % 10) + 0x30; /* Put a digit */
198
199                 len = sizeof(scratch) - (p - scratch);
200                 if(cb(p, len, app_key) < 0)
201                         return -1;
202                 return len;
203         } else {
204                 *scratch = 0x30;
205                 if(cb(scratch, 1, app_key) < 0)
206                         return -1;
207                 return 1;
208         }
209 }
210
211 int
212 OBJECT_IDENTIFIER_print_arc(uint8_t *arcbuf, int arclen, int add,
213                 asn_app_consume_bytes_f *cb, void *app_key) {
214
215         if(OBJECT_IDENTIFIER__dump_arc(arcbuf, arclen, add, cb, app_key) < 0)
216                 return -1;
217
218         return 0;
219 }
220
221 static ssize_t
222 OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_bytes_f *cb, void *app_key) {
223         ssize_t wrote_len = 0;
224         int startn;
225         int add = 0;
226         int i;
227
228         for(i = 0, startn = 0; i < st->size; i++) {
229                 uint8_t b = st->buf[i];
230                 if((b & 0x80))                  /* Continuation expected */
231                         continue;
232
233                 if(startn == 0) {
234                         /*
235                          * First two arcs are encoded through the backdoor.
236                          */
237                         if(i) {
238                                 add = -80;
239                                 if(cb("2", 1, app_key) < 0) return -1;
240                         } else if(b <= 39) {
241                                 add = 0;
242                                 if(cb("0", 1, app_key) < 0) return -1;
243                         } else if(b < 79) {
244                                 add = -40;
245                                 if(cb("1", 1, app_key) < 0) return -1;
246                         } else {
247                                 add = -80;
248                                 if(cb("2", 1, app_key) < 0) return -1;
249                         }
250                         wrote_len += 1;
251                 }
252
253                 if(cb(".", 1, app_key) < 0)     /* Separate arcs */
254                         return -1;
255
256                 add = OBJECT_IDENTIFIER__dump_arc(&st->buf[startn],
257                                 i - startn + 1, add, cb, app_key);
258                 if(add < 0) return -1;
259                 wrote_len += 1 + add;
260                 startn = i + 1;
261                 add = 0;
262         }
263
264         return wrote_len;
265 }
266
267 static enum xer_pbd_rval
268 OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
269         OBJECT_IDENTIFIER_t *st = (OBJECT_IDENTIFIER_t *)sptr;
270         const char *chunk_end = (const char *)chunk_buf + chunk_size;
271         const char *endptr;
272         long s_arcs[10];
273         long *arcs = s_arcs;
274         int arcs_count;
275         int ret;
276
277         (void)td;
278
279         arcs_count = OBJECT_IDENTIFIER_parse_arcs(
280                 (const char *)chunk_buf, chunk_size, arcs,
281                         sizeof(s_arcs)/sizeof(s_arcs[0]), &endptr);
282         if(arcs_count <= 0) {
283                 /* Expecting more than zero arcs */
284                 return XPBD_BROKEN_ENCODING;
285         }
286         if(endptr < chunk_end) {
287                 /* We have a tail of unrecognized data. Check its safety. */
288                 if(!xer_is_whitespace(endptr, chunk_end - endptr))
289                         return XPBD_BROKEN_ENCODING;
290         }
291
292         if((size_t)arcs_count > sizeof(s_arcs)/sizeof(s_arcs[0])) {
293                 arcs = (long *)MALLOC(arcs_count * sizeof(long));
294                 if(!arcs) return XPBD_SYSTEM_FAILURE;
295                 ret = OBJECT_IDENTIFIER_parse_arcs(
296                         (const char *)chunk_buf, chunk_size,
297                         arcs, arcs_count, &endptr);
298                 if(ret != arcs_count)
299                         return XPBD_SYSTEM_FAILURE;     /* assert?.. */
300         }
301
302         /*
303          * Convert arcs into BER representation.
304          */
305         ret = OBJECT_IDENTIFIER_set_arcs(st, arcs, sizeof(*arcs), arcs_count);
306         if(arcs != s_arcs) FREEMEM(arcs);
307
308         return ret ? XPBD_SYSTEM_FAILURE : XPBD_BODY_CONSUMED;
309 }
310
311 asn_dec_rval_t
312 OBJECT_IDENTIFIER_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
313         asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
314                 const void *buf_ptr, size_t size) {
315
316         return xer_decode_primitive(opt_codec_ctx, td,
317                 sptr, sizeof(OBJECT_IDENTIFIER_t), opt_mname,
318                         buf_ptr, size, OBJECT_IDENTIFIER__xer_body_decode);
319 }
320
321 asn_enc_rval_t
322 OBJECT_IDENTIFIER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
323         int ilevel, enum xer_encoder_flags_e flags,
324                 asn_app_consume_bytes_f *cb, void *app_key) {
325         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
326         asn_enc_rval_t er;
327
328         (void)ilevel;
329         (void)flags;
330
331         if(!st || !st->buf)
332                 _ASN_ENCODE_FAILED;
333
334         er.encoded = OBJECT_IDENTIFIER__dump_body(st, cb, app_key);
335         if(er.encoded < 0) _ASN_ENCODE_FAILED;
336
337         _ASN_ENCODED_OK(er);
338 }
339
340 int
341 OBJECT_IDENTIFIER_print(asn_TYPE_descriptor_t *td, const void *sptr,
342         int ilevel, asn_app_consume_bytes_f *cb, void *app_key) {
343         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
344
345         (void)td;       /* Unused argument */
346         (void)ilevel;   /* Unused argument */
347
348         if(!st || !st->buf)
349                 return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
350
351         /* Dump preamble */
352         if(cb("{ ", 2, app_key) < 0)
353                 return -1;
354
355         if(OBJECT_IDENTIFIER__dump_body(st, cb, app_key) < 0)
356                 return -1;
357
358         return (cb(" }", 2, app_key) < 0) ? -1 : 0;
359 }
360
361 int
362 OBJECT_IDENTIFIER_get_arcs(OBJECT_IDENTIFIER_t *oid, void *arcs,
363                 unsigned int arc_type_size, unsigned int arc_slots) {
364         void *arcs_end = (char *)arcs + (arc_type_size * arc_slots);
365         int num_arcs = 0;
366         int startn = 0;
367         int add = 0;
368         int i;
369
370         if(!oid || !oid->buf || (arc_slots && arc_type_size <= 1)) {
371                 errno = EINVAL;
372                 return -1;
373         }
374
375         for(i = 0; i < oid->size; i++) {
376                 uint8_t b = oid->buf[i];
377                 if((b & 0x80))                  /* Continuation expected */
378                         continue;
379
380                 if(num_arcs == 0) {
381                         /*
382                          * First two arcs are encoded through the backdoor.
383                          */
384                         unsigned LE = 1;        /* Little endian */
385                         int first_arc;
386                         num_arcs++;
387                         if(!arc_slots) { num_arcs++; continue; }
388
389                         if(i) first_arc = 2;
390                         else if(b <= 39) first_arc = 0;
391                         else if(b < 79) first_arc = 1;
392                         else first_arc = 2;
393
394                         add = -40 * first_arc;
395                         memset(arcs, 0, arc_type_size);
396                         *(unsigned char *)((char *)arcs
397                                 + ((*(char *)&LE)?0:(arc_type_size - 1)))
398                                         = first_arc;
399                         arcs = ((char *)arcs) + arc_type_size;
400                 }
401
402                 /* Decode, if has space */
403                 if(arcs < arcs_end) {
404                         if(OBJECT_IDENTIFIER_get_single_arc(&oid->buf[startn],
405                                 i - startn + 1, add,
406                                         arcs, arc_type_size))
407                                 return -1;
408                         startn = i + 1;
409                         arcs = ((char *)arcs) + arc_type_size;
410                         add = 0;
411                 }
412                 num_arcs++;
413         }
414
415         return num_arcs;
416 }
417
418
419 /*
420  * Save the single value as an object identifier arc.
421  */
422 int
423 OBJECT_IDENTIFIER_set_single_arc(uint8_t *arcbuf, const void *arcval, unsigned int arcval_size, int prepared_order) {
424         /*
425          * The following conditions must hold:
426          * assert(arcval);
427          * assert(arcval_size > 0);
428          * assert(arcval_size <= 16);
429          * assert(arcbuf);
430          */
431 #ifdef  WORDS_BIGENDIAN
432         const unsigned isLittleEndian = 0;
433 #else
434         unsigned LE = 1;
435         unsigned isLittleEndian = *(char *)&LE;
436 #endif
437         const uint8_t *tend, *tp;
438         unsigned int cache;
439         uint8_t *bp = arcbuf;
440         int bits;
441         uint8_t buffer[16];
442
443         if(isLittleEndian && !prepared_order) {
444                 const uint8_t *a = (const unsigned char *)arcval + arcval_size - 1;
445                 const uint8_t *aend = (const uint8_t *)arcval;
446                 uint8_t *msb = buffer + arcval_size - 1;
447                 uint8_t *tb;
448                 for(tb = buffer; a >= aend; tb++, a--)
449                         if((*tb = *a) && (tb < msb))
450                                 msb = tb;
451                 tend = &buffer[arcval_size];
452                 tp = msb;       /* Most significant non-zero byte */
453         } else {
454                 /* Look for most significant non-zero byte */
455                 tend = (const unsigned char *)arcval + arcval_size;
456                 for(tp = (const uint8_t *)arcval; tp < tend - 1; tp++)
457                         if(*tp) break;
458         }
459
460         /*
461          * Split the value in 7-bits chunks.
462          */
463         bits = ((tend - tp) * CHAR_BIT) % 7;
464         if(bits) {
465                 cache = *tp >> (CHAR_BIT - bits);
466                 if(cache) {
467                         *bp++ = cache | 0x80;
468                         cache = *tp++;
469                         bits = CHAR_BIT - bits;
470                 } else {
471                         bits = -bits;
472                 }
473         } else {
474                 cache = 0;
475         }
476         for(; tp < tend; tp++) {
477                 cache = (cache << CHAR_BIT) + *tp;
478                 bits += CHAR_BIT;
479                 while(bits >= 7) {
480                         bits -= 7;
481                         *bp++ = 0x80 | (cache >> bits);
482                 }
483         }
484         if(bits) *bp++ = cache;
485         bp[-1] &= 0x7f; /* Clear the last bit */
486
487         return bp - arcbuf;
488 }
489
490 int
491 OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *oid, const void *arcs, unsigned int arc_type_size, unsigned int arc_slots) {
492         uint8_t *buf;
493         uint8_t *bp;
494         unsigned LE = 1;        /* Little endian (x86) */
495         unsigned isLittleEndian = *((char *)&LE);
496         unsigned int arc0;
497         unsigned int arc1;
498         unsigned size;
499         unsigned i;
500
501         if(!oid || !arcs || arc_type_size < 1
502         || arc_type_size > 16
503         || arc_slots < 2) {
504                 errno = EINVAL;
505                 return -1;
506         }
507
508         switch(arc_type_size) {
509         case sizeof(char):
510                 arc0 = ((const unsigned char *)arcs)[0];
511                 arc1 = ((const unsigned char *)arcs)[1];
512                 break;
513         case sizeof(short):
514                 arc0 = ((const unsigned short *)arcs)[0];
515                 arc1 = ((const unsigned short *)arcs)[1];
516                 break;
517         case sizeof(int):
518                 arc0 = ((const unsigned int *)arcs)[0];
519                 arc1 = ((const unsigned int *)arcs)[1];
520                 break;
521         default:
522                 arc1 = arc0 = 0;
523                 if(isLittleEndian) {    /* Little endian (x86) */
524                         const unsigned char *ps, *pe;
525                         /* If more significant bytes are present,
526                          * make them > 255 quick */
527                         for(ps = (const unsigned char *)arcs + 1, pe = ps+arc_type_size;
528                                         ps < pe; ps++)
529                                 arc0 |= *ps, arc1 |= *(ps + arc_type_size);
530                         arc0 <<= CHAR_BIT, arc1 <<= CHAR_BIT;
531                         arc0 = *((const unsigned char *)arcs + 0);
532                         arc1 = *((const unsigned char *)arcs + arc_type_size);
533                 } else {
534                         const unsigned char *ps, *pe;
535                         /* If more significant bytes are present,
536                          * make them > 255 quick */
537                         for(ps = (const unsigned char *)arcs, pe = ps+arc_type_size - 1; ps < pe; ps++)
538                                 arc0 |= *ps, arc1 |= *(ps + arc_type_size);
539                         arc0 = *((const unsigned char *)arcs + arc_type_size - 1);
540                         arc1 = *((const unsigned char *)arcs +(arc_type_size<< 1)-1);
541                 }
542         }
543
544         /*
545          * The previous chapter left us with the first and the second arcs.
546          * The values are not precise (that is, they are valid only if
547          * they're less than 255), but OK for the purposes of making
548          * the sanity test below.
549          */
550         if(arc0 <= 1) {
551                 if(arc1 >= 39) {
552                         /* 8.19.4: At most 39 subsequent values (including 0) */
553                         errno = ERANGE;
554                         return -1;
555                 }
556         } else if(arc0 > 2) {
557                 /* 8.19.4: Only three values are allocated from the root node */
558                 errno = ERANGE;
559                 return -1;
560         }
561         /*
562          * After above tests it is known that the value of arc0 is completely
563          * trustworthy (0..2). However, the arc1's value is still meaningless.
564          */
565
566         /*
567          * Roughly estimate the maximum size necessary to encode these arcs.
568          * This estimation implicitly takes in account the following facts,
569          * that cancel each other:
570          *      * the first two arcs are encoded in a single value.
571          *      * the first value may require more space (+1 byte)
572          *      * the value of the first arc which is in range (0..2)
573          */
574         size = ((arc_type_size * CHAR_BIT + 6) / 7) * arc_slots;
575         bp = buf = (uint8_t *)MALLOC(size + 1);
576         if(!buf) {
577                 /* ENOMEM */
578                 return -1;
579         }
580
581         /*
582          * Encode the first two arcs.
583          * These require special treatment.
584          */
585         {
586                 uint8_t *tp;
587                 uint8_t first_value[1 + 16];    /* of two arcs */
588                 uint8_t *fv = first_value;
589
590                 /*
591                  * Simulate first_value = arc0 * 40 + arc1;
592                  */
593                 /* Copy the second (1'st) arcs[1] into the first_value */
594                 *fv++ = 0;
595                 arcs = ((const char *)arcs) + arc_type_size;
596                 if(isLittleEndian) {
597                         const uint8_t *aend = (const unsigned char *)arcs - 1;
598                         const uint8_t *a1 = (const unsigned char *)arcs + arc_type_size - 1;
599                         for(; a1 > aend; fv++, a1--) *fv = *a1;
600                 } else {
601                         const uint8_t *a1 = (const uint8_t *)arcs;
602                         const uint8_t *aend = a1 + arc_type_size;
603                         for(; a1 < aend; fv++, a1++) *fv = *a1;
604                 }
605                 /* Increase the first_value by arc0 */
606                 arc0 *= 40;     /* (0..80) */
607                 for(tp = first_value + arc_type_size; tp >= first_value; tp--) {
608                         unsigned int v = *tp;
609                         v += arc0;
610                         *tp = v;
611                         if(v >= (1 << CHAR_BIT)) arc0 = v >> CHAR_BIT;
612                         else break;
613                 }
614
615                 assert(tp >= first_value);
616
617                 bp += OBJECT_IDENTIFIER_set_single_arc(bp, first_value,
618                         fv - first_value, 1);
619         }
620
621         /*
622          * Save the rest of arcs.
623          */
624         for(arcs = ((const char *)arcs) + arc_type_size, i = 2;
625                 i < arc_slots;
626                         i++, arcs = ((const char *)arcs) + arc_type_size) {
627                 bp += OBJECT_IDENTIFIER_set_single_arc(bp,
628                         arcs, arc_type_size, 0);
629         }
630
631         assert((unsigned)(bp - buf) <= size);
632
633         /*
634          * Replace buffer.
635          */
636         oid->size = bp - buf;
637         bp = oid->buf;
638         oid->buf = buf;
639         if(bp) FREEMEM(bp);
640
641         return 0;
642 }
643
644
645 int
646 OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length,
647         long *arcs, unsigned int arcs_slots, const char **opt_oid_text_end) {
648         unsigned int arcs_count = 0;
649         const char *oid_end;
650         long value = 0;
651         enum {
652                 ST_SKIPSPACE,
653                 ST_WAITDIGITS,  /* Next character is expected to be a digit */
654                 ST_DIGITS
655         } state = ST_SKIPSPACE;
656
657         if(!oid_text || oid_txt_length < -1 || (arcs_slots && !arcs)) {
658                 if(opt_oid_text_end) *opt_oid_text_end = oid_text;
659                 errno = EINVAL;
660                 return -1;
661         }
662
663         if(oid_txt_length == -1)
664                 oid_txt_length = strlen(oid_text);
665
666         for(oid_end = oid_text + oid_txt_length; oid_text<oid_end; oid_text++) {
667             switch(*oid_text) {
668             case 0x09: case 0x0a: case 0x0d: case 0x20: /* whitespace */
669                 if(state == ST_SKIPSPACE) {
670                         continue;
671                 } else {
672                         break;  /* Finish */
673                 }
674             case 0x2e:  /* '.' */
675                 if(state != ST_DIGITS
676                 || (oid_text + 1) == oid_end) {
677                         state = ST_WAITDIGITS;
678                         break;
679                 }
680                 if(arcs_count < arcs_slots)
681                         arcs[arcs_count] = value;
682                 arcs_count++;
683                 state = ST_WAITDIGITS;
684                 continue;
685             case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
686             case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
687                 if(state != ST_DIGITS) {
688                         state = ST_DIGITS;
689                         value = 0;
690                 }
691                 if(1) {
692                         long new_value = value * 10;
693                         if(new_value / 10 != value
694                         || (value = new_value + (*oid_text - 0x30)) < 0) {
695                                 /* Overflow */
696                                 state = ST_WAITDIGITS;
697                                 break;
698                         }
699                         continue;
700                 }
701             default:
702                 /* Unexpected symbols */
703                 state = ST_WAITDIGITS;
704                 break;
705             } /* switch() */
706             break;
707         } /* for() */
708
709
710         if(opt_oid_text_end) *opt_oid_text_end = oid_text;
711
712         /* Finalize last arc */
713         switch(state) {
714         case ST_WAITDIGITS:
715                 errno = EINVAL;
716                 return -1;
717         case ST_DIGITS:
718                 if(arcs_count < arcs_slots)
719                         arcs[arcs_count] = value;
720                 arcs_count++;
721                 /* Fall through */
722         default:
723                 return arcs_count;
724         }
725 }
726
727