comparison src/xdiff/xdiffi.c @ 25709:d5142d87f898 v8.2.3390

patch 8.2.3390: included xdiff code is outdated Commit: https://github.com/vim/vim/commit/ba02e4720f863fdb456e7023520f0a354eec0dcf Author: Christian Brabandt <cb@256bit.org> Date: Tue Aug 31 20:46:39 2021 +0200 patch 8.2.3390: included xdiff code is outdated Problem: Included xdiff code is outdated. Solution: Sync with xdiff in git 2.33. (Christian Brabandt, closes https://github.com/vim/vim/issues/8431)
author Bram Moolenaar <Bram@vim.org>
date Tue, 31 Aug 2021 21:00:05 +0200
parents 3be01cf0a632
children f84e5db372ea
comparison
equal deleted inserted replaced
25708:a1f90f486bf7 25709:d5142d87f898
36 /* 36 /*
37 * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers. 37 * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
38 * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both 38 * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
39 * the forward diagonal starting from (off1, off2) and the backward diagonal 39 * the forward diagonal starting from (off1, off2) and the backward diagonal
40 * starting from (lim1, lim2). If the K values on the same diagonal crosses 40 * starting from (lim1, lim2). If the K values on the same diagonal crosses
41 * returns the furthest point of reach. We might end up having to expensive 41 * returns the furthest point of reach. We might encounter expensive edge cases
42 * cases using this algorithm is full, so a little bit of heuristic is needed 42 * using this algorithm, so a little bit of heuristic is needed to cut the
43 * to cut the search and to return a suboptimal point. 43 * search and to return a suboptimal point.
44 */ 44 */
45 static long xdl_split(unsigned long const *ha1, long off1, long lim1, 45 static long xdl_split(unsigned long const *ha1, long off1, long lim1,
46 unsigned long const *ha2, long off2, long lim2, 46 unsigned long const *ha2, long off2, long lim2,
47 long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, 47 long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
48 xdalgoenv_t *xenv) { 48 xdalgoenv_t *xenv) {
61 61
62 for (ec = 1;; ec++) { 62 for (ec = 1;; ec++) {
63 int got_snake = 0; 63 int got_snake = 0;
64 64
65 /* 65 /*
66 * We need to extent the diagonal "domain" by one. If the next 66 * We need to extend the diagonal "domain" by one. If the next
67 * values exits the box boundaries we need to change it in the 67 * values exits the box boundaries we need to change it in the
68 * opposite direction because (max - min) must be a power of two. 68 * opposite direction because (max - min) must be a power of
69 * two.
70 *
69 * Also we initialize the external K value to -1 so that we can 71 * Also we initialize the external K value to -1 so that we can
70 * avoid extra conditions check inside the core loop. 72 * avoid extra conditions in the check inside the core loop.
71 */ 73 */
72 if (fmin > dmin) 74 if (fmin > dmin)
73 kvdf[--fmin - 1] = -1; 75 kvdf[--fmin - 1] = -1;
74 else 76 else
75 ++fmin; 77 ++fmin;
96 return ec; 98 return ec;
97 } 99 }
98 } 100 }
99 101
100 /* 102 /*
101 * We need to extent the diagonal "domain" by one. If the next 103 * We need to extend the diagonal "domain" by one. If the next
102 * values exits the box boundaries we need to change it in the 104 * values exits the box boundaries we need to change it in the
103 * opposite direction because (max - min) must be a power of two. 105 * opposite direction because (max - min) must be a power of
106 * two.
107 *
104 * Also we initialize the external K value to -1 so that we can 108 * Also we initialize the external K value to -1 so that we can
105 * avoid extra conditions check inside the core loop. 109 * avoid extra conditions in the check inside the core loop.
106 */ 110 */
107 if (bmin > dmin) 111 if (bmin > dmin)
108 kvdb[--bmin - 1] = XDL_LINE_MAX; 112 kvdb[--bmin - 1] = XDL_LINE_MAX;
109 else 113 else
110 ++bmin; 114 ++bmin;
136 continue; 140 continue;
137 141
138 /* 142 /*
139 * If the edit cost is above the heuristic trigger and if 143 * If the edit cost is above the heuristic trigger and if
140 * we got a good snake, we sample current diagonals to see 144 * we got a good snake, we sample current diagonals to see
141 * if some of the, have reached an "interesting" path. Our 145 * if some of them have reached an "interesting" path. Our
142 * measure is a function of the distance from the diagonal 146 * measure is a function of the distance from the diagonal
143 * corner (i1 + i2) penalized with the distance from the 147 * corner (i1 + i2) penalized with the distance from the
144 * mid diagonal itself. If this value is above the current 148 * mid diagonal itself. If this value is above the current
145 * edit cost times a magic factor (XDL_K_HEUR) we consider 149 * edit cost times a magic factor (XDL_K_HEUR) we consider
146 * it interesting. 150 * it interesting.
194 return ec; 198 return ec;
195 } 199 }
196 } 200 }
197 201
198 /* 202 /*
199 * Enough is enough. We spent too much time here and now we collect 203 * Enough is enough. We spent too much time here and now we
200 * the furthest reaching path using the (i1 + i2) measure. 204 * collect the furthest reaching path using the (i1 + i2)
205 * measure.
201 */ 206 */
202 if (ec >= xenv->mxcost) { 207 if (ec >= xenv->mxcost) {
203 long fbest, fbest1, bbest, bbest1; 208 long fbest, fbest1, bbest, bbest1;
204 209
205 fbest = fbest1 = -1; 210 fbest = fbest1 = -1;
242 } 247 }
243 } 248 }
244 249
245 250
246 /* 251 /*
247 * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling 252 * Rule: "Divide et Impera" (divide & conquer). Recursively split the box in
248 * the box splitting function. Note that the real job (marking changed lines) 253 * sub-boxes by calling the box splitting function. Note that the real job
249 * is done in the two boundary reaching checks. 254 * (marking changed lines) is done in the two boundary reaching checks.
250 */ 255 */
251 int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, 256 int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
252 diffdata_t *dd2, long off2, long lim2, 257 diffdata_t *dd2, long off2, long lim2,
253 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) { 258 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
254 unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha; 259 unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
321 326
322 return -1; 327 return -1;
323 } 328 }
324 329
325 /* 330 /*
326 * Allocate and setup K vectors to be used by the differential algorithm. 331 * Allocate and setup K vectors to be used by the differential
332 * algorithm.
333 *
327 * One is to store the forward path and one to store the backward path. 334 * One is to store the forward path and one to store the backward path.
328 */ 335 */
329 ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; 336 ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
330 if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) { 337 if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {
331 338
416 return ret; 423 return ret;
417 else if (c == ' ') 424 else if (c == ' ')
418 ret += 1; 425 ret += 1;
419 else if (c == '\t') 426 else if (c == '\t')
420 ret += 8 - ret % 8; 427 ret += 8 - ret % 8;
421 // ignore other whitespace characters 428 /* ignore other whitespace characters */
422 429
423 if (ret >= MAX_INDENT) 430 if (ret >= MAX_INDENT)
424 return MAX_INDENT; 431 return MAX_INDENT;
425 } 432 }
426 433
427 // The line contains only whitespace. 434 /* The line contains only whitespace. */
428 return -1; 435 return -1;
429 } 436 }
430 437
431 /* 438 /*
432 * If more than this number of consecutive blank rows are found, just return 439 * If more than this number of consecutive blank rows are found, just return
433 * this value. This avoids requiring O(N^2) work for pathological cases, and 440 * this value. This avoids requiring O(N^2) work for pathological cases, and
434 * also ensures that the output of score_split fits in an int. 441 * also ensures that the output of score_split fits in an int.
435 */ 442 */
436 #define MAX_BLANKS 20 443 #define MAX_BLANKS 20
437 444
438 // Characteristics measured about a hypothetical split position. 445 /* Characteristics measured about a hypothetical split position. */
439 struct split_measurement { 446 struct split_measurement {
440 /* 447 /*
441 * Is the split at the end of the file (aside from any blank lines)? 448 * Is the split at the end of the file (aside from any blank lines)?
442 */ 449 */
443 int end_of_file; 450 int end_of_file;
444 451
445 /* 452 /*
446 * How much is the line immediately following the split indented (or -1 if 453 * How much is the line immediately following the split indented (or -1
447 * the line is blank): 454 * if the line is blank):
448 */ 455 */
449 int indent; 456 int indent;
450 457
451 /* 458 /*
452 * How many consecutive lines above the split are blank? 459 * How many consecutive lines above the split are blank?
453 */ 460 */
454 int pre_blank; 461 int pre_blank;
455 462
456 /* 463 /*
457 * How much is the nearest non-blank line above the split indented (or -1 464 * How much is the nearest non-blank line above the split indented (or
458 * if there is no such line)? 465 * -1 if there is no such line)?
459 */ 466 */
460 int pre_indent; 467 int pre_indent;
461 468
462 /* 469 /*
463 * How many lines after the line following the split are blank? 470 * How many lines after the line following the split are blank?
470 */ 477 */
471 int post_indent; 478 int post_indent;
472 }; 479 };
473 480
474 struct split_score { 481 struct split_score {
475 // The effective indent of this split (smaller is preferred). 482 /* The effective indent of this split (smaller is preferred). */
476 int effective_indent; 483 int effective_indent;
477 484
478 // Penalty for this split (smaller is preferred). 485 /* Penalty for this split (smaller is preferred). */
479 int penalty; 486 int penalty;
480 }; 487 };
481 488
482 /* 489 /*
483 * Fill m with information about a hypothetical split of xdf above line split. 490 * Fill m with information about a hypothetical split of xdf above line split.
532 * In practice, these numbers are chosen to be large enough that they can be 539 * In practice, these numbers are chosen to be large enough that they can be
533 * adjusted relative to each other with sufficient precision despite using 540 * adjusted relative to each other with sufficient precision despite using
534 * integer math. 541 * integer math.
535 */ 542 */
536 543
537 // Penalty if there are no non-blank lines before the split 544 /* Penalty if there are no non-blank lines before the split */
538 #define START_OF_FILE_PENALTY 1 545 #define START_OF_FILE_PENALTY 1
539 546
540 // Penalty if there are no non-blank lines after the split 547 /* Penalty if there are no non-blank lines after the split */
541 #define END_OF_FILE_PENALTY 21 548 #define END_OF_FILE_PENALTY 21
542 549
543 // Multiplier for the number of blank lines around the split 550 /* Multiplier for the number of blank lines around the split */
544 #define TOTAL_BLANK_WEIGHT (-30) 551 #define TOTAL_BLANK_WEIGHT (-30)
545 552
546 // Multiplier for the number of blank lines after the split 553 /* Multiplier for the number of blank lines after the split */
547 #define POST_BLANK_WEIGHT 6 554 #define POST_BLANK_WEIGHT 6
548 555
549 /* 556 /*
550 * Penalties applied if the line is indented more than its predecessor 557 * Penalties applied if the line is indented more than its predecessor
551 */ 558 */
579 */ 586 */
580 #define INDENT_HEURISTIC_MAX_SLIDING 100 587 #define INDENT_HEURISTIC_MAX_SLIDING 100
581 588
582 /* 589 /*
583 * Compute a badness score for the hypothetical split whose measurements are 590 * Compute a badness score for the hypothetical split whose measurements are
584 * stored in m. The weight factors were determined empirically using the tools and 591 * stored in m. The weight factors were determined empirically using the tools
585 * corpus described in 592 * and corpus described in
586 * 593 *
587 * https://github.com/mhagger/diff-slider-tools 594 * https://github.com/mhagger/diff-slider-tools
588 * 595 *
589 * Also see that project if you want to improve the weights based on, for example, 596 * Also see that project if you want to improve the weights based on, for
590 * a larger or more diverse corpus. 597 * example, a larger or more diverse corpus.
591 */ 598 */
592 static void score_add_split(const struct split_measurement *m, struct split_score *s) 599 static void score_add_split(const struct split_measurement *m, struct split_score *s)
593 { 600 {
594 /* 601 /*
595 * A place to accumulate penalty factors (positive makes this index more 602 * A place to accumulate penalty factors (positive makes this index more
608 * including the line immediately after the split: 615 * including the line immediately after the split:
609 */ 616 */
610 post_blank = (m->indent == -1) ? 1 + m->post_blank : 0; 617 post_blank = (m->indent == -1) ? 1 + m->post_blank : 0;
611 total_blank = m->pre_blank + post_blank; 618 total_blank = m->pre_blank + post_blank;
612 619
613 // Penalties based on nearby blank lines: 620 /* Penalties based on nearby blank lines: */
614 s->penalty += TOTAL_BLANK_WEIGHT * total_blank; 621 s->penalty += TOTAL_BLANK_WEIGHT * total_blank;
615 s->penalty += POST_BLANK_WEIGHT * post_blank; 622 s->penalty += POST_BLANK_WEIGHT * post_blank;
616 623
617 if (m->indent != -1) 624 if (m->indent != -1)
618 indent = m->indent; 625 indent = m->indent;
619 else 626 else
620 indent = m->post_indent; 627 indent = m->post_indent;
621 628
622 any_blanks = (total_blank != 0); 629 any_blanks = (total_blank != 0);
623 630
624 // Note that the effective indent is -1 at the end of the file: 631 /* Note that the effective indent is -1 at the end of the file: */
625 s->effective_indent += indent; 632 s->effective_indent += indent;
626 633
627 if (indent == -1) { 634 if (indent == -1) {
628 // No additional adjustments needed. 635 /* No additional adjustments needed. */
629 } else if (m->pre_indent == -1) { 636 } else if (m->pre_indent == -1) {
630 // No additional adjustments needed. 637 /* No additional adjustments needed. */
631 } else if (indent > m->pre_indent) { 638 } else if (indent > m->pre_indent) {
632 /* 639 /*
633 * The line is indented more than its predecessor. 640 * The line is indented more than its predecessor.
634 */ 641 */
635 s->penalty += any_blanks ? 642 s->penalty += any_blanks ?
667 } 674 }
668 } 675 }
669 676
670 static int score_cmp(struct split_score *s1, struct split_score *s2) 677 static int score_cmp(struct split_score *s1, struct split_score *s2)
671 { 678 {
672 // -1 if s1.effective_indent < s2->effective_indent, etc. 679 /* -1 if s1.effective_indent < s2->effective_indent, etc. */
673 int cmp_indents = ((s1->effective_indent > s2->effective_indent) - 680 int cmp_indents = ((s1->effective_indent > s2->effective_indent) -
674 (s1->effective_indent < s2->effective_indent)); 681 (s1->effective_indent < s2->effective_indent));
675 682
676 return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty); 683 return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty);
677 } 684 }
807 814
808 group_init(xdf, &g); 815 group_init(xdf, &g);
809 group_init(xdfo, &go); 816 group_init(xdfo, &go);
810 817
811 while (1) { 818 while (1) {
812 // If the group is empty in the to-be-compacted file, skip it: 819 /*
820 * If the group is empty in the to-be-compacted file, skip it:
821 */
813 if (g.end == g.start) 822 if (g.end == g.start)
814 goto next; 823 goto next;
815 824
816 /* 825 /*
817 * Now shift the change up and then down as far as possible in 826 * Now shift the change up and then down as far as possible in
818 * each direction. If it bumps into any other changes, merge them. 827 * each direction. If it bumps into any other changes, merge
828 * them.
819 */ 829 */
820 do { 830 do {
821 groupsize = g.end - g.start; 831 groupsize = g.end - g.start;
822 832
823 /* 833 /*
826 * other file. -1 indicates that we haven't found such 836 * other file. -1 indicates that we haven't found such
827 * a match yet: 837 * a match yet:
828 */ 838 */
829 end_matching_other = -1; 839 end_matching_other = -1;
830 840
831 // Shift the group backward as much as possible: 841 /* Shift the group backward as much as possible: */
832 while (!group_slide_up(xdf, &g, flags)) 842 while (!group_slide_up(xdf, &g, flags))
833 if (group_previous(xdfo, &go)) 843 if (group_previous(xdfo, &go))
834 xdl_bug("group sync broken sliding up"); 844 xdl_bug("group sync broken sliding up");
835 845
836 /* 846 /*
840 earliest_end = g.end; 850 earliest_end = g.end;
841 851
842 if (go.end > go.start) 852 if (go.end > go.start)
843 end_matching_other = g.end; 853 end_matching_other = g.end;
844 854
845 // Now shift the group forward as far as possible: 855 /* Now shift the group forward as far as possible: */
846 while (1) { 856 while (1) {
847 if (group_slide_down(xdf, &g, flags)) 857 if (group_slide_down(xdf, &g, flags))
848 break; 858 break;
849 if (group_next(xdfo, &go)) 859 if (group_next(xdfo, &go))
850 xdl_bug("group sync broken sliding down"); 860 xdl_bug("group sync broken sliding down");
856 866
857 /* 867 /*
858 * If the group can be shifted, then we can possibly use this 868 * If the group can be shifted, then we can possibly use this
859 * freedom to produce a more intuitive diff. 869 * freedom to produce a more intuitive diff.
860 * 870 *
861 * The group is currently shifted as far down as possible, so the 871 * The group is currently shifted as far down as possible, so
862 * heuristics below only have to handle upwards shifts. 872 * the heuristics below only have to handle upwards shifts.
863 */ 873 */
864 874
865 if (g.end == earliest_end) { 875 if (g.end == earliest_end) {
866 // no shifting was possible 876 /* no shifting was possible */
867 } else if (end_matching_other != -1) { 877 } else if (end_matching_other != -1) {
868 /* 878 /*
869 * Move the possibly merged group of changes back to line 879 * Move the possibly merged group of changes back to
870 * up with the last group of changes from the other file 880 * line up with the last group of changes from the
871 * that it can align with. 881 * other file that it can align with.
872 */ 882 */
873 while (go.end == go.start) { 883 while (go.end == go.start) {
874 if (group_slide_up(xdf, &g, flags)) 884 if (group_slide_up(xdf, &g, flags))
875 xdl_bug("match disappeared"); 885 xdl_bug("match disappeared");
876 if (group_previous(xdfo, &go)) 886 if (group_previous(xdfo, &go))
877 xdl_bug("group sync broken sliding to match"); 887 xdl_bug("group sync broken sliding to match");
878 } 888 }
879 } else if (flags & XDF_INDENT_HEURISTIC) { 889 } else if (flags & XDF_INDENT_HEURISTIC) {
880 /* 890 /*
881 * Indent heuristic: a group of pure add/delete lines 891 * Indent heuristic: a group of pure add/delete lines
882 * implies two splits, one between the end of the "before" 892 * implies two splits, one between the end of the
883 * context and the start of the group, and another between 893 * "before" context and the start of the group, and
884 * the end of the group and the beginning of the "after" 894 * another between the end of the group and the
885 * context. Some splits are aesthetically better and some 895 * beginning of the "after" context. Some splits are
886 * are worse. We compute a badness "score" for each split, 896 * aesthetically better and some are worse. We compute
887 * and add the scores for the two splits to define a 897 * a badness "score" for each split, and add the scores
888 * "score" for each position that the group can be shifted 898 * for the two splits to define a "score" for each
889 * to. Then we pick the shift with the lowest score. 899 * position that the group can be shifted to. Then we
900 * pick the shift with the lowest score.
890 */ 901 */
891 long shift, best_shift = -1; 902 long shift, best_shift = -1;
892 struct split_score best_score; 903 struct split_score best_score;
893 904
894 shift = earliest_end; 905 shift = earliest_end;
919 xdl_bug("group sync broken sliding to blank line"); 930 xdl_bug("group sync broken sliding to blank line");
920 } 931 }
921 } 932 }
922 933
923 next: 934 next:
924 // Move past the just-processed group: 935 /* Move past the just-processed group: */
925 if (group_next(xdf, &g)) 936 if (group_next(xdf, &g))
926 break; 937 break;
927 if (group_next(xdfo, &go)) 938 if (group_next(xdfo, &go))
928 xdl_bug("group sync broken moving to next group"); 939 xdl_bug("group sync broken moving to next group");
929 } 940 }
985 return -1; 996 return -1;
986 } 997 }
987 return 0; 998 return 0;
988 } 999 }
989 1000
990 static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) 1001 static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)
991 { 1002 {
992 xdchange_t *xch; 1003 xdchange_t *xch;
993 1004
994 for (xch = xscr; xch; xch = xch->next) { 1005 for (xch = xscr; xch; xch = xch->next) {
995 int ignore = 1; 1006 int ignore = 1;
1005 ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); 1016 ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
1006 1017
1007 xch->ignore = ignore; 1018 xch->ignore = ignore;
1008 } 1019 }
1009 } 1020 }
1021
1022 #if 0 // unused by Vim
1023 static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) {
1024 regmatch_t regmatch;
1025 int i;
1026
1027 for (i = 0; i < xpp->ignore_regex_nr; i++)
1028 if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1,
1029 &regmatch, 0))
1030 return 1;
1031
1032 return 0;
1033 }
1034
1035 static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,
1036 xpparam_t const *xpp)
1037 {
1038 xdchange_t *xch;
1039
1040 for (xch = xscr; xch; xch = xch->next) {
1041 xrecord_t **rec;
1042 int ignore = 1;
1043 long i;
1044
1045 /*
1046 * Do not override --ignore-blank-lines.
1047 */
1048 if (xch->ignore)
1049 continue;
1050
1051 rec = &xe->xdf1.recs[xch->i1];
1052 for (i = 0; i < xch->chg1 && ignore; i++)
1053 ignore = record_matches_regex(rec[i], xpp);
1054
1055 rec = &xe->xdf2.recs[xch->i2];
1056 for (i = 0; i < xch->chg2 && ignore; i++)
1057 ignore = record_matches_regex(rec[i], xpp);
1058
1059 xch->ignore = ignore;
1060 }
1061 }
1062 #endif
1010 1063
1011 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, 1064 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
1012 xdemitconf_t const *xecfg, xdemitcb_t *ecb) { 1065 xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
1013 xdchange_t *xscr; 1066 xdchange_t *xscr;
1014 xdfenv_t xe; 1067 xdfenv_t xe;
1025 xdl_free_env(&xe); 1078 xdl_free_env(&xe);
1026 return -1; 1079 return -1;
1027 } 1080 }
1028 if (xscr) { 1081 if (xscr) {
1029 if (xpp->flags & XDF_IGNORE_BLANK_LINES) 1082 if (xpp->flags & XDF_IGNORE_BLANK_LINES)
1030 xdl_mark_ignorable(xscr, &xe, xpp->flags); 1083 xdl_mark_ignorable_lines(xscr, &xe, xpp->flags);
1084
1085 #if 0
1086 if (xpp->ignore_regex)
1087 xdl_mark_ignorable_regex(xscr, &xe, xpp);
1088 #endif
1031 1089
1032 if (ef(&xe, xscr, ecb, xecfg) < 0) { 1090 if (ef(&xe, xscr, ecb, xecfg) < 0) {
1033 1091
1034 xdl_free_script(xscr); 1092 xdl_free_script(xscr);
1035 xdl_free_env(&xe); 1093 xdl_free_env(&xe);