ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
Revision: 1.19
Committed: Fri Apr 19 14:50:44 2019 UTC (5 years, 2 months ago) by root
Branch: MAIN
Changes since 1.18: +134 -50 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 // C99 required
6
7 //#define BENCHMARK
8
9 enum {
10 ASN_BOOLEAN = 0x01,
11 ASN_INTEGER32 = 0x02,
12 ASN_BIT_STRING = 0x03,
13 ASN_OCTET_STRING = 0x04,
14 ASN_NULL = 0x05,
15 ASN_OBJECT_IDENTIFIER = 0x06,
16
17 ASN_TAG_BER = 0x1f,
18 ASN_TAG_MASK = 0x1f,
19
20 ASN_CONSTRUCTED = 0x20,
21
22 ASN_UNIVERSAL = 0x00,
23 ASN_APPLICATION = 0x40,
24 ASN_CONTEXT = 0x80,
25 ASN_PRIVATE = 0xc0,
26
27 ASN_CLASS_MASK = 0xc0,
28 ASN_CLASS_SHIFT = 6,
29
30 ASN_SEQUENCE = ASN_CONSTRUCTED | 0x10,
31 ASN_IPADDRESS = ASN_APPLICATION | 0x00,
32 ASN_COUNTER32 = ASN_APPLICATION | 0x01,
33 ASN_UNSIGNED32 = ASN_APPLICATION | 0x02,
34 ASN_TIMETICKS = ASN_APPLICATION | 0x03,
35 ASN_OPAQUE = ASN_APPLICATION | 0x04,
36 ASN_COUNTER64 = ASN_APPLICATION | 0x06,
37 };
38
39 #define MAX_OID_STRLEN 4096
40
41 #define HAVE_VERSIONSORT defined (_GNU_SOURCE) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
42
43 static SV *cur_bufobj;
44 static SV *msg, *bufsv;
45 static int errflag, leading_dot;
46 static U8 *buf, *cur;
47 static STRLEN len, rem;
48
49 typedef SV *BUFOBJ;
50
51 // for "small" integers, return a readonly sv, otherwise create a new one
52 static SV *newSVcacheint (int val)
53 {
54 static SV *cache[32];
55
56 if (val < 0 || val >= sizeof (cache))
57 return newSViv (val);
58
59 if (!cache [val])
60 {
61 cache [val] = newSVuv (val);
62 SvREADONLY_on (cache [val]);
63 }
64
65 return SvREFCNT_inc_NN (cache [val]);
66 }
67
68 /////////////////////////////////////////////////////////////////////////////
69
70 #if 0
71 if (msg)
72 croak ("recursive invocation of Net::SNMP::XS parser is not supported");
73
74
75 void
76 clr_msg ()
77 CODE:
78 SvREFCNT_dec (msg); msg = 0;
79 buf = cur = (U8 *)"";
80 len = rem = 0;
81 #endif
82
83 static void
84 clear_bufobj (void)
85 {
86 // serialise our state back
87 if (msg && SvROK (msg))
88 {
89 SV *idx_sv = *hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1);
90 sv_setiv (idx_sv, cur - buf);
91 }
92
93 SvREFCNT_dec (msg);
94 msg = 0;
95 cur_bufobj = 0;
96 }
97
98 static void
99 switch_bufobj (BUFOBJ neu)
100 {
101 clear_bufobj ();
102
103 msg = newSVsv (neu);
104 cur_bufobj = SvRV (msg);
105 sv_rvweaken (msg);
106
107 errflag = 0;
108 leading_dot = -1;
109
110 IV index = SvIV (*hv_fetch ((HV *)cur_bufobj, "_index" , sizeof ("_index" ) - 1, 1));
111 bufsv = *hv_fetch ((HV *)cur_bufobj, "_buffer", sizeof ("_buffer") - 1, 1);
112
113 buf = SvPVbyte (bufsv, len);
114 cur = buf + index;
115 rem = len - index;
116 }
117
118 /////////////////////////////////////////////////////////////////////////////
119
120 static SV *
121 x_get_cv (SV *cb_sv)
122 {
123 HV *st;
124 GV *gvp;
125 CV *cv = sv_2cv (cb_sv, &st, &gvp, 0);
126
127 if (!cv)
128 croak ("CODE reference expected");
129
130 return (SV *)cv;
131 }
132
133 static void
134 error (const char *errmsg)
135 {
136 errflag = 1;
137
138 if (!msg)
139 croak ("Net::SNMP::XS fatal error, parser called without parsing context");
140
141 dSP;
142 PUSHMARK (SP);
143 EXTEND (SP, 2);
144 PUSHs (msg);
145 PUSHs (sv_2mortal (newSVpv (errmsg, 0)));
146 PUTBACK;
147 call_method ("_error", G_VOID | G_DISCARD);
148 }
149
150 static int
151 need (int count)
152 {
153 if (count < 0 || (int)rem < count)
154 {
155 error ("Unexpected end of message buffer");
156 return 0;
157 }
158
159 return 1;
160 }
161
162 static U8 *
163 getn (int count, const U8 *errres)
164 {
165 if (!need (count))
166 return (U8 *)errres;
167
168 U8 *res = cur;
169
170 cur += count;
171 rem -= count;
172
173 return res;
174 }
175
176 static U8
177 get8 (void)
178 {
179 if (rem <= 0)
180 {
181 error ("Unexpected end of message buffer");
182 return 0;
183 }
184
185 rem--;
186 return *cur++;
187 }
188
189 static U32
190 getb (void)
191 {
192 U32 res = 0;
193
194 for (;;)
195 {
196 U8 c = get8 ();
197 res = (res << 7) | (c & 0x7f);
198
199 if (!(c & 0x80))
200 return res;
201 }
202 }
203
204 #ifdef BENCHMARK
205 static double t1;
206
207 static double
208 tstamp (void)
209 {
210 struct timeval tv;
211 gettimeofday (&tv, 0);
212 return tv.tv_sec + tv.tv_usec * 0.000001;
213 }
214 #endif
215
216 static U32
217 process_length (void)
218 {
219 U32 res = get8 ();
220
221 if (res & 0x80)
222 {
223 int cnt = res & 0x7f;
224 res = 0;
225
226 switch (cnt)
227 {
228 case 0:
229 error ("Indefinite ASN.1 lengths not supported");
230 return 0;
231
232 default:
233 error ("ASN.1 length too long");
234 return 0;
235
236 case 4: res = (res << 8) | get8 ();
237 case 3: res = (res << 8) | get8 ();
238 case 2: res = (res << 8) | get8 ();
239 case 1: res = (res << 8) | get8 ();
240 }
241 }
242
243 return res;
244 }
245
246 static U32
247 process_integer32 (void)
248 {
249 U32 length = process_length ();
250
251 if (length <= 0)
252 {
253 error ("INTEGER32 length equal to zero");
254 return 0;
255 }
256
257 U8 *data = getn (length, 0);
258
259 if (!data)
260 return 0;
261
262 if (length > 5 || (length > 4 && data [0]))
263 {
264 error ("INTEGER32 length too long");
265 return 0;
266 }
267
268 U32 res = data [0] & 0x80 ? 0xffffffff : 0;
269
270 while (length--)
271 res = (res << 8) | *data++;
272
273 return res;
274 }
275
276 static SV *
277 process_integer32_sv (void)
278 {
279 return newSViv ((I32)process_integer32 ());
280 }
281
282 static SV *
283 process_unsigned32_sv (void)
284 {
285 return newSVuv ((U32)process_integer32 ());
286 }
287
288 #if IVSIZE >= 8
289
290 static U64TYPE
291 process_integer64 (void)
292 {
293 U32 length = process_length ();
294
295 if (length <= 0)
296 {
297 error ("INTEGER64 length equal to zero");
298 return 0;
299 }
300
301 U8 *data = getn (length, 0);
302
303 if (!data)
304 return 0;
305
306 if (length > 9 || (length > 8 && data [0]))
307 {
308 error ("INTEGER64 length too long");
309 return 0;
310 }
311
312 U64TYPE res = data [0] & 0x80 ? 0xffffffffffffffff : 0;
313
314 while (length--)
315 res = (res << 8) | *data++;
316
317 return res;
318 }
319
320 static SV *
321 process_integer64_sv (void)
322 {
323 return newSViv ((I64TYPE)process_integer64 ());
324 }
325
326 static SV *
327 process_unsigned64_sv (void)
328 {
329 return newSVuv ((U64TYPE)process_integer64 ());
330 }
331
332 #endif
333
334 static SV *
335 process_octet_string_sv (void)
336 {
337 U32 length = process_length ();
338
339 U8 *data = getn (length, 0);
340 if (!data)
341 {
342 error ("OCTET STRING too long");
343 return &PL_sv_undef;
344 }
345
346 return newSVpvn (data, length);
347 }
348
349 static char *
350 write_uv (char *buf, U32 u)
351 {
352 // the one-digit case is absolutely predominant
353 if (u < 10)
354 *buf++ = u + '0';
355 else
356 buf += sprintf (buf, "%u", (unsigned int)u);
357
358 return buf;
359 }
360
361 static SV *
362 process_object_identifier_sv (void)
363 {
364 U32 length = process_length ();
365
366 if (length <= 0)
367 {
368 error ("OBJECT IDENTIFIER length equal to zero");
369 return &PL_sv_undef;
370 }
371
372 U8 *end = cur + length;
373 U32 w = getb ();
374
375 static char oid[MAX_OID_STRLEN]; // must be static
376 char *app = oid;
377
378 if (leading_dot < 0)
379 leading_dot = SvTRUE (*hv_fetch ((HV *)SvRV (msg), "_leading_dot", sizeof ("_leading_dot") - 1, 1));
380
381 *app = '.'; app += ! ! leading_dot;
382 app = write_uv (app, (U8)w / 40);
383 *app++ = '.';
384 app = write_uv (app, (U8)w % 40);
385
386 // we assume an oid component is never > 64 bytes
387 while (cur < end && oid + sizeof (oid) - app > 64)
388 {
389 w = getb ();
390 *app++ = '.';
391 app = write_uv (app, w);
392 }
393
394 return newSVpvn (oid, app - oid);
395 }
396
397 static AV *av_type;
398
399 static SV *
400 process_sv (int *found)
401 {
402 int type = get8 ();
403
404 *found = type;
405
406 SV *res;
407
408 switch (type)
409 {
410 case ASN_OBJECT_IDENTIFIER:
411 res = process_object_identifier_sv ();
412 break;
413
414 case ASN_INTEGER32:
415 res = process_integer32_sv ();
416 break;
417
418 case ASN_UNSIGNED32:
419 case ASN_COUNTER32:
420 case ASN_TIMETICKS:
421 res = process_unsigned32_sv ();
422 break;
423
424 case ASN_SEQUENCE:
425 res = newSVuv (process_length ());
426 break;
427
428 case ASN_OCTET_STRING:
429 case ASN_OPAQUE:
430 res = process_octet_string_sv ();
431 break;
432
433 default:
434 {
435 if (type > AvFILLp (av_type)
436 || AvARRAY (av_type)[type] == 0
437 || AvARRAY (av_type)[type] == &PL_sv_undef)
438 {
439 error ("Unknown ASN.1 type");
440 return &PL_sv_undef;
441 }
442
443 dSP;
444 PUSHMARK (SP);
445 EXTEND (SP, 2);
446 PUSHs (msg);
447 PUSHs (sv_2mortal (newSViv (type)));
448 PUTBACK;
449 int count = call_sv (AvARRAY (av_type)[type], G_SCALAR);
450 SPAGAIN;
451 res = count ? SvREFCNT_inc (TOPs) : &PL_sv_undef;
452 }
453 }
454
455 return errflag ? &PL_sv_undef : res;
456 }
457
458 static SV *
459 decode_ber ()
460 {
461 int identifier = get8 ();
462
463 SV *res;
464
465 int constructed = identifier & ASN_CONSTRUCTED;
466 int klass = identifier & ASN_CLASS_MASK;
467 int tag = identifier & ASN_TAG_MASK;
468
469 if (tag == ASN_TAG_BER)
470 tag = getb ();
471
472 #if 0
473 if (/type & ASN_TAG_MASK) == ASN_TAG_MASK)
474 /* TODO: ber tag follows */;
475 #endif
476
477 if (constructed)
478 {
479 U32 len = process_length ();
480 U32 seqend = (cur - buf) + len;
481 AV *av = (AV *)sv_2mortal ((SV *)newAV ());
482
483 while (cur < buf + seqend)
484 {
485 av_push (av, decode_ber ());
486 }
487
488 if (cur > buf + seqend)
489 croak ("constructed type %02x overflow (%x %x)\n", identifier, cur - buf, seqend);
490
491 res = newRV_inc ((SV *)av);
492 }
493 else
494 switch (identifier)
495 {
496 case ASN_OBJECT_IDENTIFIER:
497 res = process_object_identifier_sv ();
498 break;
499
500 case ASN_INTEGER32:
501 res = process_integer32_sv ();
502 break;
503
504 case ASN_UNSIGNED32:
505 case ASN_COUNTER32:
506 case ASN_TIMETICKS:
507 res = process_unsigned32_sv ();
508 break;
509
510 #if 0 // handled by default case
511 case ASN_IPADDRESS:
512 case ASN_OCTET_STRING:
513 case ASN_OPAQUE:
514 res = process_octet_string_sv ();
515 break;
516 #endif
517
518 case ASN_COUNTER64:
519 res = process_integer64_sv ();
520 break;
521
522 default:
523 res = process_octet_string_sv ();
524 break;
525 }
526
527 if (errflag)
528 croak ("some error");
529
530 AV *av = newAV ();
531 av_fill (av, 4-1);
532 AvARRAY (av)[0] = newSVcacheint (tag);
533 AvARRAY (av)[1] = newSVcacheint (klass >> ASN_CLASS_SHIFT);
534 AvARRAY (av)[2] = newSVcacheint (constructed ? 1 : 0);
535 AvARRAY (av)[3] = res;
536 res = newRV_noinc ((SV *)av);
537
538 return errflag ? &PL_sv_undef : res;
539 }
540
541 /////////////////////////////////////////////////////////////////////////////
542
543 #if HAVE_VERSIONSORT
544
545 static int
546 oid_lex_cmp (const void *a_, const void *b_)
547 {
548 const char *a = SvPVX (*(SV **)a_);
549 const char *b = SvPVX (*(SV **)b_);
550
551 a += *a == '.';
552 b += *b == '.';
553
554 return strverscmp (a, b);
555 }
556
557 #endif
558
559 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
560
561 PROTOTYPES: ENABLE
562
563 BOOT:
564 {
565 HV *stash = gv_stashpv ("Net::SNMP::XS", 1);
566
567 static const struct {
568 const char *name;
569 IV iv;
570 } *civ, const_iv[] = {
571 { "ASN_BOOLEAN", ASN_BOOLEAN },
572 { "ASN_INTEGER32", ASN_INTEGER32 },
573 { "ASN_BIT_STRING", ASN_BIT_STRING },
574 { "ASN_OCTET_STRING", ASN_OCTET_STRING },
575 { "ASN_NULL", ASN_NULL },
576 { "ASN_OBJECT_IDENTIFIER", ASN_OBJECT_IDENTIFIER },
577 { "ASN_TAG_BER", ASN_TAG_BER },
578 { "ASN_TAG_MASK", ASN_TAG_MASK },
579 { "ASN_CONSTRUCTED", ASN_CONSTRUCTED },
580 { "ASN_UNIVERSAL", ASN_UNIVERSAL >> ASN_CLASS_SHIFT },
581 { "ASN_APPLICATION", ASN_APPLICATION >> ASN_CLASS_SHIFT },
582 { "ASN_CONTEXT", ASN_CONTEXT >> ASN_CLASS_SHIFT },
583 { "ASN_PRIVATE", ASN_PRIVATE >> ASN_CLASS_SHIFT },
584 { "ASN_CLASS_MASK", ASN_CLASS_MASK },
585 { "ASN_CLASS_SHIFT", ASN_CLASS_SHIFT },
586 { "ASN_SEQUENCE", ASN_SEQUENCE },
587 { "ASN_IPADDRESS", ASN_IPADDRESS },
588 { "ASN_COUNTER32", ASN_COUNTER32 },
589 { "ASN_UNSIGNED32", ASN_UNSIGNED32 },
590 { "ASN_TIMETICKS", ASN_TIMETICKS },
591 { "ASN_OPAQUE", ASN_OPAQUE },
592 { "ASN_COUNTER64", ASN_COUNTER64 },
593 };
594
595 for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); civ > const_iv; civ--)
596 newCONSTSUB (stash, (char *)civ[-1].name, newSViv (civ[-1].iv));
597
598 av_type = newAV ();
599 }
600
601 void
602 set_type (int type, SV *cv)
603 CODE:
604 cv = x_get_cv (cv);
605 assert (SvTYPE (cv) == SVt_PVCV);
606 av_store (av_type, type, SvREFCNT_inc_NN (cv));
607
608 #if 1
609
610 SV *
611 decode_ber (SV *ber)
612 CODE:
613 {
614 clear_bufobj ();
615
616 errflag = 0;
617 leading_dot = 0;
618
619 bufsv = ber;
620
621 buf = SvPVbyte (bufsv, len);
622 cur = buf;
623 rem = len;
624
625 RETVAL = decode_ber ();
626 }
627 OUTPUT: RETVAL
628
629 #endif
630
631 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
632
633 void
634 _buffer_append (BUFOBJ self, SV *value)
635 ALIAS:
636 _buffer_put = 1
637 PPCODE:
638 {
639 STRLEN vlen;
640 const char *vstr = SvPVbyte (value, vlen);
641
642 if (ix)
643 sv_insert (bufsv, 0, 0, vstr, vlen);
644 else
645 sv_catpvn (bufsv, vstr, vlen);
646
647 buf = SvPVbyte (bufsv, len);
648 cur = buf;
649 rem = len;
650
651 SV *len_sv = *hv_fetch ((HV *)cur_bufobj, "_length", sizeof ("_length") - 1, 1);
652 sv_setiv (len_sv, len);
653
654 // some callers test for defined'ness of the returnvalue. *sigh*
655 XPUSHs (&PL_sv_yes);
656 }
657
658 void
659 _buffer_get (BUFOBJ self, int count = -1)
660 PPCODE:
661 {
662 // grrr.
663 if (count < 0)
664 {
665 hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
666 hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
667 XPUSHs (sv_2mortal (newSVsv (bufsv)));
668 sv_setpvn (bufsv, "", 0);
669
670 buf = "";
671 cur = buf;
672 rem = 0;
673
674 XSRETURN (1);
675 }
676
677 char *data = getn (count, 0);
678
679 if (data)
680 XPUSHs (sv_2mortal (newSVpvn (data, count)));
681 }
682
683 U32
684 index (BUFOBJ self, int ndx = -1)
685 CODE:
686 {
687 if (ndx >= 0 && ndx < len)
688 {
689 cur = buf + ndx;
690 rem = len - ndx;
691 }
692
693 RETVAL = cur - buf;
694 }
695 OUTPUT: RETVAL
696
697 U32
698 _process_length (BUFOBJ self, ...)
699 ALIAS:
700 _process_sequence = 0
701 CODE:
702 RETVAL = process_length ();
703 OUTPUT: RETVAL
704
705 SV *
706 _process_integer32 (BUFOBJ self, ...)
707 CODE:
708 RETVAL = process_integer32_sv ();
709 OUTPUT: RETVAL
710
711 SV *
712 _process_counter (BUFOBJ self, ...)
713 ALIAS:
714 _process_gauge = 0
715 _process_timeticks = 0
716 CODE:
717 RETVAL = process_unsigned32_sv ();
718 OUTPUT: RETVAL
719
720 #if IVSIZE >= 8
721
722 SV *
723 _process_counter64 (BUFOBJ self, ...)
724 CODE:
725 RETVAL = process_unsigned64_sv ();
726 OUTPUT: RETVAL
727
728 #endif
729
730 SV *
731 _process_object_identifier (BUFOBJ self, ...)
732 CODE:
733 RETVAL = process_object_identifier_sv ();
734 OUTPUT: RETVAL
735
736 SV *
737 _process_octet_string (BUFOBJ self, ...)
738 ALIAS:
739 _process_opaque = 0
740 CODE:
741 RETVAL = process_octet_string_sv ();
742 OUTPUT: RETVAL
743
744 SV *
745 _process_ipaddress (BUFOBJ self, ...)
746 CODE:
747 {
748 U32 length = process_length ();
749 if (length != 4)
750 {
751 error ("IP ADDRESS length not four");
752 XSRETURN_UNDEF;
753 }
754
755 U8 *data = getn (4, "\x00\x00\x00\x00");
756 RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]);
757 }
758 OUTPUT: RETVAL
759
760 SV *
761 process (BUFOBJ self, SV *expected = &PL_sv_undef, SV *found = 0)
762 CODE:
763 {
764 int type;
765
766 RETVAL = process_sv (&type);
767
768 if (found)
769 sv_setiv (found, type);
770
771 if (SvOK (expected) && type != SvIV (expected))
772 error ("Expected a different type than found");
773 }
774 OUTPUT: RETVAL
775
776 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU
777
778 SV *
779 _process_var_bind_list (BUFOBJ self)
780 CODE:
781 {
782 if (get8 () != ASN_SEQUENCE)
783 error ("SEQUENCE expected at beginning of VarBindList");
784 int seqlen = process_length ();
785 U8 *end = cur + seqlen;
786
787 HV *list = newHV ();
788 AV *names = newAV ();
789 HV *types = newHV ();
790
791 hv_store ((HV *)cur_bufobj, "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0);
792 hv_store ((HV *)cur_bufobj, "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0);
793 hv_store ((HV *)cur_bufobj, "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0);
794
795 while (cur < end && !errflag)
796 {
797 // SEQUENCE ObjectName ObjectSyntax
798 if (get8 () != ASN_SEQUENCE)
799 error ("SEQUENCE expected at beginning of VarBind");
800 process_length ();
801
802 if (get8 () != ASN_OBJECT_IDENTIFIER)
803 error ("OBJECT IDENTIFIER expected at beginning of VarBind");
804 int type, oidlen;
805 SV *oid = process_object_identifier_sv ();
806 SV *val = process_sv (&type);
807
808 hv_store_ent (types, oid, newSViv (type), 0);
809 hv_store_ent (list , oid, val, 0);
810 av_push (names, oid);
811 }
812
813 // sigh - great design to do it here
814 SV *pdu_type = *hv_fetch ((HV *)cur_bufobj, "_pdu_type" , sizeof ("_pdu_type" ) - 1, 1);
815
816 if (SvIV (pdu_type) == 0xa8) // REPORT
817 {
818 PUSHMARK (SP);
819 XPUSHs (msg);
820 PUTBACK;
821 call_method ("_report_pdu_error", G_VOID | G_DISCARD);
822 SPAGAIN;
823 XSRETURN_EMPTY;
824 }
825
826 RETVAL = newRV_inc ((SV *)list);
827 }
828 OUTPUT: RETVAL
829
830 MODULE = Net::SNMP::XS PACKAGE = Net::SNMP
831
832 void
833 oid_base_match (SV *base_, SV *oid_)
834 PROTOTYPE: $$
835 ALIAS:
836 oid_context_match = 0
837 PPCODE:
838 {
839 if (!SvOK (base_) || !SvOK (oid_))
840 XSRETURN_NO;
841
842 STRLEN blen, olen;
843 char *base = SvPVbyte (base_, blen);
844 char *oid = SvPVbyte (oid_ , olen);
845
846 blen -= *base == '.'; base += *base == '.';
847 olen -= *base == '.'; oid += *oid == '.';
848
849 if (olen < blen)
850 XSRETURN_NO;
851
852 if (memcmp (base, oid, blen))
853 XSRETURN_NO;
854
855 if (oid [blen] && oid [blen] != '.')
856 XSRETURN_NO;
857
858 XSRETURN_YES;
859 }
860
861 #if HAVE_VERSIONSORT
862
863 void
864 oid_lex_sort (...)
865 PROTOTYPE: @
866 PPCODE:
867 {
868 // make sure SvPVX is valid
869 int i;
870 for (i = items; i--; )
871 {
872 SV *sv = ST (i);
873
874 if (SvTYPE (sv) < SVt_PV || SvTYPE (sv) == SVt_PVAV && SvTYPE (sv) == SVt_PVHV)
875 SvPV_force_nolen (sv);
876 }
877
878 qsort (&ST (0), items, sizeof (SV *), oid_lex_cmp);
879
880 EXTEND (SP, items);
881 // we cheat somewhat by not returning copies here
882 for (i = 0; i < items; ++i)
883 PUSHs (sv_2mortal (SvREFCNT_inc (ST (i))));
884 }
885
886 int
887 _index_cmp (const char *a, const char *b)
888 PROTOTYPE: $$
889 CODE:
890 RETVAL = strverscmp (a, b);
891 OUTPUT: RETVAL
892
893 #endif
894