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.11 by root, Sun Apr 12 00:48:39 2009 UTC vs.
Revision 1.19 by root, Fri Apr 19 14:50:44 2019 UTC

4 4
5// C99 required 5// C99 required
6 6
7//#define BENCHMARK 7//#define BENCHMARK
8 8
9enum {
9#define ASN_BOOLEAN 0x01 10 ASN_BOOLEAN = 0x01,
10#define ASN_INTEGER32 0x02 11 ASN_INTEGER32 = 0x02,
12 ASN_BIT_STRING = 0x03,
11#define ASN_OCTET_STRING 0x04 13 ASN_OCTET_STRING = 0x04,
12#define ASN_NULL 0x05 14 ASN_NULL = 0x05,
13#define ASN_OBJECT_IDENTIFIER 0x06 15 ASN_OBJECT_IDENTIFIER = 0x06,
14#define ASN_SEQUENCE 0x30 16
15#define ASN_IPADDRESS 0x40 17 ASN_TAG_BER = 0x1f,
16#define ASN_COUNTER32 0x41 18 ASN_TAG_MASK = 0x1f,
17#define ASN_UNSIGNED32 0x42 19
18#define ASN_TIMETICKS 0x43 20 ASN_CONSTRUCTED = 0x20,
19#define ASN_OPAQUE 0x44 21
20#define ASN_COUNTER64 0x46 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};
21 38
22#define MAX_OID_STRLEN 4096 39#define MAX_OID_STRLEN 4096
23 40
24#define HAVE_VERSIONSORT defined (_GNU_SOURCE) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 41#define HAVE_VERSIONSORT defined (_GNU_SOURCE) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
25 42
43static SV *cur_bufobj;
26static SV *msg; 44static SV *msg, *bufsv;
27static int errflag, leading_dot; 45static int errflag, leading_dot;
28static U8 *buf, *cur; 46static U8 *buf, *cur;
29static STRLEN len, rem; 47static STRLEN len, rem;
48
49typedef SV *BUFOBJ;
50
51// for "small" integers, return a readonly sv, otherwise create a new one
52static 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
75void
76clr_msg ()
77 CODE:
78 SvREFCNT_dec (msg); msg = 0;
79 buf = cur = (U8 *)"";
80 len = rem = 0;
81#endif
82
83static void
84clear_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
98static void
99switch_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/////////////////////////////////////////////////////////////////////////////
30 119
31static SV * 120static SV *
32x_get_cv (SV *cb_sv) 121x_get_cv (SV *cb_sv)
33{ 122{
34 HV *st; 123 HV *st;
341 res = process_octet_string_sv (); 430 res = process_octet_string_sv ();
342 break; 431 break;
343 432
344 default: 433 default:
345 { 434 {
346 if (type > AvFILLp (av_type) || !SvTYPE (AvARRAY (av_type)[type]) == SVt_PVCV) 435 if (type > AvFILLp (av_type)
436 || AvARRAY (av_type)[type] == 0
437 || AvARRAY (av_type)[type] == &PL_sv_undef)
347 { 438 {
348 error ("Unknown ASN.1 type"); 439 error ("Unknown ASN.1 type");
349 return &PL_sv_undef; 440 return &PL_sv_undef;
350 } 441 }
351 442
362 } 453 }
363 454
364 return errflag ? &PL_sv_undef : res; 455 return errflag ? &PL_sv_undef : res;
365} 456}
366 457
458static SV *
459decode_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
367///////////////////////////////////////////////////////////////////////////// 541/////////////////////////////////////////////////////////////////////////////
368 542
369#if HAVE_VERSIONSORT 543#if HAVE_VERSIONSORT
370 544
371static int 545static int
385MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS 559MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::XS
386 560
387PROTOTYPES: ENABLE 561PROTOTYPES: ENABLE
388 562
389BOOT: 563BOOT:
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
390 av_type = newAV (); 598 av_type = newAV ();
599}
391 600
392void 601void
393set_type (int type, SV *cv) 602set_type (int type, SV *cv)
394 CODE: 603 CODE:
604 cv = x_get_cv (cv);
605 assert (SvTYPE (cv) == SVt_PVCV);
395 av_store (av_type, type, SvREFCNT_inc (x_get_cv (cv))); 606 av_store (av_type, type, SvREFCNT_inc_NN (cv));
607
608#if 1
609
610SV *
611decode_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
631MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
396 632
397void 633void
398set_msg (SV *msg_, SV *buf_) 634_buffer_append (BUFOBJ self, SV *value)
635 ALIAS:
636 _buffer_put = 1
399 CODE: 637 PPCODE:
400{ 638{
639 STRLEN vlen;
640 const char *vstr = SvPVbyte (value, vlen);
641
401 if (msg) 642 if (ix)
402 croak ("recursive invocation of Net::SNMP::XS parser is not supported"); 643 sv_insert (bufsv, 0, 0, vstr, vlen);
644 else
645 sv_catpvn (bufsv, vstr, vlen);
403 646
404 errflag = 0;
405 leading_dot = -1;
406 msg = SvREFCNT_inc (msg_);
407 buf = SvPVbyte (buf_, len); 647 buf = SvPVbyte (bufsv, len);
408 cur = buf; 648 cur = buf;
409 rem = len; 649 rem = len;
410#ifdef BENCHMARK 650
411 t1 = tstamp (); 651 SV *len_sv = *hv_fetch ((HV *)cur_bufobj, "_length", sizeof ("_length") - 1, 1);
412#endif 652 sv_setiv (len_sv, len);
653
654 // some callers test for defined'ness of the returnvalue. *sigh*
655 XPUSHs (&PL_sv_yes);
413} 656}
414 657
415void 658void
416clr_msg ()
417 CODE:
418 SvREFCNT_dec (msg); msg = 0;
419 buf = cur = (U8 *)"";
420 len = rem = 0;
421#ifdef BENCHMARK
422 printf ("%f\n", tstamp () - t1);//D
423#endif
424
425MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::Message
426
427void
428_buffer_get (SV *self, int count = -1) 659_buffer_get (BUFOBJ self, int count = -1)
429 PPCODE: 660 PPCODE:
430{ 661{
431 // grrr. 662 // grrr.
432 if (count < 0) 663 if (count < 0)
433 { 664 {
434 hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD); 665 hv_delete ((HV *)SvRV (self), "_index" , 6, G_DISCARD);
435 hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD); 666 hv_delete ((HV *)SvRV (self), "_length", 7, G_DISCARD);
436 SV **svp = hv_fetch ((HV *)SvRV (self), "_buffer", 7, 1);
437 XPUSHs (sv_2mortal (newSVsv (*svp))); 667 XPUSHs (sv_2mortal (newSVsv (bufsv)));
438 sv_setpvn (*svp, "", 0); 668 sv_setpvn (bufsv, "", 0);
669
670 buf = "";
671 cur = buf;
672 rem = 0;
673
439 XSRETURN (1); 674 XSRETURN (1);
440 } 675 }
441 676
442 char *data = getn (count, 0); 677 char *data = getn (count, 0);
443 678
444 if (data) 679 if (data)
445 XPUSHs (sv_2mortal (newSVpvn (data, count))); 680 XPUSHs (sv_2mortal (newSVpvn (data, count)));
446} 681}
447 682
448U32 683U32
449index (SV *self, int ndx = -1) 684index (BUFOBJ self, int ndx = -1)
450 CODE: 685 CODE:
451{ 686{
452 if (ndx >= 0 && ndx < len) 687 if (ndx >= 0 && ndx < len)
453 { 688 {
454 cur = buf + ndx; 689 cur = buf + ndx;
455 rem = len - ndx; 690 rem = len - ndx;
456 } 691 }
457 692
458 RETVAL = cur - buf; 693 RETVAL = cur - buf;
459} 694}
460 OUTPUT: 695 OUTPUT: RETVAL
461 RETVAL
462 696
463U32 697U32
464_process_length (SV *self, ...) 698_process_length (BUFOBJ self, ...)
465 ALIAS: 699 ALIAS:
466 _process_sequence = 0 700 _process_sequence = 0
467 CODE: 701 CODE:
468 RETVAL = process_length (); 702 RETVAL = process_length ();
469 OUTPUT: 703 OUTPUT: RETVAL
470 RETVAL
471 704
472SV * 705SV *
473_process_integer32 (SV *self, ...) 706_process_integer32 (BUFOBJ self, ...)
474 CODE: 707 CODE:
475 RETVAL = process_integer32_sv (); 708 RETVAL = process_integer32_sv ();
476 OUTPUT: 709 OUTPUT: RETVAL
477 RETVAL
478 710
479SV * 711SV *
480_process_counter (SV *self, ...) 712_process_counter (BUFOBJ self, ...)
481 ALIAS: 713 ALIAS:
482 _process_gauge = 0 714 _process_gauge = 0
483 _process_timeticks = 0 715 _process_timeticks = 0
484 CODE: 716 CODE:
485 RETVAL = process_unsigned32_sv (); 717 RETVAL = process_unsigned32_sv ();
486 OUTPUT: 718 OUTPUT: RETVAL
487 RETVAL
488 719
489#if IVSIZE >= 8 720#if IVSIZE >= 8
490 721
491SV * 722SV *
492_process_counter64 (SV *self, ...) 723_process_counter64 (BUFOBJ self, ...)
493 CODE: 724 CODE:
494 RETVAL = process_unsigned64_sv (); 725 RETVAL = process_unsigned64_sv ();
495 OUTPUT: 726 OUTPUT: RETVAL
496 RETVAL
497 727
498#endif 728#endif
499 729
500SV * 730SV *
501_process_object_identifier (SV *self, ...) 731_process_object_identifier (BUFOBJ self, ...)
502 CODE: 732 CODE:
503 RETVAL = process_object_identifier_sv (); 733 RETVAL = process_object_identifier_sv ();
504 OUTPUT: 734 OUTPUT: RETVAL
505 RETVAL
506 735
507SV * 736SV *
508_process_octet_string (SV *self, ...) 737_process_octet_string (BUFOBJ self, ...)
509 ALIAS: 738 ALIAS:
510 _process_opaque = 0 739 _process_opaque = 0
511 CODE: 740 CODE:
512 RETVAL = process_octet_string_sv (); 741 RETVAL = process_octet_string_sv ();
513 OUTPUT: 742 OUTPUT: RETVAL
514 RETVAL
515 743
516SV * 744SV *
517_process_ipaddress (SV *self, ...) 745_process_ipaddress (BUFOBJ self, ...)
518 CODE: 746 CODE:
519{ 747{
520 U32 length = process_length (); 748 U32 length = process_length ();
521 if (length != 4) 749 if (length != 4)
522 { 750 {
523 error ("IP ADDRESS length not four"); 751 error ("IP ADDRESS length not four");
524 XSRETURN_UNDEF; 752 XSRETURN_UNDEF;
525 } 753 }
526 754
527 U8 *data = getn (4, "\x00\x00\x00\x00"); 755 U8 *data = getn (4, "\x00\x00\x00\x00");
528 RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]); 756 RETVAL = newSVpvf ("%d.%d.%d.%d", data [0], data [1], data [2], data [3]);
529} 757}
530 OUTPUT: 758 OUTPUT: RETVAL
531 RETVAL
532 759
533SV * 760SV *
534process (SV *self, SV *expected = &PL_sv_undef, SV *found = 0) 761process (BUFOBJ self, SV *expected = &PL_sv_undef, SV *found = 0)
535 CODE: 762 CODE:
536{ 763{
537 int type; 764 int type;
538 765
539 RETVAL = process_sv (&type); 766 RETVAL = process_sv (&type);
540 767
541 if (found) 768 if (found)
542 sv_setiv (found, type); 769 sv_setiv (found, type);
543 770
544 if (SvOK (expected) && type != SvIV (expected)) 771 if (SvOK (expected) && type != SvIV (expected))
545 error ("Expected a different type than found"); 772 error ("Expected a different type than found");
546} 773}
547 OUTPUT: 774 OUTPUT: RETVAL
548 RETVAL
549 775
550MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU 776MODULE = Net::SNMP::XS PACKAGE = Net::SNMP::PDU
551 777
552SV * 778SV *
553_process_var_bind_list (SV *self) 779_process_var_bind_list (BUFOBJ self)
554 CODE: 780 CODE:
555{ 781{
556 if (get8 () != ASN_SEQUENCE) 782 if (get8 () != ASN_SEQUENCE)
557 error ("SEQUENCE expected at beginning of VarBindList"); 783 error ("SEQUENCE expected at beginning of VarBindList");
558 int seqlen = process_length (); 784 int seqlen = process_length ();
560 786
561 HV *list = newHV (); 787 HV *list = newHV ();
562 AV *names = newAV (); 788 AV *names = newAV ();
563 HV *types = newHV (); 789 HV *types = newHV ();
564 790
565 hv_store ((HV *)SvRV (self), "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0); 791 hv_store ((HV *)cur_bufobj, "_var_bind_list" , sizeof ("_var_bind_list" ) - 1, newRV_noinc ((SV *)list ), 0);
566 hv_store ((HV *)SvRV (self), "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0); 792 hv_store ((HV *)cur_bufobj, "_var_bind_names", sizeof ("_var_bind_names") - 1, newRV_noinc ((SV *)names), 0);
567 hv_store ((HV *)SvRV (self), "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0); 793 hv_store ((HV *)cur_bufobj, "_var_bind_types", sizeof ("_var_bind_types") - 1, newRV_noinc ((SV *)types), 0);
568 794
569 while (cur < end && !errflag) 795 while (cur < end && !errflag)
570 { 796 {
571 // SEQUENCE ObjectName ObjectSyntax 797 // SEQUENCE ObjectName ObjectSyntax
572 if (get8 () != ASN_SEQUENCE) 798 if (get8 () != ASN_SEQUENCE)
582 hv_store_ent (types, oid, newSViv (type), 0); 808 hv_store_ent (types, oid, newSViv (type), 0);
583 hv_store_ent (list , oid, val, 0); 809 hv_store_ent (list , oid, val, 0);
584 av_push (names, oid); 810 av_push (names, oid);
585 } 811 }
586 812
587 //return $this->_report_pdu_error if ($this->{_pdu_type} == REPORT); 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 }
588 825
589 RETVAL = newRV_inc ((SV *)list); 826 RETVAL = newRV_inc ((SV *)list);
590} 827}
591 OUTPUT: 828 OUTPUT: RETVAL
592 RETVAL
593 829
594MODULE = Net::SNMP::XS PACKAGE = Net::SNMP 830MODULE = Net::SNMP::XS PACKAGE = Net::SNMP
595 831
596void 832void
597oid_base_match (SV *base_, SV *oid_) 833oid_base_match (SV *base_, SV *oid_)
602{ 838{
603 if (!SvOK (base_) || !SvOK (oid_)) 839 if (!SvOK (base_) || !SvOK (oid_))
604 XSRETURN_NO; 840 XSRETURN_NO;
605 841
606 STRLEN blen, olen; 842 STRLEN blen, olen;
607 char *base = SvPV (base_, blen); 843 char *base = SvPVbyte (base_, blen);
608 char *oid = SvPV (oid_ , olen); 844 char *oid = SvPVbyte (oid_ , olen);
609 845
610 blen -= *base == '.'; base += *base == '.'; 846 blen -= *base == '.'; base += *base == '.';
611 olen -= *base == '.'; oid += *oid == '.'; 847 olen -= *base == '.'; oid += *oid == '.';
612 848
613 if (olen < blen) 849 if (olen < blen)
650int 886int
651_index_cmp (const char *a, const char *b) 887_index_cmp (const char *a, const char *b)
652 PROTOTYPE: $$ 888 PROTOTYPE: $$
653 CODE: 889 CODE:
654 RETVAL = strverscmp (a, b); 890 RETVAL = strverscmp (a, b);
655 OUTPUT: 891 OUTPUT: RETVAL
656 RETVAL
657 892
658#endif 893#endif
659 894

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines