LCOV - code coverage report
Current view: top level - libs/xdiff/src - xdiff.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 54.4 % 712 387
Test Date: 2026-01-12 05:34:38 Functions: 87.5 % 40 35

            Line data    Source code
       1              : #include "xdiff.h"
       2              : 
       3              : typedef struct s_xdpsplit {
       4              :         long i1,i2;
       5              :         int min_lo,min_hi;
       6              : } xdpsplit_t;
       7              : 
       8              : /**
       9              :  * @brief Splits difference algorithm search space recursively
      10              :  *
      11              :  * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
      12              :  * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
      13              :  * the forward diagonal starting from (off1, off2) and the backward diagonal
      14              :  * starting from (lim1, lim2). If the K values on the same diagonal crosses
      15              :  * returns the furthest point of reach. We might end up having to expensive
      16              :  * cases using this algorithm is full, so a little bit of heuristic is needed
      17              :  * to cut the search and to return a suboptimal point.
      18              :  */
      19            0 : static long xdl_split(
      20              :         unsigned long const *ha1,
      21              :         long                off1,
      22              :         long                lim1,
      23              :         unsigned long const *ha2,
      24              :         long                off2,
      25              :         long                lim2,
      26              :         long                *kvdf,
      27              :         long                *kvdb,
      28              :         int                 need_min,
      29              :         xdpsplit_t          *spl,
      30              :         xdalgoenv_t         *xenv)
      31              : {
      32            0 :         long dmin = off1 - lim2,dmax = lim1 - off2;
      33            0 :         long fmid = off1 - off2,bmid = lim1 - lim2;
      34            0 :         long odd = (fmid - bmid) & 1;
      35            0 :         long fmin = fmid,fmax = fmid;
      36            0 :         long bmin = bmid,bmax = bmid;
      37              :         long ec,d,i1,i2,prev1,best,dd,v,k;
      38              : 
      39              :         /*
      40              :          * Set initial diagonal values for both forward and backward path.
      41              :          */
      42            0 :         kvdf[fmid] = off1;
      43            0 :         kvdb[bmid] = lim1;
      44              : 
      45            0 :         for(ec = 1;; ec++)
      46            0 :         {
      47            0 :                 int got_snake = 0;
      48              : 
      49              :                 /*
      50              :                  * We need to extent the diagonal "domain" by one. If the next
      51              :                  * values exits the box boundaries we need to change it in the
      52              :                  * opposite direction because (max - min) must be a power of two.
      53              :                  * Also we initialize the external K value to -1 so that we can
      54              :                  * avoid extra conditions check inside the core loop.
      55              :                  */
      56            0 :                 if(fmin > dmin)
      57              :                 {
      58            0 :                         kvdf[--fmin - 1] = -1;
      59              :                 } else {
      60            0 :                         ++fmin;
      61              :                 }
      62              : 
      63            0 :                 if(fmax < dmax)
      64              :                 {
      65            0 :                         kvdf[++fmax + 1] = -1;
      66              :                 } else {
      67            0 :                         --fmax;
      68              :                 }
      69              : 
      70            0 :                 for(d = fmax; d >= fmin; d -= 2)
      71              :                 {
      72            0 :                         if(kvdf[d - 1] >= kvdf[d + 1])
      73              :                         {
      74            0 :                                 i1 = kvdf[d - 1] + 1;
      75              :                         } else {
      76            0 :                                 i1 = kvdf[d + 1];
      77              :                         }
      78            0 :                         prev1 = i1;
      79            0 :                         i2 = i1 - d;
      80              : 
      81            0 :                         for(; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++,i2++)
      82              :                         {
      83              :                                 ;
      84              :                         }
      85              : 
      86            0 :                         if(i1 - prev1 > xenv->snake_cnt)
      87              :                         {
      88            0 :                                 got_snake = 1;
      89              :                         }
      90            0 :                         kvdf[d] = i1;
      91              : 
      92            0 :                         if(odd && bmin <= d && d <= bmax && kvdb[d] <= i1)
      93              :                         {
      94            0 :                                 spl->i1 = i1;
      95            0 :                                 spl->i2 = i2;
      96            0 :                                 spl->min_lo = spl->min_hi = 1;
      97            0 :                                 return ec;
      98              :                         }
      99              :                 }
     100              : 
     101              :                 /*
     102              :                  * We need to extent the diagonal "domain" by one. If the next
     103              :                  * values exits the box boundaries we need to change it in the
     104              :                  * opposite direction because (max - min) must be a power of two.
     105              :                  * Also we initialize the external K value to -1 so that we can
     106              :                  * avoid extra conditions check inside the core loop.
     107              :                  */
     108            0 :                 if(bmin > dmin)
     109              :                 {
     110            0 :                         kvdb[--bmin - 1] = XDL_LINE_MAX;
     111              :                 } else {
     112            0 :                         ++bmin;
     113              :                 }
     114              : 
     115            0 :                 if(bmax < dmax)
     116              :                 {
     117            0 :                         kvdb[++bmax + 1] = XDL_LINE_MAX;
     118              :                 } else {
     119            0 :                         --bmax;
     120              :                 }
     121              : 
     122            0 :                 for(d = bmax; d >= bmin; d -= 2)
     123              :                 {
     124            0 :                         if(kvdb[d - 1] < kvdb[d + 1])
     125              :                         {
     126            0 :                                 i1 = kvdb[d - 1];
     127              :                         } else {
     128            0 :                                 i1 = kvdb[d + 1] - 1;
     129              :                         }
     130            0 :                         prev1 = i1;
     131            0 :                         i2 = i1 - d;
     132              : 
     133            0 :                         for(; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1];
     134            0 :                                 i1--,i2--)
     135              :                         {
     136              :                                 ;
     137              :                         }
     138              : 
     139            0 :                         if(prev1 - i1 > xenv->snake_cnt)
     140              :                         {
     141            0 :                                 got_snake = 1;
     142              :                         }
     143            0 :                         kvdb[d] = i1;
     144              : 
     145            0 :                         if(!odd && fmin <= d && d <= fmax && i1 <= kvdf[d])
     146              :                         {
     147            0 :                                 spl->i1 = i1;
     148            0 :                                 spl->i2 = i2;
     149            0 :                                 spl->min_lo = spl->min_hi = 1;
     150            0 :                                 return ec;
     151              :                         }
     152              :                 }
     153              : 
     154            0 :                 if(need_min)
     155              :                 {
     156            0 :                         continue;
     157              :                 }
     158              : 
     159              :                 /*
     160              :                  * If the edit cost is above the heuristic trigger and if
     161              :                  * we got a good snake, we sample current diagonals to see
     162              :                  * if some of the, have reached an "interesting" path. Our
     163              :                  * measure is a function of the distance from the diagonal
     164              :                  * corner (i1 + i2) penalized with the distance from the
     165              :                  * mid diagonal itself. If this value is above the current
     166              :                  * edit cost times a magic factor (XDL_K_HEUR) we consider
     167              :                  * it interesting.
     168              :                  */
     169            0 :                 if(got_snake && ec > xenv->heur_min)
     170              :                 {
     171            0 :                         for(best = 0,d = fmax; d >= fmin; d -= 2)
     172              :                         {
     173            0 :                                 dd = d > fmid ? d - fmid : fmid - d;
     174            0 :                                 i1 = kvdf[d];
     175            0 :                                 i2 = i1 - d;
     176            0 :                                 v = (i1 - off1) + (i2 - off2) - dd;
     177              : 
     178            0 :                                 if(v > XDL_K_HEUR * ec && v > best &&
     179            0 :                                         off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&
     180            0 :                                         off2 + xenv->snake_cnt <= i2 && i2 < lim2)
     181              :                                 {
     182            0 :                                         for(k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)
     183              :                                         {
     184            0 :                                                 if(k == xenv->snake_cnt)
     185              :                                                 {
     186            0 :                                                         best = v;
     187            0 :                                                         spl->i1 = i1;
     188            0 :                                                         spl->i2 = i2;
     189            0 :                                                         break;
     190              :                                                 }
     191              :                                         }
     192              :                                 }
     193              :                         }
     194              : 
     195            0 :                         if(best > 0)
     196              :                         {
     197            0 :                                 spl->min_lo = 1;
     198            0 :                                 spl->min_hi = 0;
     199            0 :                                 return ec;
     200              :                         }
     201              : 
     202            0 :                         for(best = 0,d = bmax; d >= bmin; d -= 2)
     203              :                         {
     204            0 :                                 dd = d > bmid ? d - bmid : bmid - d;
     205            0 :                                 i1 = kvdb[d];
     206            0 :                                 i2 = i1 - d;
     207            0 :                                 v = (lim1 - i1) + (lim2 - i2) - dd;
     208              : 
     209            0 :                                 if(v > XDL_K_HEUR * ec && v > best && off1 < i1 &&
     210            0 :                                         i1 <= lim1 - xenv->snake_cnt && off2 < i2 &&
     211            0 :                                         i2 <= lim2 - xenv->snake_cnt)
     212              :                                 {
     213            0 :                                         for(k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)
     214              :                                         {
     215            0 :                                                 if(k == xenv->snake_cnt - 1)
     216              :                                                 {
     217            0 :                                                         best = v;
     218            0 :                                                         spl->i1 = i1;
     219            0 :                                                         spl->i2 = i2;
     220            0 :                                                         break;
     221              :                                                 }
     222              :                                         }
     223              :                                 }
     224              :                         }
     225              : 
     226            0 :                         if(best > 0)
     227              :                         {
     228            0 :                                 spl->min_lo = 0;
     229            0 :                                 spl->min_hi = 1;
     230            0 :                                 return ec;
     231              :                         }
     232              :                 }
     233              : 
     234              :                 /*
     235              :                  * Enough is enough. We spent too much time here and now we collect
     236              :                  * the furthest reaching path using the (i1 + i2) measure.
     237              :                  */
     238            0 :                 if(ec >= xenv->mxcost)
     239              :                 {
     240            0 :                         long fbest,fbest1 = 0,bbest,bbest1 = 0;
     241              : 
     242            0 :                         fbest = -1;
     243              : 
     244            0 :                         for(d = fmax; d >= fmin; d -= 2)
     245              :                         {
     246            0 :                                 i1 = XDL_MIN(kvdf[d],lim1);
     247            0 :                                 i2 = i1 - d;
     248              : 
     249            0 :                                 if(lim2 < i2)
     250              :                                 {
     251            0 :                                         i1 = lim2 + d,i2 = lim2;
     252              :                                 }
     253              : 
     254            0 :                                 if(fbest < i1 + i2)
     255              :                                 {
     256            0 :                                         fbest = i1 + i2;
     257            0 :                                         fbest1 = i1;
     258              :                                 }
     259              :                         }
     260              : 
     261            0 :                         bbest = XDL_LINE_MAX;
     262              : 
     263            0 :                         for(d = bmax; d >= bmin; d -= 2)
     264              :                         {
     265            0 :                                 i1 = XDL_MAX(off1,kvdb[d]);
     266            0 :                                 i2 = i1 - d;
     267              : 
     268            0 :                                 if(i2 < off2)
     269              :                                 {
     270            0 :                                         i1 = off2 + d,i2 = off2;
     271              :                                 }
     272              : 
     273            0 :                                 if(i1 + i2 < bbest)
     274              :                                 {
     275            0 :                                         bbest = i1 + i2;
     276            0 :                                         bbest1 = i1;
     277              :                                 }
     278              :                         }
     279              : 
     280            0 :                         if((lim1 + lim2) - bbest < fbest - (off1 + off2))
     281              :                         {
     282            0 :                                 spl->i1 = fbest1;
     283            0 :                                 spl->i2 = fbest - fbest1;
     284            0 :                                 spl->min_lo = 1;
     285            0 :                                 spl->min_hi = 0;
     286              :                         } else {
     287            0 :                                 spl->i1 = bbest1;
     288            0 :                                 spl->i2 = bbest - bbest1;
     289            0 :                                 spl->min_lo = 0;
     290            0 :                                 spl->min_hi = 1;
     291              :                         }
     292            0 :                         return ec;
     293              :                 }
     294              :         }
     295              : 
     296              :         return -1;
     297              : }
     298              : 
     299              : /**
     300              :  * @brief Compares records between two sequences to find differences
     301              :  *
     302              :  * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling
     303              :  * the box splitting function. Note that the real job (marking changed lines)
     304              :  * is done in the two boundary reaching checks.
     305              :  */
     306            8 : int xdl_recs_cmp(
     307              :         diffdata_t  *dd1,
     308              :         long        off1,
     309              :         long        lim1,
     310              :         diffdata_t  *dd2,
     311              :         long        off2,
     312              :         long        lim2,
     313              :         long        *kvdf,
     314              :         long        *kvdb,
     315              :         int         need_min,
     316              :         xdalgoenv_t *xenv)
     317              : {
     318            8 :         unsigned long const *ha1 = dd1->ha,*ha2 = dd2->ha;
     319              : 
     320              :         /*
     321              :          * Shrink the box by walking through each diagonal snake (SW and NE).
     322              :          */
     323          364 :         for(; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++,off2++)
     324              :         {
     325              :                 ;
     326              :         }
     327              : 
     328            8 :         for(; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1];
     329            0 :                 lim1--,lim2--)
     330              :         {
     331              :                 ;
     332              :         }
     333              : 
     334              :         /*
     335              :          * If one dimension is empty, then all records on the other one must
     336              :          * be obviously changed.
     337              :          */
     338            8 :         if(off1 == lim1)
     339              :         {
     340            8 :                 char *rchg2 = dd2->rchg;
     341            8 :                 long *rindex2 = dd2->rindex;
     342              : 
     343           14 :                 for(; off2 < lim2; off2++)
     344              :                 {
     345            6 :                         rchg2[rindex2[off2]] = 1;
     346              :                 }
     347            0 :         } else if(off2 == lim2){
     348            0 :                 char *rchg1 = dd1->rchg;
     349            0 :                 long *rindex1 = dd1->rindex;
     350              : 
     351            0 :                 for(; off1 < lim1; off1++)
     352              :                 {
     353            0 :                         rchg1[rindex1[off1]] = 1;
     354              :                 }
     355              :         } else {
     356              :                 xdpsplit_t spl;
     357              : 
     358              :                 /*
     359              :                  * Divide ...
     360              :                  */
     361            0 :                 if(xdl_split(
     362              :                         ha1,
     363              :                         off1,
     364              :                         lim1,
     365              :                         ha2,
     366              :                         off2,
     367              :                         lim2,
     368              :                         kvdf,
     369              :                         kvdb,
     370              :                         need_min,
     371              :                         &spl,
     372              :                         xenv) < 0)
     373              :                 {
     374            0 :                         return -1;
     375              :                 }
     376              : 
     377              :                 /*
     378              :                  * ... et Impera.
     379              :                  */
     380            0 :                 if(xdl_recs_cmp(
     381              :                         dd1,
     382              :                         off1,
     383              :                         spl.i1,
     384              :                         dd2,
     385              :                         off2,
     386              :                         spl.i2,
     387              :                         kvdf,
     388              :                         kvdb,
     389              :                         spl.min_lo,
     390            0 :                         xenv) < 0 ||
     391            0 :                         xdl_recs_cmp(
     392              :                         dd1,
     393              :                         spl.i1,
     394              :                         lim1,
     395              :                         dd2,
     396              :                         spl.i2,
     397              :                         lim2,
     398              :                         kvdf,
     399              :                         kvdb,
     400              :                         spl.min_hi,
     401              :                         xenv) < 0)
     402              :                 {
     403            0 :                         return -1;
     404              :                 }
     405              :         }
     406              : 
     407            8 :         return 0;
     408              : }
     409              : 
     410              : /**
     411              :  * @brief Performs the main difference algorithm between two files
     412              :  *
     413              :  * @param mf1 First mmfile structure
     414              :  * @param mf2 Second mmfile structure
     415              :  * @param xpp Algorithm parameters
     416              :  * @param xe Environment structure to store results
     417              :  * @return int Returns 0 on success, -1 on error
     418              :  */
     419            8 : int xdl_do_diff(
     420              :         mmfile_t        *mf1,
     421              :         mmfile_t        *mf2,
     422              :         xpparam_t const *xpp,
     423              :         xdfenv_t        *xe)
     424              : {
     425              :         long ndiags;
     426              :         long *kvd,*kvdf,*kvdb;
     427              :         xdalgoenv_t xenv;
     428              :         diffdata_t dd1,dd2;
     429              : 
     430            8 :         if(xdl_prepare_env(mf1,mf2,xe) < 0)
     431              :         {
     432            0 :                 return -1;
     433              :         }
     434              : 
     435              :         /*
     436              :          * Allocate and setup K vectors to be used by the differential algorithm.
     437              :          * One is to store the forward path and one to store the backward path.
     438              :          */
     439            8 :         ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
     440              : 
     441            8 :         if(!(kvd = (long *)malloc((2 * ndiags + 2) * sizeof(long))))
     442              :         {
     443              : 
     444            0 :                 xdl_free_env(xe);
     445            0 :                 return -1;
     446              :         }
     447            8 :         kvdf = kvd;
     448            8 :         kvdb = kvdf + ndiags;
     449            8 :         kvdf += xe->xdf2.nreff + 1;
     450            8 :         kvdb += xe->xdf2.nreff + 1;
     451              : 
     452            8 :         xenv.mxcost = xdl_bogosqrt(ndiags);
     453              : 
     454            8 :         if(xenv.mxcost < XDL_MAX_COST_MIN)
     455              :         {
     456            8 :                 xenv.mxcost = XDL_MAX_COST_MIN;
     457              :         }
     458            8 :         xenv.snake_cnt = XDL_SNAKE_CNT;
     459            8 :         xenv.heur_min = XDL_HEUR_MIN_COST;
     460              : 
     461            8 :         dd1.nrec = xe->xdf1.nreff;
     462            8 :         dd1.ha = xe->xdf1.ha;
     463            8 :         dd1.rchg = xe->xdf1.rchg;
     464            8 :         dd1.rindex = xe->xdf1.rindex;
     465            8 :         dd2.nrec = xe->xdf2.nreff;
     466            8 :         dd2.ha = xe->xdf2.ha;
     467            8 :         dd2.rchg = xe->xdf2.rchg;
     468            8 :         dd2.rindex = xe->xdf2.rindex;
     469              : 
     470            8 :         if(xdl_recs_cmp(
     471              :                 &dd1,
     472              :                 0,
     473              :                 dd1.nrec,
     474              :                 &dd2,
     475              :                 0,
     476              :                 dd2.nrec,
     477              :                 kvdf,
     478              :                 kvdb,
     479            8 :                 (xpp->flags & XDF_NEED_MINIMAL) != 0,
     480              :                 &xenv) < 0)
     481              :         {
     482              : 
     483            0 :                 free(kvd);
     484            0 :                 xdl_free_env(xe);
     485            0 :                 return -1;
     486              :         }
     487              : 
     488            8 :         free(kvd);
     489              : 
     490            8 :         return 0;
     491              : }
     492              : 
     493              : /**
     494              :  * @brief Adds a change record to the change script
     495              :  *
     496              :  * @param xscr Current change script
     497              :  * @param i1 Line number in first file
     498              :  * @param i2 Line number in second file
     499              :  * @param chg1 Number of lines changed in first file
     500              :  * @param chg2 Number of lines changed in second file
     501              :  * @return xdchange_t* Returns pointer to new change record or NULL on error
     502              :  */
     503              : static xdchange_t *
     504           36 : xdl_add_change(
     505              :         xdchange_t *xscr,
     506              :         long       i1,
     507              :         long       i2,
     508              :         long       chg1,
     509              :         long       chg2)
     510              : {
     511              :         xdchange_t *xch;
     512              : 
     513           36 :         if(!(xch = (xdchange_t *)malloc(sizeof(xdchange_t))))
     514              :         {
     515            0 :                 return NULL;
     516              :         }
     517              : 
     518           36 :         xch->next = xscr;
     519           36 :         xch->i1 = i1;
     520           36 :         xch->i2 = i2;
     521           36 :         xch->chg1 = chg1;
     522           36 :         xch->chg2 = chg2;
     523              : 
     524           36 :         return xch;
     525              : }
     526              : 
     527              : /**
     528              :  * @brief Compacts and optimizes change records for better output
     529              :  *
     530              :  * @param xdf Main file diff structure
     531              :  * @param xdfo Other file diff structure for comparison
     532              :  * @return int Returns 0 on success, -1 on error
     533              :  */
     534           16 : static int xdl_change_compact(
     535              :         xdfile_t *xdf,
     536              :         xdfile_t *xdfo)
     537              : {
     538           16 :         long ix,ixo,ixref,grpsiz,nrec = xdf->nrec;
     539           16 :         char *rchg = xdf->rchg,*rchgo = xdfo->rchg;
     540           16 :         xrecord_t **recs = xdf->recs;
     541              : 
     542              :         /*
     543              :          * This is the same of what GNU diff does. Move back and forward
     544              :          * change groups for a consistent and pretty diff output. This also
     545              :          * helps in finding joineable change groups and reduce the diff size.
     546              :          */
     547           16 :         for(ix = ixo = 0;;)
     548           56 :         {
     549              :                 /*
     550              :                  * Find the first changed line in the to-be-compacted file.
     551              :                  * We need to keep track of both indexes, so if we find a
     552              :                  * changed lines group on the other file, while scanning the
     553              :                  * to-be-compacted file, we need to skip it properly. Note
     554              :                  * that loops that are testing for changed lines on rchg* do
     555              :                  * not need index bounding since the array is prepared with
     556              :                  * a zero at position -1 and N.
     557              :                  */
     558          932 :                 for(; ix < nrec && !rchg[ix]; ix++)
     559              :                 {
     560          878 :                         while(rchgo[ixo++])
     561              :                         {
     562              :                                 ;
     563              :                         }
     564              :                 }
     565              : 
     566           72 :                 if(ix == nrec)
     567              :                 {
     568           16 :                         break;
     569              :                 }
     570              : 
     571              :                 /*
     572              :                  * Record the start of a changed-group in the to-be-compacted file
     573              :                  * and find the end of it, on both to-be-compacted and other file
     574              :                  * indexes (ix and ixo).
     575              :                  */
     576           56 :                 long ixs = ix;
     577              : 
     578          114 :                 for(ix++; rchg[ix]; ix++)
     579              :                 {
     580              :                         ;
     581              :                 }
     582              : 
     583          152 :                 for(; rchgo[ixo]; ixo++)
     584              :                 {
     585              :                         ;
     586              :                 }
     587              : 
     588              :                 do
     589              :                 {
     590           56 :                         grpsiz = ix - ixs;
     591              : 
     592              :                         /*
     593              :                          * If the line before the current change group, is equal to
     594              :                          * the last line of the current change group, shift backward
     595              :                          * the group.
     596              :                          */
     597           56 :                         while(ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
     598            0 :                                 XDL_RECMATCH(recs[ixs - 1],recs[ix - 1]))
     599              :                         {
     600            0 :                                 rchg[--ixs] = 1;
     601            0 :                                 rchg[--ix] = 0;
     602              : 
     603              :                                 /*
     604              :                                  * This change might have joined two change groups,
     605              :                                  * so we try to take this scenario in account by moving
     606              :                                  * the start index accordingly (and so the other-file
     607              :                                  * end-of-group index).
     608              :                                  */
     609            0 :                                 for(; rchg[ixs - 1]; ixs--)
     610              :                                 {
     611              :                                         ;
     612              :                                 }
     613              : 
     614            0 :                                 while(rchgo[--ixo])
     615              :                                 {
     616              :                                         ;
     617              :                                 }
     618              :                         }
     619              : 
     620              :                         /*
     621              :                          * Record the end-of-group position in case we are matched
     622              :                          * with a group of changes in the other file (that is, the
     623              :                          * change record before the enf-of-group index in the other
     624              :                          * file is set).
     625              :                          */
     626           56 :                         ixref = rchgo[ixo - 1] ? ix : nrec;
     627              : 
     628              :                         /*
     629              :                          * If the first line of the current change group, is equal to
     630              :                          * the line next of the current change group, shift forward
     631              :                          * the group.
     632              :                          */
     633           56 :                         while(ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
     634            0 :                                 XDL_RECMATCH(recs[ixs],recs[ix]))
     635              :                         {
     636            0 :                                 rchg[ixs++] = 0;
     637            0 :                                 rchg[ix++] = 1;
     638              : 
     639              :                                 /*
     640              :                                  * This change might have joined two change groups,
     641              :                                  * so we try to take this scenario in account by moving
     642              :                                  * the start index accordingly (and so the other-file
     643              :                                  * end-of-group index). Keep tracking the reference
     644              :                                  * index in case we are shifting together with a
     645              :                                  * corresponding group of changes in the other file.
     646              :                                  */
     647            0 :                                 for(; rchg[ix]; ix++)
     648              :                                 {
     649              :                                         ;
     650              :                                 }
     651              : 
     652            0 :                                 while(rchgo[++ixo])
     653              :                                 {
     654            0 :                                         ixref = ix;
     655              :                                 }
     656              :                         }
     657           56 :                 } while(grpsiz != ix - ixs);
     658              : 
     659              :                 /*
     660              :                  * Try to move back the possibly merged group of changes, to match
     661              :                  * the recorded position in the other file.
     662              :                  */
     663           56 :                 while(ixref < ix)
     664              :                 {
     665            0 :                         rchg[--ixs] = 1;
     666            0 :                         rchg[--ix] = 0;
     667              : 
     668            0 :                         while(rchgo[--ixo])
     669              :                         {
     670              :                                 ;
     671              :                         }
     672              :                 }
     673              :         }
     674              : 
     675           16 :         return 0;
     676              : }
     677              : 
     678              : /**
     679              :  * @brief Builds the change script from comparison results
     680              :  *
     681              :  * @param xe Diff environment containing comparison data
     682              :  * @param xscr Pointer to receive the generated change script
     683              :  * @return int Returns 0 on success, -1 on error
     684              :  */
     685            8 : int xdl_build_script(
     686              :         xdfenv_t   *xe,
     687              :         xdchange_t **xscr)
     688              : {
     689            8 :         xdchange_t *cscr = NULL,*xch;
     690            8 :         char *rchg1 = xe->xdf1.rchg,*rchg2 = xe->xdf2.rchg;
     691              :         long i1,i2,l1,l2;
     692              : 
     693              :         /*
     694              :          * Trivial. Collects "groups" of changes and creates an edit script.
     695              :          */
     696          446 :         for(i1 = xe->xdf1.nrec,i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--,i2--)
     697              :         {
     698          438 :                 if(rchg1[i1 - 1] || rchg2[i2 - 1])
     699              :                 {
     700           88 :                         for(l1 = i1; rchg1[i1 - 1]; i1--)
     701              :                         {
     702              :                                 ;
     703              :                         }
     704              : 
     705           98 :                         for(l2 = i2; rchg2[i2 - 1]; i2--)
     706              :                         {
     707              :                                 ;
     708              :                         }
     709              : 
     710           36 :                         if(!(xch = xdl_add_change(cscr,i1,i2,l1 - i1,l2 - i2)))
     711              :                         {
     712            0 :                                 xdl_free_script(cscr);
     713            0 :                                 return -1;
     714              :                         }
     715           36 :                         cscr = xch;
     716              :                 }
     717              :         }
     718              : 
     719            8 :         *xscr = cscr;
     720              : 
     721            8 :         return 0;
     722              : }
     723              : 
     724              : /**
     725              :  * @brief Frees memory used by a change script
     726              :  *
     727              :  * @param xscr Change script to free
     728              :  */
     729            8 : void xdl_free_script(xdchange_t *xscr)
     730              : {
     731              :         xdchange_t *xch;
     732              : 
     733           44 :         while((xch = xscr) != NULL)
     734              :         {
     735           36 :                 xscr = xscr->next;
     736           36 :                 free(xch);
     737              :         }
     738            8 : }
     739              : 
     740              : /**
     741              :  * @brief Main entry point for diff generation between two files
     742              :  *
     743              :  * @param mf1 First file
     744              :  * @param mf2 Second file
     745              :  * @param xpp Diff algorithm parameters
     746              :  * @param xecfg Output configuration
     747              :  * @param ecb Output callback functions
     748              :  * @return int Returns 0 on success, -1 on error
     749              :  */
     750            8 : int xdl_diff(
     751              :         mmfile_t           *mf1,
     752              :         mmfile_t           *mf2,
     753              :         xpparam_t const    *xpp,
     754              :         xdemitconf_t const *xecfg,
     755              :         xdemitcb_t         *ecb)
     756              : {
     757              :         xdchange_t *xscr;
     758              :         xdfenv_t xe;
     759              : 
     760            8 :         if(xdl_do_diff(mf1,mf2,xpp,&xe) < 0)
     761              :         {
     762            0 :                 return -1;
     763              :         }
     764              : 
     765           16 :         if(xdl_change_compact(&xe.xdf1,&xe.xdf2) < 0 ||
     766           16 :                 xdl_change_compact(&xe.xdf2,&xe.xdf1) < 0 ||
     767            8 :                 xdl_build_script(&xe,&xscr) < 0)
     768              :         {
     769              : 
     770            0 :                 xdl_free_env(&xe);
     771            0 :                 return -1;
     772              :         }
     773              : 
     774            8 :         if(xscr)
     775              :         {
     776            8 :                 if(xdl_emit_diff(&xe,xscr,ecb,xecfg) < 0)
     777              :                 {
     778              : 
     779            0 :                         xdl_free_script(xscr);
     780            0 :                         xdl_free_env(&xe);
     781            0 :                         return -1;
     782              :                 }
     783            8 :                 xdl_free_script(xscr);
     784              :         }
     785            8 :         xdl_free_env(&xe);
     786              : 
     787            8 :         return 0;
     788              : }
     789              : 
     790              : /**
     791              :  * @brief Wrapper for standard malloc function
     792              :  *
     793              :  * @param priv Private data pointer (unused)
     794              :  * @param size Number of bytes to allocate
     795              :  * @return void* Returns allocated memory pointer
     796              :  */
     797              : #if 0
     798              : void *wrap_malloc(
     799              :         void         *priv,
     800              :         unsigned int size)
     801              : {
     802              :         return malloc(size);
     803              : }
     804              : #endif
     805              : 
     806              : /**
     807              :  * @brief Wrapper for standard free function
     808              :  *
     809              :  * @param priv Private data pointer (unused)
     810              :  * @param ptr Pointer to memory to free
     811              :  */
     812              : #if 0
     813              : void wrap_free(
     814              :         void *priv,
     815              :         void *ptr)
     816              : {
     817              :         free(ptr);
     818              : }
     819              : #endif
     820              : 
     821              : /**
     822              :  * @brief Wrapper for standard realloc function
     823              :  *
     824              :  * @param priv Private data pointer (unused)
     825              :  * @param ptr Pointer to existing memory block
     826              :  * @param size New size in bytes
     827              :  * @return void* Returns reallocated memory pointer
     828              :  */
     829              : #if 0
     830              : void *wrap_realloc(
     831              :         void         *priv,
     832              :         void         *ptr,
     833              :         unsigned int size)
     834              : {
     835              : 
     836              :         return realloc(ptr,size);
     837              : }
     838              : #endif
     839              : 
     840              : /**
     841              :  * @brief Gets record data at specified index
     842              :  *
     843              :  * @param xdf Diff file structure
     844              :  * @param ri Record index
     845              :  * @param rec Pointer to receive record data
     846              :  * @return long Returns record size
     847              :  */
     848          114 : static long xdl_get_rec(
     849              :         xdfile_t   *xdf,
     850              :         long       ri,
     851              :         char const **rec)
     852              : {
     853              : 
     854          114 :         *rec = xdf->recs[ri]->ptr;
     855              : 
     856          114 :         return xdf->recs[ri]->size;
     857              : }
     858              : 
     859              : /**
     860              :  * @brief Emits a single record with prefix to output
     861              :  *
     862              :  * @param xdf Diff file structure
     863              :  * @param ri Record index
     864              :  * @param pre Prefix string to prepend
     865              :  * @param ecb Output callback
     866              :  * @return int Returns 0 on success, -1 on error
     867              :  */
     868              : static int
     869          114 : xdl_emit_record(
     870              :         xdfile_t   *xdf,
     871              :         long       ri,
     872              :         char const *pre,
     873              :         xdemitcb_t *ecb)
     874              : {
     875          114 :         long size,psize = strlen(pre);
     876              :         char const *rec;
     877              : 
     878          114 :         size = xdl_get_rec(xdf,ri,&rec);
     879              : 
     880          114 :         if(xdl_emit_diffrec(rec,size,pre,psize,ecb) < 0)
     881              :         {
     882            0 :                 return -1;
     883              :         }
     884              : 
     885          114 :         return 0;
     886              : }
     887              : 
     888              : /**
     889              :  * @brief Finds the latest change atom for inclusion in diff hunk
     890              :  *
     891              :  * @param xscr Current change script
     892              :  * @param xecfg Emit configuration
     893              :  * @return xdchange_t* Returns pointer to last change in hunk
     894              :  *
     895              :  * Starting at the passed change atom, find the latest change atom to be
     896              :  * included inside the differential hunk according to the specified
     897              :  * configuration.
     898              :  */
     899              : static xdchange_t *xdl_get_hunk(
     900              :         xdchange_t *,
     901              :         xdemitconf_t const *) __attribute__((pure));
     902              : 
     903           36 : static xdchange_t *xdl_get_hunk(
     904              :         xdchange_t         *xscr,
     905              :         xdemitconf_t const *xecfg)
     906              : {
     907              : 
     908              :         xdchange_t *xch,*xchp;
     909              : 
     910           36 :         for(xchp = xscr,xch = xscr->next; xch; xchp = xch,xch = xch->next)
     911              :         {
     912           28 :                 if(xch->i1 - (xchp->i1 + xchp->chg1) > 2 * xecfg->ctxlen)
     913              :                 {
     914           28 :                         break;
     915              :                 }
     916              :         }
     917              : 
     918           36 :         return xchp;
     919              : }
     920              : 
     921              : /**
     922              :  * @brief Emits the complete diff output
     923              :  *
     924              :  * @param xe Diff environment
     925              :  * @param xscr Change script
     926              :  * @param ecb Output callback
     927              :  * @param xecfg Emit configuration
     928              :  * @return int Returns 0 on success, -1 on error
     929              :  */
     930            8 : int xdl_emit_diff(
     931              :         xdfenv_t           *xe,
     932              :         xdchange_t         *xscr,
     933              :         xdemitcb_t         *ecb,
     934              :         xdemitconf_t const *xecfg)
     935              : {
     936              :         long s1,s2,e1,e2,lctx;
     937              :         xdchange_t *xch,*xche;
     938              : 
     939           44 :         for(xch = xche = xscr; xch; xch = xche->next)
     940              :         {
     941           36 :                 xche = xdl_get_hunk(xch,xecfg);
     942              : 
     943           36 :                 s1 = XDL_MAX(xch->i1 - xecfg->ctxlen,0);
     944           36 :                 s2 = XDL_MAX(xch->i2 - xecfg->ctxlen,0);
     945              : 
     946           36 :                 lctx = xecfg->ctxlen;
     947           36 :                 lctx = XDL_MIN(lctx,xe->xdf1.nrec - (xche->i1 + xche->chg1));
     948           36 :                 lctx = XDL_MIN(lctx,xe->xdf2.nrec - (xche->i2 + xche->chg2));
     949              : 
     950           36 :                 e1 = xche->i1 + xche->chg1 + lctx;
     951           36 :                 e2 = xche->i2 + xche->chg2 + lctx;
     952              : 
     953              :                 /*
     954              :                  * Emit current hunk header.
     955              :                  */
     956           36 :                 if(xecfg->str_meta && xdl_emit_hunk_hdr(s1 + 1,e1 - s1,s2 + 1,e2 - s2,ecb) < 0)
     957              :                 {
     958            0 :                         return -1;
     959              :                 }
     960              : 
     961              :                 /*
     962              :                  * Emit pre-context.
     963              :                  */
     964           36 :                 for(; s1 < xch->i1; s1++)
     965              :                 {
     966            0 :                         if(xdl_emit_record(&xe->xdf1,s1," ",ecb) < 0)
     967              :                         {
     968            0 :                                 return -1;
     969              :                         }
     970              :                 }
     971              : 
     972           36 :                 for(s1 = xch->i1,s2 = xch->i2;; xch = xch->next)
     973              :                 {
     974              :                         /*
     975              :                          * Merge previous with current change atom.
     976              :                          */
     977           36 :                         for(; s1 < xch->i1 && s2 < xch->i2; s1++,s2++)
     978              :                         {
     979            0 :                                 if(xdl_emit_record(&xe->xdf1,s1," ",ecb) < 0)
     980              :                                 {
     981            0 :                                         return -1;
     982              :                                 }
     983              :                         }
     984              : 
     985              :                         /*
     986              :                          * Removes lines from the first file.
     987              :                          */
     988           88 :                         for(s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
     989              :                         {
     990           52 :                                 if(xdl_emit_record(&xe->xdf1,s1,"-",ecb) < 0)
     991              :                                 {
     992            0 :                                         return -1;
     993              :                                 }
     994              :                         }
     995              : 
     996              :                         /*
     997              :                          * Adds lines from the second file.
     998              :                          */
     999           98 :                         for(s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
    1000              :                         {
    1001           62 :                                 if(xdl_emit_record(&xe->xdf2,s2,"+",ecb) < 0)
    1002              :                                 {
    1003            0 :                                         return -1;
    1004              :                                 }
    1005              :                         }
    1006              : 
    1007           36 :                         if(xch == xche)
    1008              :                         {
    1009           36 :                                 break;
    1010              :                         }
    1011            0 :                         s1 = xch->i1 + xch->chg1;
    1012            0 :                         s2 = xch->i2 + xch->chg2;
    1013              :                 }
    1014              : 
    1015              :                 /*
    1016              :                  * Emit post-context.
    1017              :                  */
    1018           36 :                 for(s1 = xche->i1 + xche->chg1; s1 < e1; s1++)
    1019              :                 {
    1020            0 :                         if(xdl_emit_record(&xe->xdf1,s1," ",ecb) < 0)
    1021              :                         {
    1022            0 :                                 return -1;
    1023              :                         }
    1024              :                 }
    1025              :         }
    1026              : 
    1027            8 :         return 0;
    1028              : }
    1029              : 
    1030              : typedef struct s_xdlclass {
    1031              :         struct s_xdlclass *next;
    1032              :         unsigned long ha;
    1033              :         char const *line;
    1034              :         long size;
    1035              :         long idx;
    1036              : } xdlclass_t;
    1037              : 
    1038              : typedef struct s_xdlclassifier {
    1039              :         unsigned int hbits;
    1040              :         long hsize;
    1041              :         xdlclass_t **rchash;
    1042              :         chastore_t ncha;
    1043              :         long count;
    1044              : } xdlclassifier_t;
    1045              : 
    1046            8 : static int xdl_init_classifier(
    1047              :         xdlclassifier_t *cf,
    1048              :         long            size)
    1049              : {
    1050              :         long i;
    1051              : 
    1052            8 :         cf->hbits = xdl_hashbits((unsigned int)size);
    1053            8 :         cf->hsize = 1 << cf->hbits;
    1054              : 
    1055            8 :         if(xdl_cha_init(&cf->ncha,sizeof(xdlclass_t),size / 4 + 1) < 0)
    1056              :         {
    1057            0 :                 return -1;
    1058              :         }
    1059              : 
    1060            8 :         if(!(cf->rchash =
    1061            8 :                 (xdlclass_t **)malloc(cf->hsize * sizeof(xdlclass_t *))))
    1062              :         {
    1063              : 
    1064            0 :                 xdl_cha_free(&cf->ncha);
    1065            0 :                 return -1;
    1066              :         }
    1067              : 
    1068         1544 :         for(i = 0; i < cf->hsize; i++)
    1069              :         {
    1070         1536 :                 cf->rchash[i] = NULL;
    1071              :         }
    1072              : 
    1073            8 :         cf->count = 0;
    1074              : 
    1075            8 :         return 0;
    1076              : }
    1077              : 
    1078            8 : static void xdl_free_classifier(xdlclassifier_t *cf)
    1079              : {
    1080            8 :         free(cf->rchash);
    1081            8 :         xdl_cha_free(&cf->ncha);
    1082            8 : }
    1083              : 
    1084          974 : static int xdl_classify_record(
    1085              :         xdlclassifier_t *cf,
    1086              :         xrecord_t       **rhash,
    1087              :         unsigned int    hbits,
    1088              :         xrecord_t       *rec)
    1089              : {
    1090              :         long hi;
    1091              :         char const *line;
    1092              :         xdlclass_t *rcrec;
    1093              : 
    1094          974 :         line = rec->ptr;
    1095          974 :         hi = (long)XDL_HASHLONG(rec->ha,cf->hbits);
    1096              : 
    1097         1106 :         for(rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
    1098              :         {
    1099          570 :                 if(rcrec->ha == rec->ha && rcrec->size == rec->size &&
    1100          438 :                         !memcmp(line,rcrec->line,rec->size))
    1101              :                 {
    1102          438 :                         break;
    1103              :                 }
    1104              :         }
    1105              : 
    1106          974 :         if(!rcrec)
    1107              :         {
    1108          536 :                 if(!(rcrec = xdl_cha_alloc(&cf->ncha)))
    1109              :                 {
    1110            0 :                         return -1;
    1111              :                 }
    1112          536 :                 rcrec->idx = cf->count++;
    1113          536 :                 rcrec->line = line;
    1114          536 :                 rcrec->size = rec->size;
    1115          536 :                 rcrec->ha = rec->ha;
    1116          536 :                 rcrec->next = cf->rchash[hi];
    1117          536 :                 cf->rchash[hi] = rcrec;
    1118              :         }
    1119              : 
    1120          974 :         rec->ha = (unsigned long)rcrec->idx;
    1121              : 
    1122          974 :         hi = (long)XDL_HASHLONG(rec->ha,hbits);
    1123          974 :         rec->next = rhash[hi];
    1124          974 :         rhash[hi] = rec;
    1125              : 
    1126          974 :         return 0;
    1127              : }
    1128              : 
    1129           16 : static int xdl_prepare_ctx(
    1130              :         mmfile_t        *mf,
    1131              :         long            narec,
    1132              :         xdlclassifier_t *cf,
    1133              :         xdfile_t        *xdf)
    1134              : {
    1135              :         unsigned int hbits;
    1136              :         long i,nrec,hsize,bsize;
    1137              :         unsigned long hav;
    1138              :         char const *blk,*cur,*top,*prev;
    1139              :         xrecord_t *crec;
    1140              :         xrecord_t **recs,**rrecs;
    1141              :         xrecord_t **rhash;
    1142              :         unsigned long *ha;
    1143              :         char *rchg;
    1144              :         long *rindex;
    1145              : 
    1146           16 :         if(xdl_cha_init(&xdf->rcha,sizeof(xrecord_t),narec / 4 + 1) < 0)
    1147              :         {
    1148            0 :                 return -1;
    1149              :         }
    1150              : 
    1151           16 :         if(!(recs = (xrecord_t **)malloc(narec * sizeof(xrecord_t *))))
    1152              :         {
    1153              : 
    1154            0 :                 xdl_cha_free(&xdf->rcha);
    1155            0 :                 return -1;
    1156              :         }
    1157              : 
    1158           16 :         hbits = xdl_hashbits((unsigned int)narec);
    1159           16 :         hsize = 1 << hbits;
    1160              : 
    1161           16 :         if(!(rhash = (xrecord_t **)malloc(hsize * sizeof(xrecord_t *))))
    1162              :         {
    1163              : 
    1164            0 :                 free(recs);
    1165            0 :                 xdl_cha_free(&xdf->rcha);
    1166            0 :                 return -1;
    1167              :         }
    1168              : 
    1169         1552 :         for(i = 0; i < hsize; i++)
    1170              :         {
    1171         1536 :                 rhash[i] = NULL;
    1172              :         }
    1173              : 
    1174           16 :         nrec = 0;
    1175              : 
    1176           16 :         if((cur = blk = xdl_mmfile_first(mf,&bsize)) != NULL)
    1177              :         {
    1178           16 :                 for(top = blk + bsize;;)
    1179              :                 {
    1180          990 :                         if(cur >= top)
    1181              :                         {
    1182           16 :                                 if(!(cur = blk = xdl_mmfile_next(mf,&bsize)))
    1183              :                                 {
    1184           16 :                                         break;
    1185              :                                 }
    1186            0 :                                 top = blk + bsize;
    1187              :                         }
    1188          974 :                         prev = cur;
    1189          974 :                         hav = xdl_hash_record(&cur,top);
    1190              : 
    1191          974 :                         if(nrec >= narec)
    1192              :                         {
    1193            0 :                                 narec *= 2;
    1194              : 
    1195            0 :                                 if(!(rrecs = (xrecord_t **)realloc(
    1196              :                                         recs,narec * sizeof(xrecord_t *))))
    1197              :                                 {
    1198              : 
    1199            0 :                                         free(rhash);
    1200            0 :                                         free(recs);
    1201            0 :                                         xdl_cha_free(&xdf->rcha);
    1202            0 :                                         return -1;
    1203              :                                 }
    1204            0 :                                 recs = rrecs;
    1205              :                         }
    1206              : 
    1207          974 :                         if(!(crec = xdl_cha_alloc(&xdf->rcha)))
    1208              :                         {
    1209              : 
    1210            0 :                                 free(rhash);
    1211            0 :                                 free(recs);
    1212            0 :                                 xdl_cha_free(&xdf->rcha);
    1213            0 :                                 return -1;
    1214              :                         }
    1215          974 :                         crec->ptr = prev;
    1216          974 :                         crec->size = (long)(cur - prev);
    1217          974 :                         crec->ha = hav;
    1218          974 :                         recs[nrec++] = crec;
    1219              : 
    1220          974 :                         if(xdl_classify_record(cf,rhash,hbits,crec) < 0)
    1221              :                         {
    1222              : 
    1223            0 :                                 free(rhash);
    1224            0 :                                 free(recs);
    1225            0 :                                 xdl_cha_free(&xdf->rcha);
    1226            0 :                                 return -1;
    1227              :                         }
    1228              :                 }
    1229              :         }
    1230              : 
    1231           16 :         if(!(rchg = (char *)malloc(nrec + 2)))
    1232              :         {
    1233              : 
    1234            0 :                 free(rhash);
    1235            0 :                 free(recs);
    1236            0 :                 xdl_cha_free(&xdf->rcha);
    1237            0 :                 return -1;
    1238              :         }
    1239           16 :         memset(rchg,0,nrec + 2);
    1240              : 
    1241           16 :         if(!(rindex = (long *)malloc((nrec + 1) * sizeof(long))))
    1242              :         {
    1243              : 
    1244            0 :                 free(rchg);
    1245            0 :                 free(rhash);
    1246            0 :                 free(recs);
    1247            0 :                 xdl_cha_free(&xdf->rcha);
    1248            0 :                 return -1;
    1249              :         }
    1250              : 
    1251           16 :         if(!(ha = (unsigned long *)malloc((nrec + 1) * sizeof(unsigned long))))
    1252              :         {
    1253              : 
    1254            0 :                 free(rindex);
    1255            0 :                 free(rchg);
    1256            0 :                 free(rhash);
    1257            0 :                 free(recs);
    1258            0 :                 xdl_cha_free(&xdf->rcha);
    1259            0 :                 return -1;
    1260              :         }
    1261              : 
    1262           16 :         xdf->nrec = nrec;
    1263           16 :         xdf->recs = recs;
    1264           16 :         xdf->hbits = hbits;
    1265           16 :         xdf->rhash = rhash;
    1266           16 :         xdf->rchg = rchg + 1;
    1267           16 :         xdf->rindex = rindex;
    1268           16 :         xdf->nreff = 0;
    1269           16 :         xdf->ha = ha;
    1270           16 :         xdf->dstart = 0;
    1271           16 :         xdf->dend = nrec - 1;
    1272              : 
    1273           16 :         return 0;
    1274              : }
    1275              : 
    1276           16 : static void xdl_free_ctx(xdfile_t *xdf)
    1277              : {
    1278           16 :         free(xdf->rhash);
    1279           16 :         free(xdf->rindex);
    1280           16 :         free(xdf->rchg - 1);
    1281           16 :         free(xdf->ha);
    1282           16 :         free(xdf->recs);
    1283           16 :         xdl_cha_free(&xdf->rcha);
    1284           16 : }
    1285              : 
    1286            0 : static int xdl_clean_mmatch(
    1287              :         char const *dis,
    1288              :         long       i,
    1289              :         long       s,
    1290              :         long       e)
    1291              : {
    1292              :         long r,rdis0,rpdis0,rdis1,rpdis1;
    1293              : 
    1294              :         /*
    1295              :          * Limits the window the is examined during the similar-lines
    1296              :          * scan. The loops below stops when dis[i - r] == 1 (line that
    1297              :          * has no match), but there are corner cases where the loop
    1298              :          * proceed all the way to the extremities by causing huge
    1299              :          * performance penalties in case of big files.
    1300              :          */
    1301            0 :         if(i - s > XDL_SIMSCAN_WINDOW)
    1302              :         {
    1303            0 :                 s = i - XDL_SIMSCAN_WINDOW;
    1304              :         }
    1305              : 
    1306            0 :         if(e - i > XDL_SIMSCAN_WINDOW)
    1307              :         {
    1308            0 :                 e = i + XDL_SIMSCAN_WINDOW;
    1309              :         }
    1310              : 
    1311              :         /*
    1312              :          * Scans the lines before 'i' to find a run of lines that either
    1313              :          * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
    1314              :          * Note that we always call this function with dis[i] > 1, so the
    1315              :          * current line (i) is already a multimatch line.
    1316              :          */
    1317            0 :         for(r = 1,rdis0 = 0,rpdis0 = 1; (i - r) >= s; r++)
    1318              :         {
    1319            0 :                 if(!dis[i - r])
    1320              :                 {
    1321            0 :                         rdis0++;
    1322            0 :                 } else if(dis[i - r] == 2){
    1323            0 :                         rpdis0++;
    1324              :                 } else {
    1325            0 :                         break;
    1326              :                 }
    1327              :         }
    1328              : 
    1329              :         /*
    1330              :          * If the run before the line 'i' found only multimatch lines, we
    1331              :          * return 0 and hence we don't make the current line (i) discarded.
    1332              :          * We want to discard multimatch lines only when they appear in the
    1333              :          * middle of runs with nomatch lines (dis[j] == 0).
    1334              :          */
    1335            0 :         if(rdis0 == 0)
    1336              :         {
    1337            0 :                 return 0;
    1338              :         }
    1339              : 
    1340            0 :         for(r = 1,rdis1 = 0,rpdis1 = 1; (i + r) <= e; r++)
    1341              :         {
    1342            0 :                 if(!dis[i + r])
    1343              :                 {
    1344            0 :                         rdis1++;
    1345            0 :                 } else if(dis[i + r] == 2){
    1346            0 :                         rpdis1++;
    1347              :                 } else {
    1348            0 :                         break;
    1349              :                 }
    1350              :         }
    1351              : 
    1352              :         /*
    1353              :          * If the run after the line 'i' found only multimatch lines, we
    1354              :          * return 0 and hence we don't make the current line (i) discarded.
    1355              :          */
    1356            0 :         if(rdis1 == 0)
    1357              :         {
    1358            0 :                 return 0;
    1359              :         }
    1360            0 :         rdis1 += rdis0;
    1361            0 :         rpdis1 += rpdis0;
    1362              : 
    1363            0 :         return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
    1364              : }
    1365              : 
    1366              : /*
    1367              :  * Try to reduce the problem complexity, discard records that have no
    1368              :  * matches on the other file. Also, lines that have multiple matches
    1369              :  * might be potentially discarded if they appear in a run of discardable.
    1370              :  */
    1371            8 : static int xdl_cleanup_records(
    1372              :         xdfile_t *xdf1,
    1373              :         xdfile_t *xdf2)
    1374              : {
    1375              :         long i,nm,rhi,nreff,mlim;
    1376              :         unsigned long hav;
    1377              :         xrecord_t **recs;
    1378              :         xrecord_t *rec;
    1379              :         char *dis,*dis1,*dis2;
    1380              : 
    1381            8 :         if(!(dis = (char *)malloc(xdf1->nrec + xdf2->nrec + 2)))
    1382              :         {
    1383            0 :                 return -1;
    1384              :         }
    1385            8 :         memset(dis,0,xdf1->nrec + xdf2->nrec + 2);
    1386            8 :         dis1 = dis;
    1387            8 :         dis2 = dis1 + xdf1->nrec + 1;
    1388              : 
    1389            8 :         if((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
    1390              :         {
    1391            0 :                 mlim = XDL_MAX_EQLIMIT;
    1392              :         }
    1393              : 
    1394          416 :         for(i = xdf1->dstart,recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend;
    1395          408 :                 i++,recs++)
    1396              :         {
    1397          408 :                 hav = (*recs)->ha;
    1398          408 :                 rhi = (long)XDL_HASHLONG(hav,xdf2->hbits);
    1399              : 
    1400          776 :                 for(nm = 0,rec = xdf2->rhash[rhi]; rec; rec = rec->next)
    1401              :                 {
    1402          368 :                         if(rec->ha == hav && ++nm == mlim)
    1403              :                         {
    1404            0 :                                 break;
    1405              :                         }
    1406              :                 }
    1407          408 :                 dis1[i] = (nm == 0) ? 0 : (nm >= mlim) ? 2 : 1;
    1408              :         }
    1409              : 
    1410            8 :         if((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
    1411              :         {
    1412            0 :                 mlim = XDL_MAX_EQLIMIT;
    1413              :         }
    1414              : 
    1415          426 :         for(i = xdf2->dstart,recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend;
    1416          418 :                 i++,recs++)
    1417              :         {
    1418          418 :                 hav = (*recs)->ha;
    1419          418 :                 rhi = (long)XDL_HASHLONG(hav,xdf1->hbits);
    1420              : 
    1421          794 :                 for(nm = 0,rec = xdf1->rhash[rhi]; rec; rec = rec->next)
    1422              :                 {
    1423          376 :                         if(rec->ha == hav && ++nm == mlim)
    1424              :                         {
    1425            0 :                                 break;
    1426              :                         }
    1427              :                 }
    1428          418 :                 dis2[i] = (nm == 0) ? 0 : (nm >= mlim) ? 2 : 1;
    1429              :         }
    1430              : 
    1431            8 :         for(nreff = 0,i = xdf1->dstart,recs = &xdf1->recs[xdf1->dstart];
    1432          416 :                 i <= xdf1->dend;
    1433          408 :                 i++,recs++)
    1434              :         {
    1435          408 :                 if(dis1[i] == 1 ||
    1436           52 :                         (dis1[i] == 2 &&
    1437            0 :                         !xdl_clean_mmatch(dis1,i,xdf1->dstart,xdf1->dend)))
    1438              :                 {
    1439          356 :                         xdf1->rindex[nreff] = i;
    1440          356 :                         xdf1->ha[nreff] = (*recs)->ha;
    1441          356 :                         nreff++;
    1442              :                 } else {
    1443           52 :                         xdf1->rchg[i] = 1;
    1444              :                 }
    1445              :         }
    1446            8 :         xdf1->nreff = nreff;
    1447              : 
    1448            8 :         for(nreff = 0,i = xdf2->dstart,recs = &xdf2->recs[xdf2->dstart];
    1449          426 :                 i <= xdf2->dend;
    1450          418 :                 i++,recs++)
    1451              :         {
    1452          418 :                 if(dis2[i] == 1 ||
    1453           56 :                         (dis2[i] == 2 &&
    1454            0 :                         !xdl_clean_mmatch(dis2,i,xdf2->dstart,xdf2->dend)))
    1455              :                 {
    1456          362 :                         xdf2->rindex[nreff] = i;
    1457          362 :                         xdf2->ha[nreff] = (*recs)->ha;
    1458          362 :                         nreff++;
    1459              :                 } else {
    1460           56 :                         xdf2->rchg[i] = 1;
    1461              :                 }
    1462              :         }
    1463            8 :         xdf2->nreff = nreff;
    1464              : 
    1465            8 :         free(dis);
    1466              : 
    1467            8 :         return 0;
    1468              : }
    1469              : 
    1470              : /*
    1471              :  * Early trim initial and terminal matching records.
    1472              :  */
    1473            8 : static int xdl_trim_ends(
    1474              :         xdfile_t *xdf1,
    1475              :         xdfile_t *xdf2)
    1476              : {
    1477              :         long i,lim;
    1478              :         xrecord_t **recs1,**recs2;
    1479              : 
    1480            8 :         recs1 = xdf1->recs;
    1481            8 :         recs2 = xdf2->recs;
    1482              : 
    1483           66 :         for(i = 0,lim = XDL_MIN(xdf1->nrec,xdf2->nrec); i < lim;
    1484           58 :                 i++,recs1++,recs2++)
    1485              :         {
    1486           66 :                 if((*recs1)->ha != (*recs2)->ha)
    1487              :                 {
    1488            8 :                         break;
    1489              :                 }
    1490              :         }
    1491              : 
    1492            8 :         xdf1->dstart = xdf2->dstart = i;
    1493              : 
    1494            8 :         recs1 = xdf1->recs + xdf1->nrec - 1;
    1495            8 :         recs2 = xdf2->recs + xdf2->nrec - 1;
    1496              : 
    1497           24 :         for(lim -= i,i = 0; i < lim; i++,recs1--,recs2--)
    1498              :         {
    1499           24 :                 if((*recs1)->ha != (*recs2)->ha)
    1500              :                 {
    1501            8 :                         break;
    1502              :                 }
    1503              :         }
    1504              : 
    1505            8 :         xdf1->dend = xdf1->nrec - i - 1;
    1506            8 :         xdf2->dend = xdf2->nrec - i - 1;
    1507              : 
    1508            8 :         return 0;
    1509              : }
    1510              : 
    1511            8 : static int xdl_optimize_ctxs(
    1512              :         xdfile_t *xdf1,
    1513              :         xdfile_t *xdf2)
    1514              : {
    1515            8 :         if(xdl_trim_ends(xdf1,xdf2) < 0 || xdl_cleanup_records(xdf1,xdf2) < 0)
    1516              :         {
    1517            0 :                 return -1;
    1518              :         }
    1519              : 
    1520            8 :         return 0;
    1521              : }
    1522              : 
    1523            8 : int xdl_prepare_env(
    1524              :         mmfile_t *mf1,
    1525              :         mmfile_t *mf2,
    1526              :         xdfenv_t *xe)
    1527              : {
    1528              :         long enl1,enl2;
    1529              :         xdlclassifier_t cf;
    1530              : 
    1531            8 :         enl1 = xdl_guess_lines(mf1) + 1;
    1532            8 :         enl2 = xdl_guess_lines(mf2) + 1;
    1533              : 
    1534            8 :         if(xdl_init_classifier(&cf,enl1 + enl2 + 1) < 0)
    1535              :         {
    1536            0 :                 return -1;
    1537              :         }
    1538              : 
    1539            8 :         if(xdl_prepare_ctx(mf1,enl1,&cf,&xe->xdf1) < 0)
    1540              :         {
    1541              : 
    1542            0 :                 xdl_free_classifier(&cf);
    1543            0 :                 return -1;
    1544              :         }
    1545              : 
    1546            8 :         if(xdl_prepare_ctx(mf2,enl2,&cf,&xe->xdf2) < 0)
    1547              :         {
    1548              : 
    1549            0 :                 xdl_free_ctx(&xe->xdf1);
    1550            0 :                 xdl_free_classifier(&cf);
    1551            0 :                 return -1;
    1552              :         }
    1553              : 
    1554            8 :         xdl_free_classifier(&cf);
    1555              : 
    1556            8 :         if(xdl_optimize_ctxs(&xe->xdf1,&xe->xdf2) < 0)
    1557              :         {
    1558              : 
    1559            0 :                 xdl_free_ctx(&xe->xdf2);
    1560            0 :                 xdl_free_ctx(&xe->xdf1);
    1561            0 :                 return -1;
    1562              :         }
    1563              : 
    1564            8 :         return 0;
    1565              : }
    1566              : 
    1567            8 : void xdl_free_env(xdfenv_t *xe)
    1568              : {
    1569            8 :         xdl_free_ctx(&xe->xdf2);
    1570            8 :         xdl_free_ctx(&xe->xdf1);
    1571            8 : }
    1572              : 
    1573            0 : int xdlt_load_mmfile(
    1574              :         char const *fname,
    1575              :         mmfile_t   *mf)
    1576              : {
    1577              :         int fd;
    1578              :         long size;
    1579              :         char *blk;
    1580              : 
    1581            0 :         if(xdl_init_mmfile(mf,XDLT_STD_BLKSIZE,XDL_MMF_ATOMIC) < 0)
    1582              :         {
    1583            0 :                 return -1;
    1584              :         }
    1585              : 
    1586            0 :         if((fd = open(fname,O_RDONLY)) == -1)
    1587              :         {
    1588            0 :                 perror(fname);
    1589            0 :                 xdl_free_mmfile(mf);
    1590            0 :                 return -1;
    1591              :         }
    1592            0 :         size = lseek(fd,0,SEEK_END);
    1593            0 :         lseek(fd,0,SEEK_SET);
    1594              : 
    1595            0 :         if(!(blk = (char *)xdl_mmfile_writeallocate(mf,size)))
    1596              :         {
    1597            0 :                 xdl_free_mmfile(mf);
    1598            0 :                 close(fd);
    1599            0 :                 return -1;
    1600              :         }
    1601              : 
    1602            0 :         if(read(fd,blk,(size_t)size) != (ssize_t)size)
    1603              :         {
    1604            0 :                 perror(fname);
    1605            0 :                 xdl_free_mmfile(mf);
    1606            0 :                 close(fd);
    1607            0 :                 return -1;
    1608              :         }
    1609            0 :         close(fd);
    1610              : 
    1611            0 :         return 0;
    1612              : }
    1613              : 
    1614           24 : long xdl_bogosqrt(long n)
    1615              : {
    1616              :         long i;
    1617              : 
    1618              :         /*
    1619              :          * Classical integer square root approximation using shifts.
    1620              :          */
    1621          110 :         for(i = 1; n > 0; n >>= 2)
    1622              :         {
    1623           86 :                 i <<= 1;
    1624              :         }
    1625              : 
    1626           24 :         return i;
    1627              : }
    1628              : 
    1629          114 : int xdl_emit_diffrec(
    1630              :         char const *rec,
    1631              :         long       size,
    1632              :         char const *pre,
    1633              :         long       psize,
    1634              :         xdemitcb_t *ecb)
    1635              : {
    1636          114 :         int i = 2;
    1637              :         mmbuffer_t mb[3];
    1638              : 
    1639          114 :         mb[0].ptr = (char *)pre;
    1640          114 :         mb[0].size = psize;
    1641          114 :         mb[1].ptr = (char *)rec;
    1642          114 :         mb[1].size = size;
    1643              : 
    1644          114 :         if(size > 0 && rec[size - 1] != '\n')
    1645              :         {
    1646            0 :                 mb[2].ptr = (char *)"\n\\ No newline at end of file\n";
    1647            0 :                 mb[2].size = strlen(mb[2].ptr);
    1648            0 :                 i++;
    1649              :         }
    1650              : 
    1651          114 :         if(ecb->outf(ecb->priv,mb,i) < 0)
    1652              :         {
    1653            0 :                 return -1;
    1654              :         }
    1655              : 
    1656          114 :         return 0;
    1657              : }
    1658              : 
    1659           16 : int xdl_init_mmfile(
    1660              :         mmfile_t      *mmf,
    1661              :         long          bsize,
    1662              :         unsigned long flags)
    1663              : {
    1664              : 
    1665           16 :         mmf->flags = flags;
    1666           16 :         mmf->head = mmf->tail = NULL;
    1667           16 :         mmf->bsize = bsize;
    1668           16 :         mmf->fsize = 0;
    1669           16 :         mmf->rcur = mmf->wcur = NULL;
    1670           16 :         mmf->rpos = 0;
    1671              : 
    1672           16 :         return 0;
    1673              : }
    1674              : 
    1675           16 : void xdl_free_mmfile(mmfile_t *mmf)
    1676              : {
    1677              :         mmblock_t *cur,*tmp;
    1678              : 
    1679           32 :         for(cur = mmf->head; (tmp = cur) != NULL;)
    1680              :         {
    1681           16 :                 cur = cur->next;
    1682           16 :                 free(tmp);
    1683              :         }
    1684           16 : }
    1685              : 
    1686           16 : void *xdl_mmfile_writeallocate(
    1687              :         mmfile_t *mmf,
    1688              :         long     size)
    1689              : {
    1690              :         long bsize;
    1691              :         mmblock_t *wcur;
    1692              :         char *blk;
    1693              : 
    1694           16 :         if(!(wcur = mmf->wcur) || wcur->size + size > wcur->bsize)
    1695              :         {
    1696           16 :                 bsize = XDL_MAX(mmf->bsize,size);
    1697              : 
    1698           16 :                 if(!(wcur = (mmblock_t *)malloc(sizeof(mmblock_t) + bsize)))
    1699              :                 {
    1700            0 :                         return NULL;
    1701              :                 }
    1702           16 :                 wcur->flags = 0;
    1703           16 :                 wcur->ptr = (char *)wcur + sizeof(mmblock_t);
    1704           16 :                 wcur->size = 0;
    1705           16 :                 wcur->bsize = bsize;
    1706           16 :                 wcur->next = NULL;
    1707              : 
    1708           16 :                 if(!mmf->head)
    1709              :                 {
    1710           16 :                         mmf->head = wcur;
    1711              :                 }
    1712              : 
    1713           16 :                 if(mmf->tail)
    1714              :                 {
    1715            0 :                         mmf->tail->next = wcur;
    1716              :                 }
    1717           16 :                 mmf->tail = wcur;
    1718           16 :                 mmf->wcur = wcur;
    1719              :         }
    1720              : 
    1721           16 :         blk = wcur->ptr + wcur->size;
    1722           16 :         wcur->size += size;
    1723           16 :         mmf->fsize += size;
    1724              : 
    1725           16 :         return blk;
    1726              : }
    1727              : 
    1728           32 : void *xdl_mmfile_first(
    1729              :         mmfile_t *mmf,
    1730              :         long     *size)
    1731              : {
    1732              : 
    1733           32 :         if(!(mmf->rcur = mmf->head))
    1734              :         {
    1735            0 :                 return NULL;
    1736              :         }
    1737              : 
    1738           32 :         *size = mmf->rcur->size;
    1739              : 
    1740           32 :         return mmf->rcur->ptr;
    1741              : }
    1742              : 
    1743           32 : void *xdl_mmfile_next(
    1744              :         mmfile_t *mmf,
    1745              :         long     *size)
    1746              : {
    1747              : 
    1748           32 :         if(!mmf->rcur || !(mmf->rcur = mmf->rcur->next))
    1749              :         {
    1750           32 :                 return NULL;
    1751              :         }
    1752              : 
    1753            0 :         *size = mmf->rcur->size;
    1754              : 
    1755            0 :         return mmf->rcur->ptr;
    1756              : }
    1757              : 
    1758           16 : long xdl_mmfile_size(mmfile_t *mmf)
    1759              : {
    1760           16 :         return mmf->fsize;
    1761              : }
    1762              : 
    1763           24 : int xdl_cha_init(
    1764              :         chastore_t *cha,
    1765              :         long       isize,
    1766              :         long       icount)
    1767              : {
    1768              : 
    1769           24 :         cha->head = cha->tail = NULL;
    1770           24 :         cha->isize = isize;
    1771           24 :         cha->nsize = icount * isize;
    1772           24 :         cha->ancur = cha->sncur = NULL;
    1773           24 :         cha->scurr = 0;
    1774              : 
    1775           24 :         return 0;
    1776              : }
    1777              : 
    1778           24 : void xdl_cha_free(chastore_t *cha)
    1779              : {
    1780              :         chanode_t *cur,*tmp;
    1781              : 
    1782          106 :         for(cur = cha->head; (tmp = cur) != NULL;)
    1783              :         {
    1784           82 :                 cur = cur->next;
    1785           82 :                 free(tmp);
    1786              :         }
    1787           24 : }
    1788              : 
    1789         1510 : void *xdl_cha_alloc(chastore_t *cha)
    1790              : {
    1791              :         chanode_t *ancur;
    1792              :         void *data;
    1793              : 
    1794         1510 :         if(!(ancur = cha->ancur) || ancur->icurr == cha->nsize)
    1795              :         {
    1796           82 :                 if(!(ancur = (chanode_t *)malloc(sizeof(chanode_t) + cha->nsize)))
    1797              :                 {
    1798            0 :                         return NULL;
    1799              :                 }
    1800           82 :                 ancur->icurr = 0;
    1801           82 :                 ancur->next = NULL;
    1802              : 
    1803           82 :                 if(cha->tail)
    1804              :                 {
    1805           58 :                         cha->tail->next = ancur;
    1806              :                 }
    1807              : 
    1808           82 :                 if(!cha->head)
    1809              :                 {
    1810           24 :                         cha->head = ancur;
    1811              :                 }
    1812           82 :                 cha->tail = ancur;
    1813           82 :                 cha->ancur = ancur;
    1814              :         }
    1815              : 
    1816         1510 :         data = (char *)ancur + sizeof(chanode_t) + ancur->icurr;
    1817         1510 :         ancur->icurr += cha->isize;
    1818              : 
    1819         1510 :         return data;
    1820              : }
    1821              : 
    1822           16 : long xdl_guess_lines(mmfile_t *mf)
    1823              : {
    1824           16 :         long nl = 0,size,tsize = 0;
    1825              :         char const *data,*cur,*top;
    1826              : 
    1827           16 :         if((cur = data = xdl_mmfile_first(mf,&size)) != NULL)
    1828              :         {
    1829          990 :                 for(top = data + size; nl < XDL_GUESS_NLINES;)
    1830              :                 {
    1831          990 :                         if(cur >= top)
    1832              :                         {
    1833           16 :                                 tsize += (long)(cur - data);
    1834              : 
    1835           16 :                                 if(!(cur = data = xdl_mmfile_next(mf,&size)))
    1836              :                                 {
    1837           16 :                                         break;
    1838              :                                 }
    1839            0 :                                 top = data + size;
    1840              :                         }
    1841          974 :                         nl++;
    1842              : 
    1843          974 :                         if(!(cur = memchr(cur,'\n',top - cur)))
    1844              :                         {
    1845           16 :                                 cur = top;
    1846              :                         } else {
    1847          958 :                                 cur++;
    1848              :                         }
    1849              :                 }
    1850           16 :                 tsize += (long)(cur - data);
    1851              :         }
    1852              : 
    1853           16 :         if(nl && tsize)
    1854              :         {
    1855           16 :                 nl = xdl_mmfile_size(mf) / (tsize / nl);
    1856              :         }
    1857              : 
    1858           16 :         return nl + 1;
    1859              : }
    1860              : 
    1861          974 : unsigned long xdl_hash_record(
    1862              :         char const **data,
    1863              :         char const *top)
    1864              : {
    1865          974 :         unsigned long ha = 5381;
    1866          974 :         char const *ptr = *data;
    1867              : 
    1868        58416 :         for(; ptr < top && *ptr != '\n'; ptr++)
    1869              :         {
    1870        57442 :                 ha += (ha << 5);
    1871        57442 :                 ha ^= (unsigned long)*ptr;
    1872              :         }
    1873          974 :         *data = ptr < top ? ptr + 1 : ptr;
    1874              : 
    1875          974 :         return ha;
    1876              : }
    1877              : 
    1878           24 : unsigned int xdl_hashbits(unsigned int size)
    1879              : {
    1880           24 :         unsigned int val = 1,bits = 0;
    1881              : 
    1882          188 :         for(; val < size && bits < CHAR_BIT * sizeof(unsigned int);
    1883          164 :                 val <<= 1,bits++)
    1884              :         {
    1885              :                 ;
    1886              :         }
    1887           24 :         return bits ? bits : 1;
    1888              : }
    1889              : 
    1890            0 : int xdl_num_out(
    1891              :         char *out,
    1892              :         long val)
    1893              : {
    1894            0 :         char *ptr,*str = out;
    1895              :         char buf[32];
    1896              : 
    1897            0 :         ptr = buf + sizeof(buf) - 1;
    1898            0 :         *ptr = '\0';
    1899              : 
    1900            0 :         if(val < 0)
    1901              :         {
    1902            0 :                 *--ptr = '-';
    1903            0 :                 val = -val;
    1904              :         }
    1905              : 
    1906            0 :         for(; val && ptr > buf; val /= 10)
    1907              :         {
    1908            0 :                 *--ptr = "0123456789"[val % 10];
    1909              :         }
    1910              : 
    1911            0 :         if(*ptr)
    1912              :         {
    1913            0 :                 for(; *ptr; ptr++,str++)
    1914              :                 {
    1915            0 :                         *str = *ptr;
    1916              :                 }
    1917              :         } else {
    1918            0 :                 *str++ = '0';
    1919              :         }
    1920            0 :         *str = '\0';
    1921              : 
    1922            0 :         return str - out;
    1923              : }
    1924              : 
    1925            0 : int xdl_emit_hunk_hdr(
    1926              :         long       s1,
    1927              :         long       c1,
    1928              :         long       s2,
    1929              :         long       c2,
    1930              :         xdemitcb_t *ecb)
    1931              : {
    1932            0 :         int nb = 0;
    1933              :         mmbuffer_t mb;
    1934              :         char buf[128];
    1935              : 
    1936            0 :         memcpy(buf,"@@ -",4);
    1937            0 :         nb += 4;
    1938              : 
    1939            0 :         nb += xdl_num_out(buf + nb,c1 ? s1 : s1 - 1);
    1940              : 
    1941            0 :         memcpy(buf + nb,",",1);
    1942            0 :         nb += 1;
    1943              : 
    1944            0 :         nb += xdl_num_out(buf + nb,c1);
    1945              : 
    1946            0 :         memcpy(buf + nb," +",2);
    1947            0 :         nb += 2;
    1948              : 
    1949            0 :         nb += xdl_num_out(buf + nb,c2 ? s2 : s2 - 1);
    1950              : 
    1951            0 :         memcpy(buf + nb,",",1);
    1952            0 :         nb += 1;
    1953              : 
    1954            0 :         nb += xdl_num_out(buf + nb,c2);
    1955              : 
    1956            0 :         memcpy(buf + nb," @@\n",4);
    1957            0 :         nb += 4;
    1958              : 
    1959            0 :         mb.ptr = buf;
    1960            0 :         mb.size = nb;
    1961              : 
    1962            0 :         if(ecb->outf(ecb->priv,&mb,1) < 0)
    1963              :         {
    1964            0 :                 return -1;
    1965              :         }
    1966              : 
    1967            0 :         return 0;
    1968              : }
    1969              : 
    1970              : #if 0
    1971              : int main(
    1972              :         int  argc,
    1973              :         char *argv[])
    1974              : {
    1975              :         int i = 1,ctxlen = 3;
    1976              :         mmfile_t mf1,mf2;
    1977              :         xpparam_t xpp;
    1978              :         xdemitconf_t xecfg;
    1979              :         xdemitcb_t ecb;
    1980              : 
    1981              :         if(!strcmp(argv[i],"--diff"))
    1982              :         {
    1983              :                 i++;
    1984              : 
    1985              :                 for(; i < argc; i++)
    1986              :                 {
    1987              :                         if(strcmp(argv[i],"-C") == 0)
    1988              :                         {
    1989              :                                 if(++i < argc)
    1990              :                                 {
    1991              :                                         ctxlen = atoi(argv[i]);
    1992              :                                 }
    1993              :                         } else {
    1994              :                                 break;
    1995              :                         }
    1996              :                 }
    1997              :         }
    1998              : 
    1999              :         xpp.flags = 0;
    2000              :         xecfg.ctxlen = ctxlen;
    2001              : 
    2002              :         if(xdlt_load_mmfile(argv[i],&mf1) < 0)
    2003              :         {
    2004              :                 return 2;
    2005              :         }
    2006              : 
    2007              :         if(xdlt_load_mmfile(argv[i + 1],&mf2) < 0)
    2008              :         {
    2009              :                 xdl_free_mmfile(&mf1);
    2010              :                 return 2;
    2011              :         }
    2012              : 
    2013              :         ecb.priv = stdout;
    2014              : 
    2015              :         if(xdl_diff(&mf1,&mf2,&xpp,&xecfg,&ecb) < 0)
    2016              :         {
    2017              :                 xdl_free_mmfile(&mf2);
    2018              :                 xdl_free_mmfile(&mf1);
    2019              :                 return 3;
    2020              :         }
    2021              : 
    2022              :         xdl_free_mmfile(&mf2);
    2023              :         xdl_free_mmfile(&mf1);
    2024              : 
    2025              :         return 0;
    2026              : }
    2027              : #endif
        

Generated by: LCOV version 2.0-1