Changes On Branch retro-sbsdiff
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch retro-sbsdiff Excluding Merge-Ins

This is equivalent to a diff from dc96d73dd0 to eae55eeafc

2012-02-04
21:09
Merge the retro-sbsdiff changes into trunk. check-in: 72c0183ac8 user: drh tags: trunk
20:36
Make the default font size is smaller for side-by-side diff. Fix the context-diff so that it understands the dc=N query parameter. Closed-Leaf check-in: eae55eeafc user: drh tags: retro-sbsdiff
20:16
Suppress the top divider on a context-diff with line numbers. check-in: 43cbe9dd5a user: drh tags: retro-sbsdiff
15:02
Merge recent trunk changes into the retro-sbsdiff branch. check-in: 066adeedfe user: drh tags: retro-sbsdiff
14:58
Make sure diff output is flushed to screen in a timely manner. check-in: dc96d73dd0 user: drh tags: trunk
14:48
User-contributed patch to the debian/makedeb.sh script for generating a debian package for Fossil. check-in: 7b1fd597b4 user: drh tags: trunk

Changes to src/diff.c.

    23     23   #include <assert.h>
    24     24   
    25     25   
    26     26   #if INTERFACE
    27     27   /*
    28     28   ** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
    29     29   */
    30         -#define DIFF_CONTEXT_MASK  0x0000fff  /* Lines of context.  Default if 0 */
    31         -#define DIFF_WIDTH_MASK    0x00ff000  /* side-by-side column width */
    32         -#define DIFF_IGNORE_EOLWS  0x0100000  /* Ignore end-of-line whitespace */
    33         -#define DIFF_SIDEBYSIDE    0x0200000  /* Generate a side-by-side diff */
    34         -#define DIFF_NEWFILE       0x0400000  /* Missing files are as empty files */
           30  +#define DIFF_CONTEXT_MASK  0x0000ffff  /* Lines of context.  Default if 0 */
           31  +#define DIFF_WIDTH_MASK    0x00ff0000  /* side-by-side column width */
           32  +#define DIFF_IGNORE_EOLWS  0x01000000  /* Ignore end-of-line whitespace */
           33  +#define DIFF_SIDEBYSIDE    0x02000000  /* Generate a side-by-side diff */
           34  +#define DIFF_NEWFILE       0x04000000  /* Missing files are as empty files */
           35  +#define DIFF_INLINE        0x08000000  /* Inline (not side-by-side) diff */
           36  +#define DIFF_HTML          0x10000000  /* Render for HTML */
           37  +#define DIFF_LINENO        0x20000000  /* Show line numbers in context diff */
    35     38   
    36     39   #endif /* INTERFACE */
    37     40   
    38     41   /*
    39     42   ** Maximum length of a line in a text file.  (8192)
    40     43   */
    41     44   #define LENGTH_MASK_SZ  13
................................................................................
   144    147   static int same_dline(DLine *pA, DLine *pB){
   145    148     return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
   146    149   }
   147    150   
   148    151   /*
   149    152   ** Append a single line of "diff" output to pOut.
   150    153   */
   151         -static void appendDiffLine(Blob *pOut, char *zPrefix, DLine *pLine){
   152         -  blob_append(pOut, zPrefix, 1);
   153         -  blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
          154  +static void appendDiffLine(Blob *pOut, char cPrefix, DLine *pLine, int html){
          155  +  blob_append(pOut, &cPrefix, 1);
          156  +  if( html ){
          157  +    char *zHtml;
          158  +    if( cPrefix=='+' ){
          159  +      blob_append(pOut, "<span class=\"diffadd\">", -1);
          160  +    }else if( cPrefix=='-' ){
          161  +      blob_append(pOut, "<span class=\"diffrm\">", -1);
          162  +    }
          163  +    zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
          164  +    blob_append(pOut, zHtml, -1);
          165  +    fossil_free(zHtml);
          166  +    if( cPrefix!=' ' ){
          167  +      blob_append(pOut, "</span>", -1);
          168  +    }
          169  +  }else{
          170  +    blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
          171  +  }
   154    172     blob_append(pOut, "\n", 1);
   155    173   }
          174  +
          175  +/*
          176  +** Append line numbers to the context diff output.  Zero or negative numbers
          177  +** are blanks.
          178  +*/
          179  +static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
          180  +  if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
          181  +  if( lnA>0 ){
          182  +    blob_appendf(pOut, "%6d ", lnA);
          183  +  }else{
          184  +    blob_append(pOut, "       ", 7);
          185  +  }
          186  +  if( lnB>0 ){
          187  +    blob_appendf(pOut, "%6d  ", lnB);
          188  +  }else{
          189  +    blob_append(pOut, "        ", 8);
          190  +  }
          191  +  if( html ) blob_append(pOut, "</span>", -1);
          192  +}
   156    193   
   157    194   /*
   158    195   ** Expand the size of aEdit[] array to hold nEdit elements.
   159    196   */
   160    197   static void expandEdit(DContext *p, int nEdit){
   161    198     p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int));
   162    199     p->nEditAlloc = nEdit;
................................................................................
   196    233   }
   197    234   
   198    235   
   199    236   /*
   200    237   ** Given a diff context in which the aEdit[] array has been filled
   201    238   ** in, compute a context diff into pOut.
   202    239   */
   203         -static void contextDiff(DContext *p, Blob *pOut, int nContext){
          240  +static void contextDiff(
          241  +  DContext *p,      /* The difference */
          242  +  Blob *pOut,       /* Output a context diff to here */
          243  +  int nContext,     /* Number of lines of context */
          244  +  int showLn,       /* Show line numbers */
          245  +  int html          /* Render as HTML */
          246  +){
   204    247     DLine *A;     /* Left side of the diff */
   205    248     DLine *B;     /* Right side of the diff */  
   206    249     int a = 0;    /* Index of next line in A[] */
   207    250     int b = 0;    /* Index of next line in B[] */
   208    251     int *R;       /* Array of COPY/DELETE/INSERT triples */
   209    252     int r;        /* Index into R[] */
   210    253     int nr;       /* Number of COPY/DELETE/INSERT triples to process */
................................................................................
   250    293         nb += R[r+i*3];
   251    294       }
   252    295       /*
   253    296        * If the patch changes an empty file or results in an empty file,
   254    297        * the block header must use 0,0 as position indicator and not 1,0.
   255    298        * Otherwise, patch would be confused and may reject the diff.
   256    299        */
   257         -    blob_appendf(pOut,"@@ -%d,%d +%d,%d @@\n",
   258         -      na ? a+skip+1 : 0, na,
   259         -      nb ? b+skip+1 : 0, nb);
          300  +    if( showLn ){
          301  +      if( r==0 ){
          302  +        /* Do not show a top divider */
          303  +      }else if( html ){
          304  +        blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
          305  +      }else{
          306  +        blob_appendf(pOut, "%.80c\n", '.');
          307  +      }
          308  +    }else{
          309  +      if( html ) blob_appendf(pOut, "<span class=\"diffln\">");
          310  +      blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
          311  +        na ? a+skip+1 : 0, na,
          312  +        nb ? b+skip+1 : 0, nb);
          313  +      if( html ) blob_appendf(pOut, "</span>");
          314  +      blob_append(pOut, "\n", 1);
          315  +    }
   260    316   
   261    317       /* Show the initial common area */
   262    318       a += skip;
   263    319       b += skip;
   264    320       m = R[r] - skip;
   265    321       for(j=0; j<m; j++){
   266         -      appendDiffLine(pOut, " ", &A[a+j]);
          322  +      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
          323  +      appendDiffLine(pOut, ' ', &A[a+j], html);
   267    324       }
   268    325       a += m;
   269    326       b += m;
   270    327   
   271    328       /* Show the differences */
   272    329       for(i=0; i<nr; i++){
   273    330         m = R[r+i*3+1];
   274    331         for(j=0; j<m; j++){
   275         -        appendDiffLine(pOut, "-", &A[a+j]);
          332  +        if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
          333  +        appendDiffLine(pOut, '-', &A[a+j], html);
   276    334         }
   277    335         a += m;
   278    336         m = R[r+i*3+2];
   279    337         for(j=0; j<m; j++){
   280         -        appendDiffLine(pOut, "+", &B[b+j]);
          338  +        if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
          339  +        appendDiffLine(pOut, '+', &B[b+j], html);
   281    340         }
   282    341         b += m;
   283    342         if( i<nr-1 ){
   284    343           m = R[r+i*3+3];
   285    344           for(j=0; j<m; j++){
   286         -          appendDiffLine(pOut, " ", &B[b+j]);
          345  +          if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
          346  +          appendDiffLine(pOut, ' ', &B[b+j], html);
   287    347           }
   288    348           b += m;
   289    349           a += m;
   290    350         }
   291    351       }
   292    352   
   293    353       /* Show the final common area */
   294    354       assert( nr==i );
   295    355       m = R[r+nr*3];
   296    356       if( m>nContext ) m = nContext;
   297    357       for(j=0; j<m; j++){
   298         -      appendDiffLine(pOut, " ", &B[b+j]);
          358  +      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
          359  +      appendDiffLine(pOut, ' ', &B[b+j], html);
   299    360       }
   300    361     }
   301    362   }
   302    363   
   303    364   /*
   304         -** Write a 6-digit line number into the buffer z[].  z[] is guaranteed to
   305         -** have space for at least 7 characters.
          365  +** Status of a single output line
   306    366   */
   307         -static void sbsWriteLineno(char *z, int ln){
   308         -  sqlite3_snprintf(7, z, "%6d", ln+1);
   309         -  z[6] = ' ';
   310         -}
          367  +typedef struct SbsLine SbsLine;
          368  +struct SbsLine {
          369  +  char *zLine;             /* The output line under construction */
          370  +  int n;                   /* Index of next unused slot in the zLine[] */
          371  +  int width;               /* Maximum width of a column in the output */
          372  +  unsigned char escHtml;  /* True to escape html characters */
          373  +};
          374  +
          375  +/*
          376  +** Flags for sbsWriteText()
          377  +*/
          378  +#define SBS_NEWLINE  0x0001   /* End with \n\000 */
          379  +#define SBS_PAD      0x0002   /* Pad output to width spaces */
          380  +#define SBS_ENDSPAN  0x0004   /* Write a </span> after text */
   311    381   
   312    382   /*
   313    383   ** Write up to width characters of pLine into z[].  Translate tabs into
   314         -** spaces.  If trunc is true, then append \n\000 after the last character
   315         -** written.
          384  +** spaces.  Add a newline if SBS_NEWLINE is set.  Translate HTML characters
          385  +** if SBS_HTML is set.  Pad the rendering out width bytes if SBS_PAD is set.
   316    386   */
   317         -static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){
          387  +static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){
   318    388     int n = pLine->h & LENGTH_MASK;
   319    389     int i, j;
   320    390     const char *zIn = pLine->z;
   321         -  for(i=j=0; i<n && j<width; i++){
          391  +  char *z = &p->zLine[p->n];
          392  +  int w = p->width;
          393  +  if( n>w ) n = w;
          394  +  for(i=j=0; i<n; i++){
   322    395       char c = zIn[i];
   323    396       if( c=='\t' ){
   324    397         z[j++] = ' ';
   325         -      while( (j&7)!=0 && j<width ) z[j++] = ' ';
          398  +      while( (j&7)!=0 && j<n ) z[j++] = ' ';
   326    399       }else if( c=='\r' || c=='\f' ){
   327    400         z[j++] = ' ';
          401  +    }else if( c=='<' && p->escHtml ){
          402  +      memcpy(&z[j], "&lt;", 4);
          403  +      j += 4;
          404  +    }else if( c=='&' && p->escHtml ){
          405  +      memcpy(&z[j], "&amp;", 5);
          406  +      j += 5;
          407  +    }else if( c=='>' && p->escHtml ){
          408  +      memcpy(&z[j], "&gt;", 4);
          409  +      j += 4;
   328    410       }else{
   329    411         z[j++] = c;
   330    412       }
   331    413     }
   332         -  if( trunc ){
          414  +  if( (flags & SBS_ENDSPAN) && p->escHtml ){
          415  +    memcpy(&z[j], "</span>", 7);
          416  +    j += 7;
          417  +  }
          418  +  if( (flags & SBS_PAD)!=0 ){
          419  +    while( i<w ){ i++;  z[j++] = ' '; }
          420  +  }
          421  +  if( flags & SBS_NEWLINE ){
   333    422       z[j++] = '\n';
   334         -    z[j] = 0;
   335    423     }
   336         -  return j;
          424  +  p->n += j;
          425  +}
          426  +
          427  +/*
          428  +** Append a string to an SbSLine with coding, interpretation, or padding.
          429  +*/
          430  +static void sbsWrite(SbsLine *p, const char *zIn, int nIn){
          431  +  memcpy(p->zLine+p->n, zIn, nIn);
          432  +  p->n += nIn;
          433  +}
          434  +
          435  +/*
          436  +** Append n spaces to the string.
          437  +*/
          438  +static void sbsWriteSpace(SbsLine *p, int n){
          439  +  while( n-- ) p->zLine[p->n++] = ' ';
          440  +}
          441  +
          442  +/*
          443  +** Append a string to the output only if we are rendering HTML.
          444  +*/
          445  +static void sbsWriteHtml(SbsLine *p, const char *zIn){
          446  +  if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn));
          447  +}
          448  +
          449  +/*
          450  +** Write a 6-digit line number followed by a single space onto the line.
          451  +*/
          452  +static void sbsWriteLineno(SbsLine *p, int ln){
          453  +  sbsWriteHtml(p, "<span class=\"diffln\">");
          454  +  sqlite3_snprintf(7, &p->zLine[p->n], "%5d ", ln+1);
          455  +  p->n += 6;
          456  +  sbsWriteHtml(p, "</span>");
          457  +  p->zLine[p->n++] = ' ';
   337    458   }
   338    459   
   339    460   
   340    461   /*
   341    462   ** Given a diff context in which the aEdit[] array has been filled
   342    463   ** in, compute a side-by-side diff into pOut.
   343    464   */
   344         -static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
          465  +static void sbsDiff(
          466  +  DContext *p,       /* The computed diff */
          467  +  Blob *pOut,        /* Write the results here */
          468  +  int nContext,      /* Number of lines of context around each change */
          469  +  int width,         /* Width of each column of output */
          470  +  int escHtml        /* True to generate HTML output */
          471  +){
   345    472     DLine *A;     /* Left side of the diff */
   346    473     DLine *B;     /* Right side of the diff */  
   347    474     int a = 0;    /* Index of next line in A[] */
   348    475     int b = 0;    /* Index of next line in B[] */
   349    476     int *R;       /* Array of COPY/DELETE/INSERT triples */
   350    477     int r;        /* Index into R[] */
   351    478     int nr;       /* Number of COPY/DELETE/INSERT triples to process */
   352    479     int mxr;      /* Maximum value for r */
   353    480     int na, nb;   /* Number of lines shown from A and B */
   354    481     int i, j;     /* Loop counters */
   355    482     int m, ma, mb;/* Number of lines to output */
   356    483     int skip;     /* Number of lines to skip */
   357         -  int mxLine;   /* Length of a line of text */
   358         -  char *zLine;  /* A line of text being formatted */
   359         -  int len;      /* Length of an output line */
          484  +  SbsLine s;    /* Output line buffer */
   360    485   
   361         -  mxLine = width*2 + 2*7 + 3 + 1;
   362         -  zLine = fossil_malloc( mxLine + 1 );
   363         -  if( zLine==0 ) return;
   364         -  zLine[mxLine] = 0;
          486  +  s.zLine = fossil_malloc( 10*width + 100 );
          487  +  if( s.zLine==0 ) return;
          488  +  s.width = width;
          489  +  s.escHtml = escHtml;
   365    490     A = p->aFrom;
   366    491     B = p->aTo;
   367    492     R = p->aEdit;
   368    493     mxr = p->nEdit;
   369    494     while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
   370    495     for(r=0; r<mxr; r += 3*nr){
   371    496       /* Figure out how many triples to show in a single block */
................................................................................
   398    523         nb += R[r+i*3];
   399    524       }
   400    525       /*
   401    526        * If the patch changes an empty file or results in an empty file,
   402    527        * the block header must use 0,0 as position indicator and not 1,0.
   403    528        * Otherwise, patch would be confused and may reject the diff.
   404    529        */
   405         -    if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.');
          530  +    if( r>0 ){
          531  +      if( escHtml ){
          532  +        blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
          533  +                           width*2+16, '.');
          534  +      }else{
          535  +        blob_appendf(pOut, "%.*c\n", width*2+16, '.');
          536  +      }
          537  +    }
   406    538   
   407    539       /* Show the initial common area */
   408    540       a += skip;
   409    541       b += skip;
   410    542       m = R[r] - skip;
   411    543       for(j=0; j<m; j++){
   412         -      memset(zLine, ' ', mxLine);
   413         -      sbsWriteLineno(zLine, a+j);
   414         -      sbsWriteText(&zLine[7], &A[a+j], width, 0);
   415         -      sbsWriteLineno(&zLine[width+10], b+j);
   416         -      len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
   417         -      blob_append(pOut, zLine, len+width+17);
          544  +      s.n = 0;
          545  +      sbsWriteLineno(&s, a+j);
          546  +      sbsWriteText(&s, &A[a+j], SBS_PAD);
          547  +      sbsWrite(&s, "   ", 3);
          548  +      sbsWriteLineno(&s, b+j);
          549  +      sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
          550  +      blob_append(pOut, s.zLine, s.n);
   418    551       }
   419    552       a += m;
   420    553       b += m;
   421    554   
   422    555       /* Show the differences */
   423    556       for(i=0; i<nr; i++){
   424    557         ma = R[r+i*3+1];
   425    558         mb = R[r+i*3+2];
   426    559         m = ma<mb ? ma : mb;
   427    560         for(j=0; j<m; j++){
   428         -        memset(zLine, ' ', mxLine);
   429         -        sbsWriteLineno(zLine, a+j);
   430         -        sbsWriteText(&zLine[7], &A[a+j], width, 0);
   431         -        zLine[width+8] = '|';
   432         -        sbsWriteLineno(&zLine[width+10], b+j);
   433         -        len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
   434         -        blob_append(pOut, zLine, len+width+17);
          561  +        s.n = 0;
          562  +        sbsWriteLineno(&s, a+j);
          563  +        sbsWriteHtml(&s, "<span class=\"diffchng\">");
          564  +        sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
          565  +        sbsWrite(&s, " | ", 3);
          566  +        sbsWriteLineno(&s, b+j);
          567  +        sbsWriteHtml(&s, "<span class=\"diffchng\">");
          568  +        sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
          569  +        blob_append(pOut, s.zLine, s.n);
   435    570         }
   436    571         a += m;
   437    572         b += m;
   438    573         ma -= m;
   439    574         mb -= m;
   440    575         for(j=0; j<ma; j++){
   441         -        memset(zLine, ' ', width+7);
   442         -        sbsWriteLineno(zLine, a+j);
   443         -        sbsWriteText(&zLine[7], &A[a+j], width, 0);
   444         -        zLine[width+8] = '<';
   445         -        zLine[width+9] = '\n';
   446         -        zLine[width+10] = 0;
   447         -        blob_append(pOut, zLine, width+10);
          576  +        s.n = 0;
          577  +        sbsWriteLineno(&s, a+j);
          578  +        sbsWriteHtml(&s, "<span class=\"diffrm\">");
          579  +        sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
          580  +        sbsWrite(&s, " <\n", 3);
          581  +        blob_append(pOut, s.zLine, s.n);
   448    582         }
   449    583         a += ma;
   450    584         for(j=0; j<mb; j++){
   451         -        memset(zLine, ' ', mxLine);
   452         -        zLine[width+8] = '>';
   453         -        sbsWriteLineno(&zLine[width+10], b+j);
   454         -        len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
   455         -        blob_append(pOut, zLine, len+width+17);
          585  +        s.n = 0;
          586  +        sbsWriteSpace(&s, width + 7);
          587  +        sbsWrite(&s, " > ", 3);
          588  +        sbsWriteLineno(&s, b+j);
          589  +        sbsWriteHtml(&s, "<span class=\"diffadd\">");
          590  +        sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
          591  +        blob_append(pOut, s.zLine, s.n);
   456    592         }
   457    593         b += mb;
   458    594         if( i<nr-1 ){
   459    595           m = R[r+i*3+3];
   460    596           for(j=0; j<m; j++){
   461         -          memset(zLine, ' ', mxLine);
   462         -          sbsWriteLineno(zLine, a+j);
   463         -          sbsWriteText(&zLine[7], &A[a+j], width, 0);
   464         -          sbsWriteLineno(&zLine[width+10], b+j);
   465         -          len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
   466         -          blob_append(pOut, zLine, len+width+17);
          597  +          s.n = 0;
          598  +          sbsWriteLineno(&s, a+j);
          599  +          sbsWriteText(&s, &A[a+j], SBS_PAD);
          600  +          sbsWrite(&s, "   ", 3);
          601  +          sbsWriteLineno(&s, b+j);
          602  +          sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
          603  +          blob_append(pOut, s.zLine, s.n);
   467    604           }
   468    605           b += m;
   469    606           a += m;
   470    607         }
   471    608       }
   472    609   
   473    610       /* Show the final common area */
   474    611       assert( nr==i );
   475    612       m = R[r+nr*3];
   476    613       if( m>nContext ) m = nContext;
   477    614       for(j=0; j<m; j++){
   478         -      memset(zLine, ' ', mxLine);
   479         -      sbsWriteLineno(zLine, a+j);
   480         -      sbsWriteText(&zLine[7], &A[a+j], width, 0);
   481         -      sbsWriteLineno(&zLine[width+10], b+j);
   482         -      len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
   483         -      blob_append(pOut, zLine, len+width+17);
          615  +      s.n = 0;
          616  +      sbsWriteLineno(&s, a+j);
          617  +      sbsWriteText(&s, &A[a+j], SBS_PAD);
          618  +      sbsWrite(&s, "   ", 3);
          619  +      sbsWriteLineno(&s, b+j);
          620  +      sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
          621  +      blob_append(pOut, s.zLine, s.n);
   484    622       }
   485    623     }
   486         -  free(zLine);
          624  +  free(s.zLine);
   487    625   }
   488    626   
   489    627   /*
   490    628   ** Compute the optimal longest common subsequence (LCS) using an
   491    629   ** exhaustive search.  This version of the LCS is only used for
   492    630   ** shorter input strings since runtime is O(N*N) where N is the
   493    631   ** input string length.
................................................................................
   784    922     }
   785    923   
   786    924     /* Compute the difference */
   787    925     diff_all(&c);
   788    926   
   789    927     if( pOut ){
   790    928       /* Compute a context or side-by-side diff into pOut */
          929  +    int escHtml = (diffFlags & DIFF_HTML)!=0;
   791    930       if( diffFlags & DIFF_SIDEBYSIDE ){
   792    931         int width = diff_width(diffFlags);
   793         -      sbsDiff(&c, pOut, nContext, width);
          932  +      sbsDiff(&c, pOut, nContext, width, escHtml);
   794    933       }else{
   795         -      contextDiff(&c, pOut, nContext);
          934  +      int showLn = (diffFlags & DIFF_LINENO)!=0;
          935  +      contextDiff(&c, pOut, nContext, showLn, escHtml);
   796    936       }
   797    937       free(c.aFrom);
   798    938       free(c.aTo);
   799    939       free(c.aEdit);
   800    940       return 0;
   801    941     }else{
   802    942       /* If a context diff is not requested, then return the
................................................................................
   803    943       ** array of COPY/DELETE/INSERT triples.
   804    944       */
   805    945       free(c.aFrom);
   806    946       free(c.aTo);
   807    947       return c.aEdit;
   808    948     }
   809    949   }
   810         -
   811         -/*
   812         -** Copy a line with a limit. Used for side-by-side diffs to enforce a maximum
   813         -** line length limit.
   814         -*/
   815         -static char *copylimline(char *out, DLine *dl, int lim){
   816         -  int len;
   817         -  len = dl->h & LENGTH_MASK;
   818         -  if( lim && len > lim ){
   819         -    memcpy(out, dl->z, lim-3);
   820         -    memcpy(&out[lim-3], "...", 4);
   821         -  }else{
   822         -    memcpy(out, dl->z, len);
   823         -    out[len] = '\0';
   824         -  }
   825         -  return out;
   826         -}
   827         -
   828         -/*
   829         -** Output table body of a side-by-side diff. Prior to the call, the caller
   830         -** should have output:
   831         -**   <table class="sbsdiff">
   832         -**   <tr><th colspan="2" class="diffhdr">Old title</th><th/>
   833         -**   <th colspan="2" class="diffhdr">New title</th></tr>
   834         -**
   835         -** And after the call, it should output:
   836         -**   </table>
   837         -**
   838         -** Some good reference diffs in the fossil repository for testing:
   839         -** /vdiff?from=080d27a&to=4b0f813&detail=1
   840         -** /vdiff?from=636804745b&to=c1d78e0556&detail=1
   841         -** /vdiff?from=c0b6c28d29&to=25169506b7&detail=1
   842         -** /vdiff?from=e3d022dffa&to=48bcfbd47b&detail=1
   843         -*/
   844         -int html_sbsdiff(
   845         -  Blob *pA_Blob,   /* FROM file */
   846         -  Blob *pB_Blob,   /* TO file */
   847         -  int nContext,    /* Amount of context to unified diff */
   848         -  int ignoreEolWs  /* Ignore whitespace at the end of lines */
   849         -){
   850         -  DContext c;
   851         -  int i;
   852         -  int iFrom, iTo;
   853         -  char *linebuf;
   854         -  int collim=0; /* Currently not settable; allows a column limit for diffs */
   855         -  int allowExp=0; /* Currently not settable; (dis)allow expansion of rows */
   856         -
   857         -  /* Prepare the input files */
   858         -  memset(&c, 0, sizeof(c));
   859         -  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
   860         -                             &c.nFrom, ignoreEolWs);
   861         -  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
   862         -                           &c.nTo, ignoreEolWs);
   863         -  if( c.aFrom==0 || c.aTo==0 ){
   864         -    free(c.aFrom);
   865         -    free(c.aTo);
   866         -    /* Note: This would be generated within a table. */
   867         -    @ <p class="generalError" style="white-space: nowrap">cannot compute
   868         -    @ difference between binary files</p>
   869         -    return 0;
   870         -  }
   871         -
   872         -  collim = collim < 4 ? 0 : collim;
   873         -
   874         -  /* Compute the difference */
   875         -  diff_all(&c);
   876         -
   877         -  linebuf = fossil_malloc(LENGTH_MASK+1);
   878         -  if( !linebuf ){
   879         -    free(c.aFrom);
   880         -    free(c.aTo);
   881         -    free(c.aEdit);
   882         -    return 0;
   883         -  }
   884         -
   885         -  iFrom=iTo=0;
   886         -  i=0;
   887         -  while( i<c.nEdit ){
   888         -    int j;
   889         -    /* Copied lines */
   890         -    for( j=0; j<c.aEdit[i]; j++){
   891         -      /* Hide lines which are copied and are further away from block boundaries
   892         -      ** than nContext lines. For each block with hidden lines, show a row
   893         -      ** notifying the user about the hidden rows.
   894         -      */
   895         -      if( j<nContext || j>c.aEdit[i]-nContext-1 ){
   896         -        @ <tr>
   897         -      }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){
   898         -        @ <tr>
   899         -        @ <td class="meta" colspan="5" style="white-space: nowrap;">
   900         -        @ %d(c.aEdit[i]-2*nContext) hidden lines</td>
   901         -        @ </tr>
   902         -        if( !allowExp )
   903         -           continue;
   904         -        @ <tr style="display:none;">
   905         -      }else{
   906         -        if( !allowExp )
   907         -           continue;
   908         -        @ <tr style="display:none;">
   909         -      }
   910         -
   911         -      copylimline(linebuf, &c.aFrom[iFrom+j], collim);
   912         -      @ <td class="lineno">%d(iFrom+j+1)</td>
   913         -      @ <td class="srcline">%h(linebuf)</td>
   914         -
   915         -      @ <td> </td>
   916         -
   917         -      copylimline(linebuf, &c.aTo[iTo+j], collim);
   918         -      @ <td class="lineno">%d(iTo+j+1)</td>
   919         -      @ <td class="srcline">%h(linebuf)</td>
   920         -
   921         -      @ </tr>
   922         -    }
   923         -    iFrom+=c.aEdit[i];
   924         -    iTo+=c.aEdit[i];
   925         -
   926         -    if( c.aEdit[i+1]!=0 && c.aEdit[i+2]!=0 ){
   927         -      int lim;
   928         -      lim = c.aEdit[i+1] > c.aEdit[i+2] ? c.aEdit[i+1] : c.aEdit[i+2];
   929         -
   930         -      /* Assume changed lines */
   931         -      for( j=0; j<lim; j++ ){
   932         -        @ <tr>
   933         -
   934         -        if( j<c.aEdit[i+1] ){
   935         -          copylimline(linebuf, &c.aFrom[iFrom+j], collim);
   936         -          @ <td class="changed lineno">%d(iFrom+j+1)</td>
   937         -          @ <td class="changed srcline">%h(linebuf)</td>
   938         -        }else{
   939         -          @ <td colspan="2" class="changedvoid"/>
   940         -        }
   941         -
   942         -        @ <td class="changed">|</td>
   943         -
   944         -        if( j<c.aEdit[i+2] ){
   945         -          copylimline(linebuf, &c.aTo[iTo+j], collim);
   946         -          @ <td class="changed lineno">%d(iTo+j+1)</td>
   947         -          @ <td class="changed srcline">%h(linebuf)</td>
   948         -        }else{
   949         -          @ <td colspan="2" class="changedvoid"/>
   950         -        }
   951         -
   952         -        @ </tr>
   953         -      }
   954         -      iFrom+=c.aEdit[i+1];
   955         -      iTo+=c.aEdit[i+2];
   956         -    }else{
   957         -
   958         -      /* Process deleted lines */
   959         -      for( j=0; j<c.aEdit[i+1]; j++ ){
   960         -        @ <tr>
   961         -
   962         -        copylimline(linebuf, &c.aFrom[iFrom+j], collim);
   963         -        @ <td class="removed lineno">%d(iFrom+j+1)</td>
   964         -        @ <td class="removed srcline">%h(linebuf)</td>
   965         -        @ <td>&lt;</td>
   966         -        @ <td colspan="2" class="removedvoid"/>
   967         -        @ </tr>
   968         -      }
   969         -      iFrom+=c.aEdit[i+1];
   970         -
   971         -      /* Process inserted lines */
   972         -      for( j=0; j<c.aEdit[i+2]; j++ ){
   973         -        @ <tr>
   974         -        @ <td colspan="2" class="addedvoid"/>
   975         -        @ <td>&gt;</td>
   976         -        copylimline(linebuf, &c.aTo[iTo+j], collim);
   977         -        @ <td class="added lineno">%d(iTo+j+1)</td>
   978         -        @ <td class="added srcline">%h(linebuf)</td>
   979         -        @ </tr>
   980         -      }
   981         -      iTo+=c.aEdit[i+2];
   982         -    }
   983         -
   984         -    i+=3;
   985         -  }
   986         -
   987         -  free(linebuf);
   988         -  free(c.aFrom);
   989         -  free(c.aTo);
   990         -  free(c.aEdit);
   991         -  return 1;
   992         -}
   993         -
   994    950   
   995    951   /*
   996    952   ** COMMAND: test-rawdiff
   997    953   */
   998    954   void test_rawdiff_cmd(void){
   999    955     Blob a, b;
  1000    956     int r;
................................................................................
  1017    973   /*
  1018    974   ** Process diff-related command-line options and return an appropriate
  1019    975   ** "diffFlags" integer.  
  1020    976   **
  1021    977   **   --side-by-side|-y      Side-by-side diff.     DIFF_SIDEBYSIDE
  1022    978   **   --context|-c N         N lines of context.    DIFF_CONTEXT_MASK
  1023    979   **   --width|-W N           N character lines.     DIFF_WIDTH_MASK
          980  +**   --html                 Format for HTML        DIFF_HTML
          981  +**   --linenum|-n           Show line numbers      DIFF_LINENO
  1024    982   */
  1025    983   int diff_options(void){
  1026    984     int diffFlags = 0;
  1027    985     const char *z;
  1028    986     int f;
  1029    987     if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
  1030    988     if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
................................................................................
  1032    990       diffFlags |= f;
  1033    991     }
  1034    992     if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
  1035    993       f *= DIFF_CONTEXT_MASK+1;
  1036    994       if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
  1037    995       diffFlags |= f;
  1038    996     }
          997  +  if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
          998  +  if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
  1039    999     return diffFlags;
  1040   1000   }
  1041   1001   
  1042   1002   /*
  1043   1003   ** COMMAND: test-udiff
  1044   1004   **
  1045   1005   ** Print the difference between two files.  The usual diff options apply.

Changes to src/info.c.

   249    249     }
   250    250   }
   251    251   
   252    252   
   253    253   /*
   254    254   ** Append the difference between two RIDs to the output
   255    255   */
   256         -static void append_diff(const char *zFrom, const char *zTo){
          256  +static void append_diff(const char *zFrom, const char *zTo, int diffFlags){
   257    257     int fromid;
   258    258     int toid;
   259    259     Blob from, to, out;
   260    260     if( zFrom ){
   261    261       fromid = uuid_to_rid(zFrom, 0);
   262    262       content_get(fromid, &from);
   263    263     }else{
................................................................................
   266    266     if( zTo ){
   267    267       toid = uuid_to_rid(zTo, 0);
   268    268       content_get(toid, &to);
   269    269     }else{
   270    270       blob_zero(&to);
   271    271     }
   272    272     blob_zero(&out);
   273         -  text_diff(&from, &to, &out, DIFF_IGNORE_EOLWS | 5);
   274         -  @ %h(blob_str(&out))
          273  +  if( diffFlags & DIFF_SIDEBYSIDE ){
          274  +    text_diff(&from, &to, &out, diffFlags | DIFF_HTML);
          275  +    @ <div class="sbsdiff">
          276  +    @ %s(blob_str(&out))
          277  +    @ </div>
          278  +  }else{
          279  +    text_diff(&from, &to, &out, diffFlags | DIFF_LINENO | DIFF_HTML);
          280  +    @ <div class="udiff">
          281  +    @ %s(blob_str(&out))
          282  +    @ </div>
          283  +  }
   275    284     blob_reset(&from);
   276    285     blob_reset(&to);
   277    286     blob_reset(&out);  
   278    287   }
   279    288   
   280         -
   281         -/*
   282         -** Write the difference between two RIDs to the output
   283         -*/
   284         -static void generate_sbsdiff(const char *zFrom, const char *zTo){
   285         -  int fromid;
   286         -  int toid;
   287         -  Blob from, to;
   288         -  if( zFrom ){
   289         -    fromid = uuid_to_rid(zFrom, 0);
   290         -    content_get(fromid, &from);
   291         -  }else{
   292         -    blob_zero(&from);
   293         -  }
   294         -  if( zTo ){
   295         -    toid = uuid_to_rid(zTo, 0);
   296         -    content_get(toid, &to);
   297         -  }else{
   298         -    blob_zero(&to);
   299         -  }
   300         -  @ <table class="sbsdiff">
   301         -  @ <tr><th colspan="2" class="diffhdr">Old (%S(zFrom))</th><th/>
   302         -  @ <th colspan="2" class="diffhdr">New (%S(zTo))</th></tr>
   303         -  html_sbsdiff(&from, &to, 5, 1);
   304         -  @ </table>
   305         -  blob_reset(&from);
   306         -  blob_reset(&to);
   307         -}
   308         -
   309    289   
   310    290   /*
   311    291   ** Write a line of web-page output that shows changes that have occurred 
   312    292   ** to a file between two check-ins.
   313    293   */
   314    294   static void append_file_change_line(
   315    295     const char *zName,    /* Name of the file that has changed */
   316    296     const char *zOld,     /* blob.uuid before change.  NULL for added files */
   317    297     const char *zNew,     /* blob.uuid after change.  NULL for deletes */
   318    298     const char *zOldName, /* Prior name.  NULL if no name change. */
   319         -  int showDiff,         /* Show edit diffs if true */
   320         -  int sideBySide,       /* Show diffs side-by-side */
          299  +  int diffFlags,        /* Flags for text_diff().  Zero to omit diffs */
   321    300     int mperm             /* executable or symlink permission for zNew */
   322    301   ){
   323    302     if( !g.perm.History ){
   324    303       if( zNew==0 ){
   325    304         @ <p>Deleted %h(zName)</p>
   326    305       }else if( zOld==0 ){
   327    306         @ <p>Added %h(zName)</p>
................................................................................
   329    308         @ <p>Name change from %h(zOldName) to %h(zName)
   330    309       }else if( fossil_strcmp(zNew, zOld)==0 ){
   331    310         @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared")
   332    311         @  for %h(zName)</p>
   333    312       }else{
   334    313         @ <p>Changes to %h(zName)</p>
   335    314       }
   336         -    if( showDiff ){
   337         -      if( sideBySide ){
   338         -        generate_sbsdiff(zOld, zNew);
   339         -      }else{
   340         -        @ <blockquote><pre>
   341         -        append_diff(zOld, zNew);
   342         -        @ </pre></blockquote>
   343         -      }
          315  +    if( diffFlags ){
          316  +      @ <pre style="white-space:pre;">
          317  +      append_diff(zOld, zNew, diffFlags);
          318  +      @ </pre>
   344    319       }
   345    320     }else{
   346    321       if( zOld && zNew ){
   347    322         if( fossil_strcmp(zOld, zNew)!=0 ){
   348    323           @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   349    324           @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
   350    325           @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a>
................................................................................
   359    334       }else if( zOld ){
   360    335         @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   361    336         @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
   362    337       }else{
   363    338         @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   364    339         @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a>
   365    340       }
   366         -    if( showDiff ){
   367         -      if( sideBySide ){
   368         -        generate_sbsdiff(zOld, zNew);
   369         -      }else{
   370         -        @ <blockquote><pre>
   371         -        append_diff(zOld, zNew);
   372         -        @ </pre></blockquote>
   373         -      }
          341  +    if( diffFlags ){
          342  +      @ <pre style="white-space:pre;">
          343  +      append_diff(zOld, zNew, diffFlags);
          344  +      @ </pre>
   374    345       }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
   375    346         @ &nbsp;&nbsp;
   376    347         @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
   377    348       }
   378    349       @ </p>
   379    350     }
   380    351   }
          352  +
          353  +/*
          354  +** Construct an appropriate diffFlag for text_diff() based on query
          355  +** parameters and the to boolean arguments.
          356  +*/
          357  +static int construct_diff_flags(int showDiff, int sideBySide){
          358  +  int diffFlags;
          359  +  if( showDiff==0 ){
          360  +    diffFlags = 0;  /* Zero means do not show any diff */
          361  +  }else{
          362  +    int x;
          363  +    if( sideBySide ){
          364  +      diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS;
          365  +
          366  +      /* "dw" query parameter determines width of each column */
          367  +      x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
          368  +      if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
          369  +      diffFlags += x;
          370  +    }else{
          371  +      diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS;
          372  +    }
          373  +
          374  +    /* "dc" query parameter determines lines of context */
          375  +    x = atoi(PD("dc","7"));
          376  +    if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK;
          377  +    diffFlags += x;
          378  +  }
          379  +  return diffFlags;
          380  +}
   381    381   
   382    382   
   383    383   /*
   384    384   ** WEBPAGE: vinfo
   385    385   ** WEBPAGE: ci
   386    386   ** URL:  /ci?name=RID|ARTIFACTID
   387    387   **
................................................................................
   395    395   ** shown, without diffs.  This behavior is inverted if the
   396    396   ** "show-version-diffs" setting is turned on.
   397    397   */
   398    398   void ci_page(void){
   399    399     Stmt q;
   400    400     int rid;
   401    401     int isLeaf;
   402         -  int showDiff;
   403         -  int sideBySide;
          402  +  int showDiff;        /* True to show diffs */
          403  +  int sideBySide;      /* True for side-by-side diffs */
          404  +  int diffFlags;       /* Flag parameter for text_diff() */
   404    405     const char *zName;   /* Name of the checkin to be displayed */
   405    406     const char *zUuid;   /* UUID of zName */
   406    407     const char *zParent; /* UUID of the parent checkin (if any) */
   407    408   
   408    409     login_check_credentials();
   409    410     if( !g.perm.Read ){ login_needed(); return; }
   410    411     zName = P("name");
................................................................................
   593    594          "       (SELECT uuid FROM blob WHERE rid=mlink.fid),"
   594    595          "       (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)"
   595    596          "  FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
   596    597          " WHERE mlink.mid=%d"
   597    598          " ORDER BY name /*sort*/",
   598    599          rid
   599    600       );
          601  +    diffFlags = construct_diff_flags(showDiff, sideBySide);
   600    602       while( db_step(&q)==SQLITE_ROW ){
   601    603         const char *zName = db_column_text(&q,0);
   602    604         int mperm = db_column_int(&q, 1);
   603    605         const char *zOld = db_column_text(&q,2);
   604    606         const char *zNew = db_column_text(&q,3);
   605    607         const char *zOldName = db_column_text(&q, 4);
   606         -      append_file_change_line(zName, zOld, zNew, zOldName, showDiff,
   607         -            sideBySide, mperm);
          608  +      append_file_change_line(zName, zOld, zNew, zOldName, diffFlags, mperm);
   608    609       }
   609    610       db_finalize(&q);
   610    611     }
   611    612     style_footer();
   612    613   }
   613    614   
   614    615   /*
................................................................................
   758    759   **
   759    760   ** Show all differences between two checkins.  
   760    761   */
   761    762   void vdiff_page(void){
   762    763     int ridFrom, ridTo;
   763    764     int showDetail = 0;
   764    765     int sideBySide = 0;
          766  +  int diffFlags = 0;
   765    767     Manifest *pFrom, *pTo;
   766    768     ManifestFile *pFileFrom, *pFileTo;
   767    769   
   768    770     login_check_credentials();
   769    771     if( !g.perm.Read ){ login_needed(); return; }
   770    772     login_anonymous_available();
   771    773   
   772    774     pFrom = vdiff_parse_manifest("from", &ridFrom);
   773    775     if( pFrom==0 ) return;
   774    776     pTo = vdiff_parse_manifest("to", &ridTo);
   775    777     if( pTo==0 ) return;
   776         -  showDetail = atoi(PD("detail","0"));
   777    778     sideBySide = atoi(PD("sbs","1"));
          779  +  showDetail = atoi(PD("detail","0"));
          780  +  if( !showDetail && sideBySide ) showDetail = 1;
   778    781     if( !sideBySide ){
   779    782       style_submenu_element("Side-by-side Diff", "sbsdiff",
   780    783                             "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1",
   781    784                             g.zTop, P("from"), P("to"), showDetail);
   782    785     }else{
   783    786       style_submenu_element("Unified Diff", "udiff",
   784    787                             "%s/vdiff?from=%T&to=%T&detail=%d&sbs=0",
................................................................................
   791    794     checkin_description(ridTo);
   792    795     @ </blockquote><hr /><p>
   793    796   
   794    797     manifest_file_rewind(pFrom);
   795    798     pFileFrom = manifest_file_next(pFrom, 0);
   796    799     manifest_file_rewind(pTo);
   797    800     pFileTo = manifest_file_next(pTo, 0);
          801  +  diffFlags = construct_diff_flags(showDetail, sideBySide);
   798    802     while( pFileFrom || pFileTo ){
   799    803       int cmp;
   800    804       if( pFileFrom==0 ){
   801    805         cmp = +1;
   802    806       }else if( pFileTo==0 ){
   803    807         cmp = -1;
   804    808       }else{
   805    809         cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
   806    810       }
   807    811       if( cmp<0 ){
   808    812         append_file_change_line(pFileFrom->zName, 
   809         -                              pFileFrom->zUuid, 0, 0, 0, 0, 0);
          813  +                              pFileFrom->zUuid, 0, 0, 0, 0);
   810    814         pFileFrom = manifest_file_next(pFrom, 0);
   811    815       }else if( cmp>0 ){
   812    816         append_file_change_line(pFileTo->zName, 
   813         -                              0, pFileTo->zUuid, 0, 0, 0,
          817  +                              0, pFileTo->zUuid, 0, 0,
   814    818                                 manifest_file_mperm(pFileTo));
   815    819         pFileTo = manifest_file_next(pTo, 0);
   816    820       }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
   817    821         /* No changes */
   818    822         pFileFrom = manifest_file_next(pFrom, 0);
   819    823         pFileTo = manifest_file_next(pTo, 0);
   820    824       }else{
   821    825         append_file_change_line(pFileFrom->zName, 
   822    826                                 pFileFrom->zUuid,
   823         -                              pFileTo->zUuid, 0, showDetail, sideBySide,
          827  +                              pFileTo->zUuid, 0, diffFlags,
   824    828                                 manifest_file_mperm(pFileTo));
   825    829         pFileFrom = manifest_file_next(pFrom, 0);
   826    830         pFileTo = manifest_file_next(pTo, 0);
   827    831       }
   828    832     }
   829    833     manifest_destroy(pFrom);
   830    834     manifest_destroy(pTo);
................................................................................
  1067   1071   void diff_page(void){
  1068   1072     int v1, v2;
  1069   1073     int isPatch;
  1070   1074     int sideBySide;
  1071   1075     Blob c1, c2, diff, *pOut;
  1072   1076     char *zV1;
  1073   1077     char *zV2;
         1078  +  int diffFlags;
  1074   1079   
  1075   1080     login_check_credentials();
  1076   1081     if( !g.perm.Read ){ login_needed(); return; }
  1077   1082     v1 = name_to_rid_www("v1");
  1078   1083     v2 = name_to_rid_www("v2");
  1079   1084     if( v1==0 || v2==0 ) fossil_redirect_home();
  1080   1085     sideBySide = atoi(PD("sbs","1"));
  1081   1086     zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
  1082   1087     zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
  1083   1088     isPatch = P("patch")!=0;
  1084   1089     if( isPatch ){
  1085   1090       pOut = cgi_output_blob();
  1086   1091       cgi_set_content_type("text/plain");
         1092  +    diffFlags = 4;
  1087   1093     }else{
  1088   1094       blob_zero(&diff);
  1089   1095       pOut = &diff;
         1096  +    if( sideBySide ){
         1097  +      diffFlags = DIFF_IGNORE_EOLWS | DIFF_SIDEBYSIDE | 7;
         1098  +    }else{
         1099  +      diffFlags = DIFF_IGNORE_EOLWS | 7;
         1100  +    }
  1090   1101     }
  1091         -  if( !sideBySide || isPatch ){
  1092         -    content_get(v1, &c1);
  1093         -    content_get(v2, &c2);
  1094         -    text_diff(&c1, &c2, pOut, 4 | 0);
  1095         -    blob_reset(&c1);
  1096         -    blob_reset(&c2);
  1097         -  }
         1102  +  content_get(v1, &c1);
         1103  +  content_get(v2, &c2);
         1104  +  text_diff(&c1, &c2, pOut, diffFlags);
         1105  +  blob_reset(&c1);
         1106  +  blob_reset(&c2);
  1098   1107     if( !isPatch ){
  1099   1108       style_header("Diff");
  1100   1109       style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
  1101   1110                             g.zTop, P("v1"), P("v2"));
  1102   1111       if( !sideBySide ){
  1103   1112         style_submenu_element("Side-by-side Diff", "sbsdiff",
  1104   1113                               "%s/fdiff?v1=%T&v2=%T&sbs=1",
................................................................................
  1111   1120   
  1112   1121       @ <h2>Differences From
  1113   1122       @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2>
  1114   1123       object_description(v1, 0, 0);
  1115   1124       @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2>
  1116   1125       object_description(v2, 0, 0);
  1117   1126       @ <hr />
  1118         -    if( sideBySide ){
  1119         -      generate_sbsdiff(zV1, zV2);
  1120         -    }else{
  1121         -      @ <blockquote><pre>
  1122         -      @ %h(blob_str(&diff))
  1123         -      @ </pre></blockquote>
  1124         -    }
         1127  +    @ <pre style="while-space:pre;">
         1128  +    @ %h(blob_str(&diff))
         1129  +    @ </pre>
  1125   1130       blob_reset(&diff);
  1126   1131       style_footer();
  1127   1132     }
  1128   1133   }
  1129   1134   
  1130   1135   /*
  1131   1136   ** WEBPAGE: raw

Changes to src/manifest.c.

  1243   1243       }
  1244   1244     }
  1245   1245     if( p->zBaseline==0 ) return 0;
  1246   1246     fetch_baseline(p, 1);
  1247   1247     pBase = p->pBaseline;
  1248   1248     if( pBase==0 ) return 0;
  1249   1249     for(i=0; i<pBase->nFile; i++){
  1250         -    if( fossil_stricmp(zName, p->aFile[i].zName)==0 ){
  1251         -      return &p->aFile[i];
         1250  +    if( fossil_stricmp(zName, pBase->aFile[i].zName)==0 ){
         1251  +      return &pBase->aFile[i];
  1252   1252       }
  1253   1253     }
  1254   1254     return 0;
  1255   1255   }
  1256   1256   
  1257   1257   /*
  1258   1258   ** Add mlink table entries associated with manifest cid, pChild.  The

Changes to src/skins.c.

   151    151   @ }
   152    152   @ 
   153    153   @ /* The label/value pairs on (for example) the vinfo page */
   154    154   @ table.label-value th {
   155    155   @   vertical-align: top;
   156    156   @   text-align: right;
   157    157   @   padding: 0.2ex 2ex;
   158         -@ }
   159         -@
   160         -@ /* Side-by-side diff */
   161         -@ table.sbsdiff {
   162         -@   background-color: white;
   163         -@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
   164         -@   font-size: 8pt;
   165         -@   border-collapse:collapse;
   166         -@   white-space: pre;
   167         -@   width: 98%;
   168         -@   border: 1px #000 dashed;
   169         -@ }
   170         -@
   171         -@ table.sbsdiff th.diffhdr {
   172         -@   border-bottom: dotted;
   173         -@   border-width: 1px;
   174         -@ }
   175         -@
   176         -@ table.sbsdiff tr td {
   177         -@   white-space: pre;
   178         -@   padding-left: 3px;
   179         -@   padding-right: 3px;
   180         -@   margin: 0px;
   181         -@ }
   182         -@
   183         -@ table.sbsdiff tr td.lineno {
   184         -@   text-align: right;
   185         -@ }
   186         -@
   187         -@ table.sbsdiff tr td.meta {
   188         -@   color: white;
   189         -@   background-color: rgb(20, 20, 20);
   190         -@   text-align: center;
   191         -@ }
   192         -@
   193         -@ table.sbsdiff tr td.added {
   194         -@   background-color: rgb(230, 230, 230);
   195         -@ }
   196         -@
   197         -@ table.sbsdiff tr td.removed {
   198         -@   background-color: rgb(200, 200, 200);
   199         -@ }
   200         -@
   201         -@ table.sbsdiff tr td.changed {
   202         -@   background-color: rgb(220, 220, 220);
   203    158   @ }');
   204    159   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   205    160   @ <head>
   206    161   @ <title>$<project_name>: $<title></title>
   207    162   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   208    163   @       href="$home/timeline.rss">
   209    164   @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
................................................................................
   398    353   @ }
   399    354   @ 
   400    355   @ /* The label/value pairs on (for example) the ci page */
   401    356   @ table.label-value th {
   402    357   @   vertical-align: top;
   403    358   @   text-align: right;
   404    359   @   padding: 0.2ex 2ex;
   405         -@ }
   406         -@
   407         -@ /* Side-by-side diff */
   408         -@ table.sbsdiff {
   409         -@   background-color: #ffffc5;
   410         -@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
   411         -@   font-size: 8pt;
   412         -@   border-collapse:collapse;
   413         -@   white-space: pre;
   414         -@   width: 98%;
   415         -@   border: 1px #000 dashed;
   416         -@ }
   417         -@
   418         -@ table.sbsdiff th.diffhdr {
   419         -@   border-bottom: dotted;
   420         -@   border-width: 1px;
   421         -@ }
   422         -@
   423         -@ table.sbsdiff tr td {
   424         -@   white-space: pre;
   425         -@   padding-left: 3px;
   426         -@   padding-right: 3px;
   427         -@   margin: 0px;
   428         -@ }
   429         -@
   430         -@ table.sbsdiff tr td.lineno {
   431         -@   text-align: right;
   432         -@ }
   433         -@
   434         -@ table.sbsdiff tr td.meta {
   435         -@   background-color: #a09048;
   436         -@   text-align: center;
   437         -@ }
   438         -@
   439         -@ table.sbsdiff tr td.added {
   440         -@   background-color: rgb(210, 210, 100);
   441         -@ }
   442         -@
   443         -@ table.sbsdiff tr td.removed {
   444         -@   background-color: rgb(190, 200, 110);
   445         -@ }
   446         -@
   447         -@ table.sbsdiff tr td.changed {
   448         -@   background-color: rgb(200, 210, 120);
   449    360   @ }');
   450    361   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   451    362   @ <head>
   452    363   @ <title>$<project_name>: $<title></title>
   453    364   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   454    365   @       href="$home/timeline.rss">
   455    366   @ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
................................................................................
   678    589   @ }
   679    590   @ 
   680    591   @ /* The label/value pairs on (for example) the ci page */
   681    592   @ table.label-value th {
   682    593   @   vertical-align: top;
   683    594   @   text-align: right;
   684    595   @   padding: 0.2ex 2ex;
   685         -@ }
   686         -@
   687         -@ /* Side-by-side diff */
   688         -@ table.sbsdiff {
   689         -@   background-color: white;
   690         -@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
   691         -@   font-size: 6pt;
   692         -@   border-collapse:collapse;
   693         -@   white-space: pre;
   694         -@   width: 98%;
   695         -@   border: 1px #000 dashed;
   696         -@ }
   697         -@
   698         -@ table.sbsdiff th.diffhdr {
   699         -@   border-bottom: dotted;
   700         -@   border-width: 1px;
   701         -@ }
   702         -@
   703         -@ table.sbsdiff tr td {
   704         -@   white-space: pre;
   705         -@   padding-left: 3px;
   706         -@   padding-right: 3px;
   707         -@   margin: 0px;
   708         -@ }
   709         -@
   710         -@ table.sbsdiff tr td.lineno {
   711         -@   text-align: right;
   712         -@ }
   713         -@
   714         -@ table.sbsdiff tr td.meta {
   715         -@   color: white;
   716         -@   background-color: black;
   717         -@   text-align: center;
   718         -@ }
   719         -@
   720         -@ table.sbsdiff tr td.added {
   721         -@   background-color: white;
   722         -@ }
   723         -@
   724         -@ table.sbsdiff tr td.removed {
   725         -@   background-color: white;
   726         -@   text-decoration: line-through;
   727         -@ }
   728         -@
   729         -@ table.sbsdiff tr td.changed {
   730         -@   background-color: white;
   731    596   @ }');
   732    597   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   733    598   @ <head>
   734    599   @ <title>$<project_name>: $<title></title>
   735    600   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   736    601   @       href="$home/timeline.rss">
   737    602   @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
................................................................................
  1022    887   @ 
  1023    888   @ table.report tr td {
  1024    889   @   padding: 3px 5px;
  1025    890   @ }
  1026    891   @ 
  1027    892   @ textarea {
  1028    893   @   font-size: 1em;
  1029         -@ }
  1030         -@
  1031         -@ /* Side-by-side diff */
  1032         -@ table.sbsdiff {
  1033         -@   background-color: white;
  1034         -@   font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
  1035         -@   font-size: 6pt;
  1036         -@   border-collapse:collapse;
  1037         -@   width: 98%;
  1038         -@   border: 1px #000 dashed;
  1039         -@   margin-left: auto;
  1040         -@   margin-right: auto;
  1041         -@ }
  1042         -@
  1043         -@ table.sbsdiff th.diffhdr {
  1044         -@   border-bottom: dotted;
  1045         -@   border-width: 1px;
  1046         -@ }
  1047         -@
  1048         -@ table.sbsdiff tr td {
  1049         -@   padding-left: 3px;
  1050         -@   padding-right: 3px;
  1051         -@   margin: 0px;
  1052         -@   vertical-align: top;
  1053         -@   white-space: pre-wrap;
  1054         -@ }
  1055         -@
  1056         -@ table.sbsdiff tr td.lineno {
  1057         -@   text-align: right;
  1058         -@   /* border-bottom: 1px solid rgb(220, 220, 220); */
  1059         -@ }
  1060         -@
  1061         -@ table.sbsdiff tr td.srcline {
  1062         -@   /* max-width: 400px; */
  1063         -@   /* Note: May partially hide long lines without whitespaces */
  1064         -@   /* overflow: hidden; */
  1065         -@   /* border-bottom: 1px solid rgb(220, 220, 220); */
  1066         -@ }
  1067         -@
  1068         -@ table.sbsdiff tr td.meta {
  1069         -@   background-color: rgb(170, 160, 255);
  1070         -@   padding-top: 0.25em;
  1071         -@   padding-bottom: 0.25em;
  1072         -@   text-align: center;
  1073         -@   -moz-border-radius: 5px;
  1074         -@   -moz-border-radius: 5px;
  1075         -@   -webkit-border-radius: 5px;
  1076         -@   -webkit-border-radius: 5px;
  1077         -@   -border-radius: 5px;
  1078         -@   -border-radius: 5px;
  1079         -@   border-radius: 5px;
  1080         -@   border-radius: 5px;
  1081         -@ }
  1082         -@
  1083         -@ table.sbsdiff tr td.added {
  1084         -@   background-color: rgb(180, 250, 180);
  1085         -@   /* border-bottom: 1px solid rgb(160, 230, 160); */
  1086         -@ }
  1087         -@
  1088         -@ table.sbsdiff tr td.removed {
  1089         -@   background-color: rgb(250, 130, 130);
  1090         -@   /* border-bottom: 1px solid rgb(230, 110, 110); */
  1091         -@ }
  1092         -@
  1093         -@ table.sbsdiff tr td.changed {
  1094         -@   background-color: rgb(210, 210, 200);
  1095         -@   /* border-bottom: 1px solid rgb(190, 190, 180); */
  1096    894   @ }');
  1097    895   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
  1098    896   @ <head>
  1099    897   @ <title>$<project_name>: $<title></title>
  1100    898   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
  1101    899   @       href="$home/timeline.rss">
  1102    900   @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"

Changes to src/style.c.

   396    396   @
   397    397   @ /* The label/value pairs on (for example) the ci page */
   398    398   @ table.label-value th {
   399    399   @   vertical-align: top;
   400    400   @   text-align: right;
   401    401   @   padding: 0.2ex 2ex;
   402    402   @ }
   403         -@
   404         -@ /* Side-by-side diff */
   405         -@ table.sbsdiff {
   406         -@   background-color: white;
   407         -@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
   408         -@   font-size: 8pt;
   409         -@   border-collapse:collapse;
   410         -@   white-space: pre;
   411         -@   width: 98%;
   412         -@   border: 1px #000 dashed;
   413         -@   margin-left: auto;
   414         -@   margin-right: auto;
   415         -@ }
   416         -@
   417         -@ table.sbsdiff th.diffhdr {
   418         -@   border-bottom: dotted;
   419         -@   border-width: 1px;
   420         -@ }
   421         -@
   422         -@ table.sbsdiff tr td {
   423         -@   white-space: pre;
   424         -@   padding-left: 3px;
   425         -@   padding-right: 3px;
   426         -@   margin: 0px;
   427         -@   vertical-align: top;
   428         -@ }
   429         -@
   430         -@ table.sbsdiff tr td.lineno {
   431         -@   text-align: right;
   432         -@ }
   433         -@
   434         -@ table.sbsdiff tr td.srcline {
   435         -@ }
   436         -@
   437         -@ table.sbsdiff tr td.meta {
   438         -@   background-color: rgb(170, 160, 255);
   439         -@   text-align: center;
   440         -@ }
   441         -@
   442         -@ table.sbsdiff tr td.added {
   443         -@   background-color: rgb(180, 250, 180);
   444         -@ }
   445         -@ table.sbsdiff tr td.addedvoid {
   446         -@   background-color: rgb(190, 190, 180);
   447         -@ }
   448         -@
   449         -@ table.sbsdiff tr td.removed {
   450         -@   background-color: rgb(250, 130, 130);
   451         -@ }
   452         -@ table.sbsdiff tr td.removedvoid {
   453         -@   background-color: rgb(190, 190, 180);
   454         -@ }
   455         -@
   456         -@ table.sbsdiff tr td.changed {
   457         -@   background-color: rgb(210, 210, 200);
   458         -@ }
   459         -@ table.sbsdiff tr td.changedvoid {
   460         -@   background-color: rgb(190, 190, 180);
   461         -@ }
   462         -@
   463    403   ;
   464    404   
   465    405   
   466    406   /* The following table contains bits of default CSS that must
   467    407   ** be included if they are not found in the application-defined
   468    408   ** CSS.
   469    409   */
................................................................................
   809    749       @   color: red;
   810    750     },
   811    751     { "ul.filelist",
   812    752       "List of files in a timeline",
   813    753       @   margin-top: 3px;
   814    754       @   line-height: 100%;
   815    755     },
          756  +  { "div.sbsdiff",
          757  +    "side-by-side diff display",
          758  +    @   font-family: monospace;
          759  +    @   font-size: smaller;
          760  +    @   white-space: pre;
          761  +  },
          762  +  { "div.udiff",
          763  +    "context diff display",
          764  +    @   font-family: monospace;
          765  +    @   white-space: pre;
          766  +  },
          767  +  { "span.diffchng",
          768  +    "changes in a diff",
          769  +    @   background-color: #ffffc8;
          770  +  },
          771  +  { "span.diffadd",
          772  +    "added code in a diff",
          773  +    @   background-color: #e0ffe0;
          774  +  },
          775  +  { "span.diffrm",
          776  +    "deleted in a diff",
          777  +    @   background-color: #ffe0e0;
          778  +  },
          779  +  { "span.diffhr",
          780  +    "suppressed lines in a diff",
          781  +    @   color: #0000ff;
          782  +  },
          783  +  { "span.diffln",
          784  +    "line nubmers in a diff",
          785  +    @   color: #a0a0a0;
          786  +  },
   816    787     { 0,
   817    788       0,
   818    789       0
   819    790     }
   820    791   };
   821    792   
   822    793   /*