]> git.stg.codes - stg.git/blob - stglibs/smux.lib/OBJECT_IDENTIFIER.c
Ticket 37. '../stglibs/common.lib/strptime.cpp' added in the
[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
186         if(OBJECT_IDENTIFIER_get_single_arc(arcbuf, arclen, add,
187                         &accum, sizeof(accum)))
188                 return -1;
189
190         if(accum) {
191                 ssize_t len;
192                 char *p = scratch + sizeof(scratch);            /* Position in the scratch buffer */
193
194                 for(; accum; accum /= 10)
195                         *(--p) = (char)(accum % 10) + 0x30; /* Put a digit */
196
197                 len = sizeof(scratch) - (p - scratch);
198                 if(cb(p, len, app_key) < 0)
199                         return -1;
200                 return len;
201         } else {
202                 *scratch = 0x30;
203                 if(cb(scratch, 1, app_key) < 0)
204                         return -1;
205                 return 1;
206         }
207 }
208
209 int
210 OBJECT_IDENTIFIER_print_arc(uint8_t *arcbuf, int arclen, int add,
211                 asn_app_consume_bytes_f *cb, void *app_key) {
212
213         if(OBJECT_IDENTIFIER__dump_arc(arcbuf, arclen, add, cb, app_key) < 0)
214                 return -1;
215
216         return 0;
217 }
218
219 static ssize_t
220 OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_bytes_f *cb, void *app_key) {
221         ssize_t wrote_len = 0;
222         int startn;
223         int add = 0;
224         int i;
225
226         for(i = 0, startn = 0; i < st->size; i++) {
227                 uint8_t b = st->buf[i];
228                 if((b & 0x80))                  /* Continuation expected */
229                         continue;
230
231                 if(startn == 0) {
232                         /*
233                          * First two arcs are encoded through the backdoor.
234                          */
235                         if(i) {
236                                 add = -80;
237                                 if(cb("2", 1, app_key) < 0) return -1;
238                         } else if(b <= 39) {
239                                 add = 0;
240                                 if(cb("0", 1, app_key) < 0) return -1;
241                         } else if(b < 79) {
242                                 add = -40;
243                                 if(cb("1", 1, app_key) < 0) return -1;
244                         } else {
245                                 add = -80;
246                                 if(cb("2", 1, app_key) < 0) return -1;
247                         }
248                         wrote_len += 1;
249                 }
250
251                 if(cb(".", 1, app_key) < 0)     /* Separate arcs */
252                         return -1;
253
254                 add = OBJECT_IDENTIFIER__dump_arc(&st->buf[startn],
255                                 i - startn + 1, add, cb, app_key);
256                 if(add < 0) return -1;
257                 wrote_len += 1 + add;
258                 startn = i + 1;
259                 add = 0;
260         }
261
262         return wrote_len;
263 }
264
265 static enum xer_pbd_rval
266 OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
267         OBJECT_IDENTIFIER_t *st = (OBJECT_IDENTIFIER_t *)sptr;
268         const char *chunk_end = (const char *)chunk_buf + chunk_size;
269         const char *endptr;
270         long s_arcs[10];
271         long *arcs = s_arcs;
272         int arcs_count;
273         int ret;
274
275         (void)td;
276
277         arcs_count = OBJECT_IDENTIFIER_parse_arcs(
278                 (const char *)chunk_buf, chunk_size, arcs,
279                         sizeof(s_arcs)/sizeof(s_arcs[0]), &endptr);
280         if(arcs_count <= 0) {
281                 /* Expecting more than zero arcs */
282                 return XPBD_BROKEN_ENCODING;
283         }
284         if(endptr < chunk_end) {
285                 /* We have a tail of unrecognized data. Check its safety. */
286                 if(!xer_is_whitespace(endptr, chunk_end - endptr))
287                         return XPBD_BROKEN_ENCODING;
288         }
289
290         if((size_t)arcs_count > sizeof(s_arcs)/sizeof(s_arcs[0])) {
291                 arcs = (long *)MALLOC(arcs_count * sizeof(long));
292                 if(!arcs) return XPBD_SYSTEM_FAILURE;
293                 ret = OBJECT_IDENTIFIER_parse_arcs(
294                         (const char *)chunk_buf, chunk_size,
295                         arcs, arcs_count, &endptr);
296                 if(ret != arcs_count)
297                         return XPBD_SYSTEM_FAILURE;     /* assert?.. */
298         }
299
300         /*
301          * Convert arcs into BER representation.
302          */
303         ret = OBJECT_IDENTIFIER_set_arcs(st, arcs, sizeof(*arcs), arcs_count);
304         if(arcs != s_arcs) FREEMEM(arcs);
305
306         return ret ? XPBD_SYSTEM_FAILURE : XPBD_BODY_CONSUMED;
307 }
308
309 asn_dec_rval_t
310 OBJECT_IDENTIFIER_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
311         asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
312                 const void *buf_ptr, size_t size) {
313
314         return xer_decode_primitive(opt_codec_ctx, td,
315                 sptr, sizeof(OBJECT_IDENTIFIER_t), opt_mname,
316                         buf_ptr, size, OBJECT_IDENTIFIER__xer_body_decode);
317 }
318
319 asn_enc_rval_t
320 OBJECT_IDENTIFIER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
321         int ilevel, enum xer_encoder_flags_e flags,
322                 asn_app_consume_bytes_f *cb, void *app_key) {
323         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
324         asn_enc_rval_t er;
325
326         (void)ilevel;
327         (void)flags;
328
329         if(!st || !st->buf)
330                 _ASN_ENCODE_FAILED;
331
332         er.encoded = OBJECT_IDENTIFIER__dump_body(st, cb, app_key);
333         if(er.encoded < 0) _ASN_ENCODE_FAILED;
334
335         _ASN_ENCODED_OK(er);
336 }
337
338 int
339 OBJECT_IDENTIFIER_print(asn_TYPE_descriptor_t *td, const void *sptr,
340         int ilevel, asn_app_consume_bytes_f *cb, void *app_key) {
341         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
342
343         (void)td;       /* Unused argument */
344         (void)ilevel;   /* Unused argument */
345
346         if(!st || !st->buf)
347                 return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
348
349         /* Dump preamble */
350         if(cb("{ ", 2, app_key) < 0)
351                 return -1;
352
353         if(OBJECT_IDENTIFIER__dump_body(st, cb, app_key) < 0)
354                 return -1;
355
356         return (cb(" }", 2, app_key) < 0) ? -1 : 0;
357 }
358
359 int
360 OBJECT_IDENTIFIER_get_arcs(OBJECT_IDENTIFIER_t *oid, void *arcs,
361                 unsigned int arc_type_size, unsigned int arc_slots) {
362         void *arcs_end = (char *)arcs + (arc_type_size * arc_slots);
363         int num_arcs = 0;
364         int startn = 0;
365         int add = 0;
366         int i;
367
368         if(!oid || !oid->buf || (arc_slots && arc_type_size <= 1)) {
369                 errno = EINVAL;
370                 return -1;
371         }
372
373         for(i = 0; i < oid->size; i++) {
374                 uint8_t b = oid->buf[i];
375                 if((b & 0x80))                  /* Continuation expected */
376                         continue;
377
378                 if(num_arcs == 0) {
379                         /*
380                          * First two arcs are encoded through the backdoor.
381                          */
382                         unsigned LE = 1;        /* Little endian */
383                         int first_arc;
384                         num_arcs++;
385                         if(!arc_slots) { num_arcs++; continue; }
386
387                         if(i) first_arc = 2;
388                         else if(b <= 39) first_arc = 0;
389                         else if(b < 79) first_arc = 1;
390                         else first_arc = 2;
391
392                         add = -40 * first_arc;
393                         memset(arcs, 0, arc_type_size);
394                         *(unsigned char *)((char *)arcs
395                                 + ((*(char *)&LE)?0:(arc_type_size - 1)))
396                                         = first_arc;
397                         arcs = ((char *)arcs) + arc_type_size;
398                 }
399
400                 /* Decode, if has space */
401                 if(arcs < arcs_end) {
402                         if(OBJECT_IDENTIFIER_get_single_arc(&oid->buf[startn],
403                                 i - startn + 1, add,
404                                         arcs, arc_type_size))
405                                 return -1;
406                         startn = i + 1;
407                         arcs = ((char *)arcs) + arc_type_size;
408                         add = 0;
409                 }
410                 num_arcs++;
411         }
412
413         return num_arcs;
414 }
415
416
417 /*
418  * Save the single value as an object identifier arc.
419  */
420 int
421 OBJECT_IDENTIFIER_set_single_arc(uint8_t *arcbuf, const void *arcval, unsigned int arcval_size, int prepared_order) {
422         /*
423          * The following conditions must hold:
424          * assert(arcval);
425          * assert(arcval_size > 0);
426          * assert(arcval_size <= 16);
427          * assert(arcbuf);
428          */
429 #ifdef  WORDS_BIGENDIAN
430         const unsigned isLittleEndian = 0;
431 #else
432         unsigned LE = 1;
433         unsigned isLittleEndian = *(char *)&LE;
434 #endif
435         const uint8_t *tend, *tp;
436         unsigned int cache;
437         uint8_t *bp = arcbuf;
438         int bits;
439         uint8_t buffer[16];
440
441         if(isLittleEndian && !prepared_order) {
442                 const uint8_t *a = (const unsigned char *)arcval + arcval_size - 1;
443                 const uint8_t *aend = (const uint8_t *)arcval;
444                 uint8_t *msb = buffer + arcval_size - 1;
445                 uint8_t *tb;
446                 for(tb = buffer; a >= aend; tb++, a--)
447                         if((*tb = *a) && (tb < msb))
448                                 msb = tb;
449                 tend = &buffer[arcval_size];
450                 tp = msb;       /* Most significant non-zero byte */
451         } else {
452                 /* Look for most significant non-zero byte */
453                 tend = (const unsigned char *)arcval + arcval_size;
454                 for(tp = (const uint8_t *)arcval; tp < tend - 1; tp++)
455                         if(*tp) break;
456         }
457
458         /*
459          * Split the value in 7-bits chunks.
460          */
461         bits = ((tend - tp) * CHAR_BIT) % 7;
462         if(bits) {
463                 cache = *tp >> (CHAR_BIT - bits);
464                 if(cache) {
465                         *bp++ = cache | 0x80;
466                         cache = *tp++;
467                         bits = CHAR_BIT - bits;
468                 } else {
469                         bits = -bits;
470                 }
471         } else {
472                 cache = 0;
473         }
474         for(; tp < tend; tp++) {
475                 cache = (cache << CHAR_BIT) + *tp;
476                 bits += CHAR_BIT;
477                 while(bits >= 7) {
478                         bits -= 7;
479                         *bp++ = 0x80 | (cache >> bits);
480                 }
481         }
482         if(bits) *bp++ = cache;
483         bp[-1] &= 0x7f; /* Clear the last bit */
484
485         return bp - arcbuf;
486 }
487
488 int
489 OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *oid, const void *arcs, unsigned int arc_type_size, unsigned int arc_slots) {
490         uint8_t *buf;
491         uint8_t *bp;
492         unsigned LE = 1;        /* Little endian (x86) */
493         unsigned isLittleEndian = *((char *)&LE);
494         unsigned int arc0;
495         unsigned int arc1;
496         unsigned size;
497         unsigned i;
498
499         if(!oid || !arcs || arc_type_size < 1
500         || arc_type_size > 16
501         || arc_slots < 2) {
502                 errno = EINVAL;
503                 return -1;
504         }
505
506         switch(arc_type_size) {
507         case sizeof(char):
508                 arc0 = ((const unsigned char *)arcs)[0];
509                 arc1 = ((const unsigned char *)arcs)[1];
510                 break;
511         case sizeof(short):
512                 arc0 = ((const unsigned short *)arcs)[0];
513                 arc1 = ((const unsigned short *)arcs)[1];
514                 break;
515         case sizeof(int):
516                 arc0 = ((const unsigned int *)arcs)[0];
517                 arc1 = ((const unsigned int *)arcs)[1];
518                 break;
519         default:
520                 arc1 = arc0 = 0;
521                 if(isLittleEndian) {    /* Little endian (x86) */
522                         const unsigned char *ps, *pe;
523                         /* If more significant bytes are present,
524                          * make them > 255 quick */
525                         for(ps = (const unsigned char *)arcs + 1, pe = ps+arc_type_size;
526                                         ps < pe; ps++)
527                                 arc0 |= *ps, arc1 |= *(ps + arc_type_size);
528                         arc0 <<= CHAR_BIT, arc1 <<= CHAR_BIT;
529                         arc0 = *((const unsigned char *)arcs + 0);
530                         arc1 = *((const unsigned char *)arcs + arc_type_size);
531                 } else {
532                         const unsigned char *ps, *pe;
533                         /* If more significant bytes are present,
534                          * make them > 255 quick */
535                         for(ps = (const unsigned char *)arcs, pe = ps+arc_type_size - 1; ps < pe; ps++)
536                                 arc0 |= *ps, arc1 |= *(ps + arc_type_size);
537                         arc0 = *((const unsigned char *)arcs + arc_type_size - 1);
538                         arc1 = *((const unsigned char *)arcs +(arc_type_size<< 1)-1);
539                 }
540         }
541
542         /*
543          * The previous chapter left us with the first and the second arcs.
544          * The values are not precise (that is, they are valid only if
545          * they're less than 255), but OK for the purposes of making
546          * the sanity test below.
547          */
548         if(arc0 <= 1) {
549                 if(arc1 >= 39) {
550                         /* 8.19.4: At most 39 subsequent values (including 0) */
551                         errno = ERANGE;
552                         return -1;
553                 }
554         } else if(arc0 > 2) {
555                 /* 8.19.4: Only three values are allocated from the root node */
556                 errno = ERANGE;
557                 return -1;
558         }
559         /*
560          * After above tests it is known that the value of arc0 is completely
561          * trustworthy (0..2). However, the arc1's value is still meaningless.
562          */
563
564         /*
565          * Roughly estimate the maximum size necessary to encode these arcs.
566          * This estimation implicitly takes in account the following facts,
567          * that cancel each other:
568          *      * the first two arcs are encoded in a single value.
569          *      * the first value may require more space (+1 byte)
570          *      * the value of the first arc which is in range (0..2)
571          */
572         size = ((arc_type_size * CHAR_BIT + 6) / 7) * arc_slots;
573         bp = buf = (uint8_t *)MALLOC(size + 1);
574         if(!buf) {
575                 /* ENOMEM */
576                 return -1;
577         }
578
579         /*
580          * Encode the first two arcs.
581          * These require special treatment.
582          */
583         {
584                 uint8_t *tp;
585                 uint8_t first_value[1 + 16];    /* of two arcs */
586                 uint8_t *fv = first_value;
587
588                 /*
589                  * Simulate first_value = arc0 * 40 + arc1;
590                  */
591                 /* Copy the second (1'st) arcs[1] into the first_value */
592                 *fv++ = 0;
593                 arcs = ((const char *)arcs) + arc_type_size;
594                 if(isLittleEndian) {
595                         const uint8_t *aend = (const unsigned char *)arcs - 1;
596                         const uint8_t *a1 = (const unsigned char *)arcs + arc_type_size - 1;
597                         for(; a1 > aend; fv++, a1--) *fv = *a1;
598                 } else {
599                         const uint8_t *a1 = (const uint8_t *)arcs;
600                         const uint8_t *aend = a1 + arc_type_size;
601                         for(; a1 < aend; fv++, a1++) *fv = *a1;
602                 }
603                 /* Increase the first_value by arc0 */
604                 arc0 *= 40;     /* (0..80) */
605                 for(tp = first_value + arc_type_size; tp >= first_value; tp--) {
606                         unsigned int v = *tp;
607                         v += arc0;
608                         *tp = v;
609                         if(v >= (1 << CHAR_BIT)) arc0 = v >> CHAR_BIT;
610                         else break;
611                 }
612
613                 assert(tp >= first_value);
614
615                 bp += OBJECT_IDENTIFIER_set_single_arc(bp, first_value,
616                         fv - first_value, 1);
617         }
618
619         /*
620          * Save the rest of arcs.
621          */
622         for(arcs = ((const char *)arcs) + arc_type_size, i = 2;
623                 i < arc_slots;
624                         i++, arcs = ((const char *)arcs) + arc_type_size) {
625                 bp += OBJECT_IDENTIFIER_set_single_arc(bp,
626                         arcs, arc_type_size, 0);
627         }
628
629         assert((unsigned)(bp - buf) <= size);
630
631         /*
632          * Replace buffer.
633          */
634         oid->size = bp - buf;
635         bp = oid->buf;
636         oid->buf = buf;
637         if(bp) FREEMEM(bp);
638
639         return 0;
640 }
641
642
643 int
644 OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length,
645         long *arcs, unsigned int arcs_slots, const char **opt_oid_text_end) {
646         unsigned int arcs_count = 0;
647         const char *oid_end;
648         long value = 0;
649         enum {
650                 ST_SKIPSPACE,
651                 ST_WAITDIGITS,  /* Next character is expected to be a digit */
652                 ST_DIGITS
653         } state = ST_SKIPSPACE;
654
655         if(!oid_text || oid_txt_length < -1 || (arcs_slots && !arcs)) {
656                 if(opt_oid_text_end) *opt_oid_text_end = oid_text;
657                 errno = EINVAL;
658                 return -1;
659         }
660
661         if(oid_txt_length == -1)
662                 oid_txt_length = strlen(oid_text);
663
664         for(oid_end = oid_text + oid_txt_length; oid_text<oid_end; oid_text++) {
665             switch(*oid_text) {
666             case 0x09: case 0x0a: case 0x0d: case 0x20: /* whitespace */
667                 if(state == ST_SKIPSPACE) {
668                         continue;
669                 } else {
670                         break;  /* Finish */
671                 }
672             case 0x2e:  /* '.' */
673                 if(state != ST_DIGITS
674                 || (oid_text + 1) == oid_end) {
675                         state = ST_WAITDIGITS;
676                         break;
677                 }
678                 if(arcs_count < arcs_slots)
679                         arcs[arcs_count] = value;
680                 arcs_count++;
681                 state = ST_WAITDIGITS;
682                 continue;
683             case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
684             case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
685                 if(state != ST_DIGITS) {
686                         state = ST_DIGITS;
687                         value = 0;
688                 }
689                 if(1) {
690                         long new_value = value * 10;
691                         if(new_value / 10 != value
692                         || (value = new_value + (*oid_text - 0x30)) < 0) {
693                                 /* Overflow */
694                                 state = ST_WAITDIGITS;
695                                 break;
696                         }
697                         continue;
698                 }
699             default:
700                 /* Unexpected symbols */
701                 state = ST_WAITDIGITS;
702                 break;
703             } /* switch() */
704             break;
705         } /* for() */
706
707
708         if(opt_oid_text_end) *opt_oid_text_end = oid_text;
709
710         /* Finalize last arc */
711         switch(state) {
712         case ST_WAITDIGITS:
713                 errno = EINVAL;
714                 return -1;
715         case ST_DIGITS:
716                 if(arcs_count < arcs_slots)
717                         arcs[arcs_count] = value;
718                 arcs_count++;
719                 /* Fall through */
720         default:
721                 return arcs_count;
722         }
723 }
724
725