ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-SNMP-XS/XS.xs
(Generate patch)

Comparing Net-SNMP-XS/XS.xs (file contents):
Revision 1.2 by root, Wed Apr 8 10:39:32 2009 UTC vs.
Revision 1.18 by root, Fri Apr 19 10:11:03 2019 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines