Index: src/diff.c
==================================================================
--- src/diff.c
+++ src/diff.c
@@ -25,15 +25,18 @@
 
 #if INTERFACE
 /*
 ** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
 */
-#define DIFF_CONTEXT_MASK  0x0000fff  /* Lines of context.  Default if 0 */
-#define DIFF_WIDTH_MASK    0x00ff000  /* side-by-side column width */
-#define DIFF_IGNORE_EOLWS  0x0100000  /* Ignore end-of-line whitespace */
-#define DIFF_SIDEBYSIDE    0x0200000  /* Generate a side-by-side diff */
-#define DIFF_NEWFILE       0x0400000  /* Missing files are as empty files */
+#define DIFF_CONTEXT_MASK  0x0000ffff  /* Lines of context.  Default if 0 */
+#define DIFF_WIDTH_MASK    0x00ff0000  /* side-by-side column width */
+#define DIFF_IGNORE_EOLWS  0x01000000  /* Ignore end-of-line whitespace */
+#define DIFF_SIDEBYSIDE    0x02000000  /* Generate a side-by-side diff */
+#define DIFF_NEWFILE       0x04000000  /* Missing files are as empty files */
+#define DIFF_INLINE        0x08000000  /* Inline (not side-by-side) diff */
+#define DIFF_HTML          0x10000000  /* Render for HTML */
+#define DIFF_LINENO        0x20000000  /* Show line numbers in context diff */
 
 #endif /* INTERFACE */
 
 /*
 ** Maximum length of a line in a text file.  (8192)
@@ -146,15 +149,49 @@
 }
 
 /*
 ** Append a single line of "diff" output to pOut.
 */
-static void appendDiffLine(Blob *pOut, char *zPrefix, DLine *pLine){
-  blob_append(pOut, zPrefix, 1);
-  blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
+static void appendDiffLine(Blob *pOut, char cPrefix, DLine *pLine, int html){
+  blob_append(pOut, &cPrefix, 1);
+  if( html ){
+    char *zHtml;
+    if( cPrefix=='+' ){
+      blob_append(pOut, "<span class=\"diffadd\">", -1);
+    }else if( cPrefix=='-' ){
+      blob_append(pOut, "<span class=\"diffrm\">", -1);
+    }
+    zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
+    blob_append(pOut, zHtml, -1);
+    fossil_free(zHtml);
+    if( cPrefix!=' ' ){
+      blob_append(pOut, "</span>", -1);
+    }
+  }else{
+    blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
+  }
   blob_append(pOut, "\n", 1);
 }
+
+/*
+** Append line numbers to the context diff output.  Zero or negative numbers
+** are blanks.
+*/
+static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
+  if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
+  if( lnA>0 ){
+    blob_appendf(pOut, "%6d ", lnA);
+  }else{
+    blob_append(pOut, "       ", 7);
+  }
+  if( lnB>0 ){
+    blob_appendf(pOut, "%6d  ", lnB);
+  }else{
+    blob_append(pOut, "        ", 8);
+  }
+  if( html ) blob_append(pOut, "</span>", -1);
+}
 
 /*
 ** Expand the size of aEdit[] array to hold nEdit elements.
 */
 static void expandEdit(DContext *p, int nEdit){
@@ -198,11 +235,17 @@
 
 /*
 ** Given a diff context in which the aEdit[] array has been filled
 ** in, compute a context diff into pOut.
 */
-static void contextDiff(DContext *p, Blob *pOut, int nContext){
+static void contextDiff(
+  DContext *p,      /* The difference */
+  Blob *pOut,       /* Output a context diff to here */
+  int nContext,     /* Number of lines of context */
+  int showLn,       /* Show line numbers */
+  int html          /* Render as HTML */
+){
   DLine *A;     /* Left side of the diff */
   DLine *B;     /* Right side of the diff */  
   int a = 0;    /* Index of next line in A[] */
   int b = 0;    /* Index of next line in B[] */
   int *R;       /* Array of COPY/DELETE/INSERT triples */
@@ -252,40 +295,57 @@
     /*
      * If the patch changes an empty file or results in an empty file,
      * the block header must use 0,0 as position indicator and not 1,0.
      * Otherwise, patch would be confused and may reject the diff.
      */
-    blob_appendf(pOut,"@@ -%d,%d +%d,%d @@\n",
-      na ? a+skip+1 : 0, na,
-      nb ? b+skip+1 : 0, nb);
+    if( showLn ){
+      if( r==0 ){
+        /* Do not show a top divider */
+      }else if( html ){
+        blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
+      }else{
+        blob_appendf(pOut, "%.80c\n", '.');
+      }
+    }else{
+      if( html ) blob_appendf(pOut, "<span class=\"diffln\">");
+      blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
+        na ? a+skip+1 : 0, na,
+        nb ? b+skip+1 : 0, nb);
+      if( html ) blob_appendf(pOut, "</span>");
+      blob_append(pOut, "\n", 1);
+    }
 
     /* Show the initial common area */
     a += skip;
     b += skip;
     m = R[r] - skip;
     for(j=0; j<m; j++){
-      appendDiffLine(pOut, " ", &A[a+j]);
+      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
+      appendDiffLine(pOut, ' ', &A[a+j], html);
     }
     a += m;
     b += m;
 
     /* Show the differences */
     for(i=0; i<nr; i++){
       m = R[r+i*3+1];
       for(j=0; j<m; j++){
-        appendDiffLine(pOut, "-", &A[a+j]);
+        if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
+        appendDiffLine(pOut, '-', &A[a+j], html);
       }
       a += m;
       m = R[r+i*3+2];
       for(j=0; j<m; j++){
-        appendDiffLine(pOut, "+", &B[b+j]);
+        if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
+        appendDiffLine(pOut, '+', &B[b+j], html);
       }
       b += m;
       if( i<nr-1 ){
         m = R[r+i*3+3];
         for(j=0; j<m; j++){
-          appendDiffLine(pOut, " ", &B[b+j]);
+          if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
+          appendDiffLine(pOut, ' ', &B[b+j], html);
         }
         b += m;
         a += m;
       }
     }
@@ -293,57 +353,124 @@
     /* Show the final common area */
     assert( nr==i );
     m = R[r+nr*3];
     if( m>nContext ) m = nContext;
     for(j=0; j<m; j++){
-      appendDiffLine(pOut, " ", &B[b+j]);
+      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
+      appendDiffLine(pOut, ' ', &B[b+j], html);
     }
   }
 }
 
 /*
-** Write a 6-digit line number into the buffer z[].  z[] is guaranteed to
-** have space for at least 7 characters.
+** Status of a single output line
 */
-static void sbsWriteLineno(char *z, int ln){
-  sqlite3_snprintf(7, z, "%6d", ln+1);
-  z[6] = ' ';
-}
+typedef struct SbsLine SbsLine;
+struct SbsLine {
+  char *zLine;             /* The output line under construction */
+  int n;                   /* Index of next unused slot in the zLine[] */
+  int width;               /* Maximum width of a column in the output */
+  unsigned char escHtml;  /* True to escape html characters */
+};
+
+/*
+** Flags for sbsWriteText()
+*/
+#define SBS_NEWLINE  0x0001   /* End with \n\000 */
+#define SBS_PAD      0x0002   /* Pad output to width spaces */
+#define SBS_ENDSPAN  0x0004   /* Write a </span> after text */
 
 /*
 ** Write up to width characters of pLine into z[].  Translate tabs into
-** spaces.  If trunc is true, then append \n\000 after the last character
-** written.
+** spaces.  Add a newline if SBS_NEWLINE is set.  Translate HTML characters
+** if SBS_HTML is set.  Pad the rendering out width bytes if SBS_PAD is set.
 */
-static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){
+static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){
   int n = pLine->h & LENGTH_MASK;
   int i, j;
   const char *zIn = pLine->z;
-  for(i=j=0; i<n && j<width; i++){
+  char *z = &p->zLine[p->n];
+  int w = p->width;
+  if( n>w ) n = w;
+  for(i=j=0; i<n; i++){
     char c = zIn[i];
     if( c=='\t' ){
       z[j++] = ' ';
-      while( (j&7)!=0 && j<width ) z[j++] = ' ';
+      while( (j&7)!=0 && j<n ) z[j++] = ' ';
     }else if( c=='\r' || c=='\f' ){
       z[j++] = ' ';
+    }else if( c=='<' && p->escHtml ){
+      memcpy(&z[j], "&lt;", 4);
+      j += 4;
+    }else if( c=='&' && p->escHtml ){
+      memcpy(&z[j], "&amp;", 5);
+      j += 5;
+    }else if( c=='>' && p->escHtml ){
+      memcpy(&z[j], "&gt;", 4);
+      j += 4;
     }else{
       z[j++] = c;
     }
   }
-  if( trunc ){
+  if( (flags & SBS_ENDSPAN) && p->escHtml ){
+    memcpy(&z[j], "</span>", 7);
+    j += 7;
+  }
+  if( (flags & SBS_PAD)!=0 ){
+    while( i<w ){ i++;  z[j++] = ' '; }
+  }
+  if( flags & SBS_NEWLINE ){
     z[j++] = '\n';
-    z[j] = 0;
   }
-  return j;
+  p->n += j;
+}
+
+/*
+** Append a string to an SbSLine with coding, interpretation, or padding.
+*/
+static void sbsWrite(SbsLine *p, const char *zIn, int nIn){
+  memcpy(p->zLine+p->n, zIn, nIn);
+  p->n += nIn;
+}
+
+/*
+** Append n spaces to the string.
+*/
+static void sbsWriteSpace(SbsLine *p, int n){
+  while( n-- ) p->zLine[p->n++] = ' ';
+}
+
+/*
+** Append a string to the output only if we are rendering HTML.
+*/
+static void sbsWriteHtml(SbsLine *p, const char *zIn){
+  if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn));
+}
+
+/*
+** Write a 6-digit line number followed by a single space onto the line.
+*/
+static void sbsWriteLineno(SbsLine *p, int ln){
+  sbsWriteHtml(p, "<span class=\"diffln\">");
+  sqlite3_snprintf(7, &p->zLine[p->n], "%5d ", ln+1);
+  p->n += 6;
+  sbsWriteHtml(p, "</span>");
+  p->zLine[p->n++] = ' ';
 }
 
 
 /*
 ** Given a diff context in which the aEdit[] array has been filled
 ** in, compute a side-by-side diff into pOut.
 */
-static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
+static void sbsDiff(
+  DContext *p,       /* The computed diff */
+  Blob *pOut,        /* Write the results here */
+  int nContext,      /* Number of lines of context around each change */
+  int width,         /* Width of each column of output */
+  int escHtml        /* True to generate HTML output */
+){
   DLine *A;     /* Left side of the diff */
   DLine *B;     /* Right side of the diff */  
   int a = 0;    /* Index of next line in A[] */
   int b = 0;    /* Index of next line in B[] */
   int *R;       /* Array of COPY/DELETE/INSERT triples */
@@ -352,18 +479,16 @@
   int mxr;      /* Maximum value for r */
   int na, nb;   /* Number of lines shown from A and B */
   int i, j;     /* Loop counters */
   int m, ma, mb;/* Number of lines to output */
   int skip;     /* Number of lines to skip */
-  int mxLine;   /* Length of a line of text */
-  char *zLine;  /* A line of text being formatted */
-  int len;      /* Length of an output line */
+  SbsLine s;    /* Output line buffer */
 
-  mxLine = width*2 + 2*7 + 3 + 1;
-  zLine = fossil_malloc( mxLine + 1 );
-  if( zLine==0 ) return;
-  zLine[mxLine] = 0;
+  s.zLine = fossil_malloc( 10*width + 100 );
+  if( s.zLine==0 ) return;
+  s.width = width;
+  s.escHtml = escHtml;
   A = p->aFrom;
   B = p->aTo;
   R = p->aEdit;
   mxr = p->nEdit;
   while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -400,23 +525,31 @@
     /*
      * If the patch changes an empty file or results in an empty file,
      * the block header must use 0,0 as position indicator and not 1,0.
      * Otherwise, patch would be confused and may reject the diff.
      */
-    if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.');
+    if( r>0 ){
+      if( escHtml ){
+        blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
+                           width*2+16, '.');
+      }else{
+        blob_appendf(pOut, "%.*c\n", width*2+16, '.');
+      }
+    }
 
     /* Show the initial common area */
     a += skip;
     b += skip;
     m = R[r] - skip;
     for(j=0; j<m; j++){
-      memset(zLine, ' ', mxLine);
-      sbsWriteLineno(zLine, a+j);
-      sbsWriteText(&zLine[7], &A[a+j], width, 0);
-      sbsWriteLineno(&zLine[width+10], b+j);
-      len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
-      blob_append(pOut, zLine, len+width+17);
+      s.n = 0;
+      sbsWriteLineno(&s, a+j);
+      sbsWriteText(&s, &A[a+j], SBS_PAD);
+      sbsWrite(&s, "   ", 3);
+      sbsWriteLineno(&s, b+j);
+      sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
+      blob_append(pOut, s.zLine, s.n);
     }
     a += m;
     b += m;
 
     /* Show the differences */
@@ -423,49 +556,53 @@
     for(i=0; i<nr; i++){
       ma = R[r+i*3+1];
       mb = R[r+i*3+2];
       m = ma<mb ? ma : mb;
       for(j=0; j<m; j++){
-        memset(zLine, ' ', mxLine);
-        sbsWriteLineno(zLine, a+j);
-        sbsWriteText(&zLine[7], &A[a+j], width, 0);
-        zLine[width+8] = '|';
-        sbsWriteLineno(&zLine[width+10], b+j);
-        len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
-        blob_append(pOut, zLine, len+width+17);
+        s.n = 0;
+        sbsWriteLineno(&s, a+j);
+        sbsWriteHtml(&s, "<span class=\"diffchng\">");
+        sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
+        sbsWrite(&s, " | ", 3);
+        sbsWriteLineno(&s, b+j);
+        sbsWriteHtml(&s, "<span class=\"diffchng\">");
+        sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
+        blob_append(pOut, s.zLine, s.n);
       }
       a += m;
       b += m;
       ma -= m;
       mb -= m;
       for(j=0; j<ma; j++){
-        memset(zLine, ' ', width+7);
-        sbsWriteLineno(zLine, a+j);
-        sbsWriteText(&zLine[7], &A[a+j], width, 0);
-        zLine[width+8] = '<';
-        zLine[width+9] = '\n';
-        zLine[width+10] = 0;
-        blob_append(pOut, zLine, width+10);
+        s.n = 0;
+        sbsWriteLineno(&s, a+j);
+        sbsWriteHtml(&s, "<span class=\"diffrm\">");
+        sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
+        sbsWrite(&s, " <\n", 3);
+        blob_append(pOut, s.zLine, s.n);
       }
       a += ma;
       for(j=0; j<mb; j++){
-        memset(zLine, ' ', mxLine);
-        zLine[width+8] = '>';
-        sbsWriteLineno(&zLine[width+10], b+j);
-        len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
-        blob_append(pOut, zLine, len+width+17);
+        s.n = 0;
+        sbsWriteSpace(&s, width + 7);
+        sbsWrite(&s, " > ", 3);
+        sbsWriteLineno(&s, b+j);
+        sbsWriteHtml(&s, "<span class=\"diffadd\">");
+        sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
+        blob_append(pOut, s.zLine, s.n);
       }
       b += mb;
       if( i<nr-1 ){
         m = R[r+i*3+3];
         for(j=0; j<m; j++){
-          memset(zLine, ' ', mxLine);
-          sbsWriteLineno(zLine, a+j);
-          sbsWriteText(&zLine[7], &A[a+j], width, 0);
-          sbsWriteLineno(&zLine[width+10], b+j);
-          len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
-          blob_append(pOut, zLine, len+width+17);
+          s.n = 0;
+          sbsWriteLineno(&s, a+j);
+          sbsWriteText(&s, &A[a+j], SBS_PAD);
+          sbsWrite(&s, "   ", 3);
+          sbsWriteLineno(&s, b+j);
+          sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
+          blob_append(pOut, s.zLine, s.n);
         }
         b += m;
         a += m;
       }
     }
@@ -473,19 +610,20 @@
     /* Show the final common area */
     assert( nr==i );
     m = R[r+nr*3];
     if( m>nContext ) m = nContext;
     for(j=0; j<m; j++){
-      memset(zLine, ' ', mxLine);
-      sbsWriteLineno(zLine, a+j);
-      sbsWriteText(&zLine[7], &A[a+j], width, 0);
-      sbsWriteLineno(&zLine[width+10], b+j);
-      len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
-      blob_append(pOut, zLine, len+width+17);
+      s.n = 0;
+      sbsWriteLineno(&s, a+j);
+      sbsWriteText(&s, &A[a+j], SBS_PAD);
+      sbsWrite(&s, "   ", 3);
+      sbsWriteLineno(&s, b+j);
+      sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
+      blob_append(pOut, s.zLine, s.n);
     }
   }
-  free(zLine);
+  free(s.zLine);
 }
 
 /*
 ** Compute the optimal longest common subsequence (LCS) using an
 ** exhaustive search.  This version of the LCS is only used for
@@ -786,15 +924,17 @@
   /* Compute the difference */
   diff_all(&c);
 
   if( pOut ){
     /* Compute a context or side-by-side diff into pOut */
+    int escHtml = (diffFlags & DIFF_HTML)!=0;
     if( diffFlags & DIFF_SIDEBYSIDE ){
       int width = diff_width(diffFlags);
-      sbsDiff(&c, pOut, nContext, width);
+      sbsDiff(&c, pOut, nContext, width, escHtml);
     }else{
-      contextDiff(&c, pOut, nContext);
+      int showLn = (diffFlags & DIFF_LINENO)!=0;
+      contextDiff(&c, pOut, nContext, showLn, escHtml);
     }
     free(c.aFrom);
     free(c.aTo);
     free(c.aEdit);
     return 0;
@@ -805,194 +945,10 @@
     free(c.aFrom);
     free(c.aTo);
     return c.aEdit;
   }
 }
-
-/*
-** Copy a line with a limit. Used for side-by-side diffs to enforce a maximum
-** line length limit.
-*/
-static char *copylimline(char *out, DLine *dl, int lim){
-  int len;
-  len = dl->h & LENGTH_MASK;
-  if( lim && len > lim ){
-    memcpy(out, dl->z, lim-3);
-    memcpy(&out[lim-3], "...", 4);
-  }else{
-    memcpy(out, dl->z, len);
-    out[len] = '\0';
-  }
-  return out;
-}
-
-/*
-** Output table body of a side-by-side diff. Prior to the call, the caller
-** should have output:
-**   <table class="sbsdiff">
-**   <tr><th colspan="2" class="diffhdr">Old title</th><th/>
-**   <th colspan="2" class="diffhdr">New title</th></tr>
-**
-** And after the call, it should output:
-**   </table>
-**
-** Some good reference diffs in the fossil repository for testing:
-** /vdiff?from=080d27a&to=4b0f813&detail=1
-** /vdiff?from=636804745b&to=c1d78e0556&detail=1
-** /vdiff?from=c0b6c28d29&to=25169506b7&detail=1
-** /vdiff?from=e3d022dffa&to=48bcfbd47b&detail=1
-*/
-int html_sbsdiff(
-  Blob *pA_Blob,   /* FROM file */
-  Blob *pB_Blob,   /* TO file */
-  int nContext,    /* Amount of context to unified diff */
-  int ignoreEolWs  /* Ignore whitespace at the end of lines */
-){
-  DContext c;
-  int i;
-  int iFrom, iTo;
-  char *linebuf;
-  int collim=0; /* Currently not settable; allows a column limit for diffs */
-  int allowExp=0; /* Currently not settable; (dis)allow expansion of rows */
-
-  /* Prepare the input files */
-  memset(&c, 0, sizeof(c));
-  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
-                             &c.nFrom, ignoreEolWs);
-  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
-                           &c.nTo, ignoreEolWs);
-  if( c.aFrom==0 || c.aTo==0 ){
-    free(c.aFrom);
-    free(c.aTo);
-    /* Note: This would be generated within a table. */
-    @ <p class="generalError" style="white-space: nowrap">cannot compute
-    @ difference between binary files</p>
-    return 0;
-  }
-
-  collim = collim < 4 ? 0 : collim;
-
-  /* Compute the difference */
-  diff_all(&c);
-
-  linebuf = fossil_malloc(LENGTH_MASK+1);
-  if( !linebuf ){
-    free(c.aFrom);
-    free(c.aTo);
-    free(c.aEdit);
-    return 0;
-  }
-
-  iFrom=iTo=0;
-  i=0;
-  while( i<c.nEdit ){
-    int j;
-    /* Copied lines */
-    for( j=0; j<c.aEdit[i]; j++){
-      /* Hide lines which are copied and are further away from block boundaries
-      ** than nContext lines. For each block with hidden lines, show a row
-      ** notifying the user about the hidden rows.
-      */
-      if( j<nContext || j>c.aEdit[i]-nContext-1 ){
-        @ <tr>
-      }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){
-        @ <tr>
-        @ <td class="meta" colspan="5" style="white-space: nowrap;">
-        @ %d(c.aEdit[i]-2*nContext) hidden lines</td>
-        @ </tr>
-        if( !allowExp )
-           continue;
-        @ <tr style="display:none;">
-      }else{
-        if( !allowExp )
-           continue;
-        @ <tr style="display:none;">
-      }
-
-      copylimline(linebuf, &c.aFrom[iFrom+j], collim);
-      @ <td class="lineno">%d(iFrom+j+1)</td>
-      @ <td class="srcline">%h(linebuf)</td>
-
-      @ <td> </td>
-
-      copylimline(linebuf, &c.aTo[iTo+j], collim);
-      @ <td class="lineno">%d(iTo+j+1)</td>
-      @ <td class="srcline">%h(linebuf)</td>
-
-      @ </tr>
-    }
-    iFrom+=c.aEdit[i];
-    iTo+=c.aEdit[i];
-
-    if( c.aEdit[i+1]!=0 && c.aEdit[i+2]!=0 ){
-      int lim;
-      lim = c.aEdit[i+1] > c.aEdit[i+2] ? c.aEdit[i+1] : c.aEdit[i+2];
-
-      /* Assume changed lines */
-      for( j=0; j<lim; j++ ){
-        @ <tr>
-
-        if( j<c.aEdit[i+1] ){
-          copylimline(linebuf, &c.aFrom[iFrom+j], collim);
-          @ <td class="changed lineno">%d(iFrom+j+1)</td>
-          @ <td class="changed srcline">%h(linebuf)</td>
-        }else{
-          @ <td colspan="2" class="changedvoid"/>
-        }
-
-        @ <td class="changed">|</td>
-
-        if( j<c.aEdit[i+2] ){
-          copylimline(linebuf, &c.aTo[iTo+j], collim);
-          @ <td class="changed lineno">%d(iTo+j+1)</td>
-          @ <td class="changed srcline">%h(linebuf)</td>
-        }else{
-          @ <td colspan="2" class="changedvoid"/>
-        }
-
-        @ </tr>
-      }
-      iFrom+=c.aEdit[i+1];
-      iTo+=c.aEdit[i+2];
-    }else{
-
-      /* Process deleted lines */
-      for( j=0; j<c.aEdit[i+1]; j++ ){
-        @ <tr>
-
-        copylimline(linebuf, &c.aFrom[iFrom+j], collim);
-        @ <td class="removed lineno">%d(iFrom+j+1)</td>
-        @ <td class="removed srcline">%h(linebuf)</td>
-        @ <td>&lt;</td>
-        @ <td colspan="2" class="removedvoid"/>
-        @ </tr>
-      }
-      iFrom+=c.aEdit[i+1];
-
-      /* Process inserted lines */
-      for( j=0; j<c.aEdit[i+2]; j++ ){
-        @ <tr>
-        @ <td colspan="2" class="addedvoid"/>
-        @ <td>&gt;</td>
-        copylimline(linebuf, &c.aTo[iTo+j], collim);
-        @ <td class="added lineno">%d(iTo+j+1)</td>
-        @ <td class="added srcline">%h(linebuf)</td>
-        @ </tr>
-      }
-      iTo+=c.aEdit[i+2];
-    }
-
-    i+=3;
-  }
-
-  free(linebuf);
-  free(c.aFrom);
-  free(c.aTo);
-  free(c.aEdit);
-  return 1;
-}
-
 
 /*
 ** COMMAND: test-rawdiff
 */
 void test_rawdiff_cmd(void){
@@ -1019,10 +975,12 @@
 ** "diffFlags" integer.  
 **
 **   --side-by-side|-y      Side-by-side diff.     DIFF_SIDEBYSIDE
 **   --context|-c N         N lines of context.    DIFF_CONTEXT_MASK
 **   --width|-W N           N character lines.     DIFF_WIDTH_MASK
+**   --html                 Format for HTML        DIFF_HTML
+**   --linenum|-n           Show line numbers      DIFF_LINENO
 */
 int diff_options(void){
   int diffFlags = 0;
   const char *z;
   int f;
@@ -1034,10 +992,12 @@
   if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
     f *= DIFF_CONTEXT_MASK+1;
     if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
     diffFlags |= f;
   }
+  if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
+  if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
   return diffFlags;
 }
 
 /*
 ** COMMAND: test-udiff

Index: src/info.c
==================================================================
--- src/info.c
+++ src/info.c
@@ -251,11 +251,11 @@
 
 
 /*
 ** Append the difference between two RIDs to the output
 */
-static void append_diff(const char *zFrom, const char *zTo){
+static void append_diff(const char *zFrom, const char *zTo, int diffFlags){
   int fromid;
   int toid;
   Blob from, to, out;
   if( zFrom ){
     fromid = uuid_to_rid(zFrom, 0);
@@ -268,46 +268,26 @@
     content_get(toid, &to);
   }else{
     blob_zero(&to);
   }
   blob_zero(&out);
-  text_diff(&from, &to, &out, DIFF_IGNORE_EOLWS | 5);
-  @ %h(blob_str(&out))
+  if( diffFlags & DIFF_SIDEBYSIDE ){
+    text_diff(&from, &to, &out, diffFlags | DIFF_HTML);
+    @ <div class="sbsdiff">
+    @ %s(blob_str(&out))
+    @ </div>
+  }else{
+    text_diff(&from, &to, &out, diffFlags | DIFF_LINENO | DIFF_HTML);
+    @ <div class="udiff">
+    @ %s(blob_str(&out))
+    @ </div>
+  }
   blob_reset(&from);
   blob_reset(&to);
   blob_reset(&out);  
 }
 
-
-/*
-** Write the difference between two RIDs to the output
-*/
-static void generate_sbsdiff(const char *zFrom, const char *zTo){
-  int fromid;
-  int toid;
-  Blob from, to;
-  if( zFrom ){
-    fromid = uuid_to_rid(zFrom, 0);
-    content_get(fromid, &from);
-  }else{
-    blob_zero(&from);
-  }
-  if( zTo ){
-    toid = uuid_to_rid(zTo, 0);
-    content_get(toid, &to);
-  }else{
-    blob_zero(&to);
-  }
-  @ <table class="sbsdiff">
-  @ <tr><th colspan="2" class="diffhdr">Old (%S(zFrom))</th><th/>
-  @ <th colspan="2" class="diffhdr">New (%S(zTo))</th></tr>
-  html_sbsdiff(&from, &to, 5, 1);
-  @ </table>
-  blob_reset(&from);
-  blob_reset(&to);
-}
-
 
 /*
 ** Write a line of web-page output that shows changes that have occurred 
 ** to a file between two check-ins.
 */
@@ -314,12 +294,11 @@
 static void append_file_change_line(
   const char *zName,    /* Name of the file that has changed */
   const char *zOld,     /* blob.uuid before change.  NULL for added files */
   const char *zNew,     /* blob.uuid after change.  NULL for deletes */
   const char *zOldName, /* Prior name.  NULL if no name change. */
-  int showDiff,         /* Show edit diffs if true */
-  int sideBySide,       /* Show diffs side-by-side */
+  int diffFlags,        /* Flags for text_diff().  Zero to omit diffs */
   int mperm             /* executable or symlink permission for zNew */
 ){
   if( !g.perm.History ){
     if( zNew==0 ){
       @ <p>Deleted %h(zName)</p>
@@ -331,18 +310,14 @@
       @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared")
       @  for %h(zName)</p>
     }else{
       @ <p>Changes to %h(zName)</p>
     }
-    if( showDiff ){
-      if( sideBySide ){
-        generate_sbsdiff(zOld, zNew);
-      }else{
-        @ <blockquote><pre>
-        append_diff(zOld, zNew);
-        @ </pre></blockquote>
-      }
+    if( diffFlags ){
+      @ <pre style="white-space:pre;">
+      append_diff(zOld, zNew, diffFlags);
+      @ </pre>
     }
   }else{
     if( zOld && zNew ){
       if( fossil_strcmp(zOld, zNew)!=0 ){
         @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@@ -361,25 +336,50 @@
       @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
     }else{
       @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
       @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a>
     }
-    if( showDiff ){
-      if( sideBySide ){
-        generate_sbsdiff(zOld, zNew);
-      }else{
-        @ <blockquote><pre>
-        append_diff(zOld, zNew);
-        @ </pre></blockquote>
-      }
+    if( diffFlags ){
+      @ <pre style="white-space:pre;">
+      append_diff(zOld, zNew, diffFlags);
+      @ </pre>
     }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
       @ &nbsp;&nbsp;
       @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
     }
     @ </p>
   }
 }
+
+/*
+** Construct an appropriate diffFlag for text_diff() based on query
+** parameters and the to boolean arguments.
+*/
+static int construct_diff_flags(int showDiff, int sideBySide){
+  int diffFlags;
+  if( showDiff==0 ){
+    diffFlags = 0;  /* Zero means do not show any diff */
+  }else{
+    int x;
+    if( sideBySide ){
+      diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS;
+
+      /* "dw" query parameter determines width of each column */
+      x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
+      if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
+      diffFlags += x;
+    }else{
+      diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS;
+    }
+
+    /* "dc" query parameter determines lines of context */
+    x = atoi(PD("dc","7"));
+    if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK;
+    diffFlags += x;
+  }
+  return diffFlags;
+}
 
 
 /*
 ** WEBPAGE: vinfo
 ** WEBPAGE: ci
@@ -397,12 +397,13 @@
 */
 void ci_page(void){
   Stmt q;
   int rid;
   int isLeaf;
-  int showDiff;
-  int sideBySide;
+  int showDiff;        /* True to show diffs */
+  int sideBySide;      /* True for side-by-side diffs */
+  int diffFlags;       /* Flag parameter for text_diff() */
   const char *zName;   /* Name of the checkin to be displayed */
   const char *zUuid;   /* UUID of zName */
   const char *zParent; /* UUID of the parent checkin (if any) */
 
   login_check_credentials();
@@ -595,18 +596,18 @@
        "  FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
        " WHERE mlink.mid=%d"
        " ORDER BY name /*sort*/",
        rid
     );
+    diffFlags = construct_diff_flags(showDiff, sideBySide);
     while( db_step(&q)==SQLITE_ROW ){
       const char *zName = db_column_text(&q,0);
       int mperm = db_column_int(&q, 1);
       const char *zOld = db_column_text(&q,2);
       const char *zNew = db_column_text(&q,3);
       const char *zOldName = db_column_text(&q, 4);
-      append_file_change_line(zName, zOld, zNew, zOldName, showDiff,
-            sideBySide, mperm);
+      append_file_change_line(zName, zOld, zNew, zOldName, diffFlags, mperm);
     }
     db_finalize(&q);
   }
   style_footer();
 }
@@ -760,10 +761,11 @@
 */
 void vdiff_page(void){
   int ridFrom, ridTo;
   int showDetail = 0;
   int sideBySide = 0;
+  int diffFlags = 0;
   Manifest *pFrom, *pTo;
   ManifestFile *pFileFrom, *pFileTo;
 
   login_check_credentials();
   if( !g.perm.Read ){ login_needed(); return; }
@@ -771,12 +773,13 @@
 
   pFrom = vdiff_parse_manifest("from", &ridFrom);
   if( pFrom==0 ) return;
   pTo = vdiff_parse_manifest("to", &ridTo);
   if( pTo==0 ) return;
-  showDetail = atoi(PD("detail","0"));
   sideBySide = atoi(PD("sbs","1"));
+  showDetail = atoi(PD("detail","0"));
+  if( !showDetail && sideBySide ) showDetail = 1;
   if( !sideBySide ){
     style_submenu_element("Side-by-side Diff", "sbsdiff",
                           "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1",
                           g.zTop, P("from"), P("to"), showDetail);
   }else{
@@ -793,10 +796,11 @@
 
   manifest_file_rewind(pFrom);
   pFileFrom = manifest_file_next(pFrom, 0);
   manifest_file_rewind(pTo);
   pFileTo = manifest_file_next(pTo, 0);
+  diffFlags = construct_diff_flags(showDetail, sideBySide);
   while( pFileFrom || pFileTo ){
     int cmp;
     if( pFileFrom==0 ){
       cmp = +1;
     }else if( pFileTo==0 ){
@@ -804,25 +808,25 @@
     }else{
       cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
     }
     if( cmp<0 ){
       append_file_change_line(pFileFrom->zName, 
-                              pFileFrom->zUuid, 0, 0, 0, 0, 0);
+                              pFileFrom->zUuid, 0, 0, 0, 0);
       pFileFrom = manifest_file_next(pFrom, 0);
     }else if( cmp>0 ){
       append_file_change_line(pFileTo->zName, 
-                              0, pFileTo->zUuid, 0, 0, 0,
+                              0, pFileTo->zUuid, 0, 0,
                               manifest_file_mperm(pFileTo));
       pFileTo = manifest_file_next(pTo, 0);
     }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
       /* No changes */
       pFileFrom = manifest_file_next(pFrom, 0);
       pFileTo = manifest_file_next(pTo, 0);
     }else{
       append_file_change_line(pFileFrom->zName, 
                               pFileFrom->zUuid,
-                              pFileTo->zUuid, 0, showDetail, sideBySide,
+                              pFileTo->zUuid, 0, diffFlags,
                               manifest_file_mperm(pFileTo));
       pFileFrom = manifest_file_next(pFrom, 0);
       pFileTo = manifest_file_next(pTo, 0);
     }
   }
@@ -1069,10 +1073,11 @@
   int isPatch;
   int sideBySide;
   Blob c1, c2, diff, *pOut;
   char *zV1;
   char *zV2;
+  int diffFlags;
 
   login_check_credentials();
   if( !g.perm.Read ){ login_needed(); return; }
   v1 = name_to_rid_www("v1");
   v2 = name_to_rid_www("v2");
@@ -1082,21 +1087,25 @@
   zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
   isPatch = P("patch")!=0;
   if( isPatch ){
     pOut = cgi_output_blob();
     cgi_set_content_type("text/plain");
+    diffFlags = 4;
   }else{
     blob_zero(&diff);
     pOut = &diff;
+    if( sideBySide ){
+      diffFlags = DIFF_IGNORE_EOLWS | DIFF_SIDEBYSIDE | 7;
+    }else{
+      diffFlags = DIFF_IGNORE_EOLWS | 7;
+    }
   }
-  if( !sideBySide || isPatch ){
-    content_get(v1, &c1);
-    content_get(v2, &c2);
-    text_diff(&c1, &c2, pOut, 4 | 0);
-    blob_reset(&c1);
-    blob_reset(&c2);
-  }
+  content_get(v1, &c1);
+  content_get(v2, &c2);
+  text_diff(&c1, &c2, pOut, diffFlags);
+  blob_reset(&c1);
+  blob_reset(&c2);
   if( !isPatch ){
     style_header("Diff");
     style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
                           g.zTop, P("v1"), P("v2"));
     if( !sideBySide ){
@@ -1113,17 +1122,13 @@
     @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2>
     object_description(v1, 0, 0);
     @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2>
     object_description(v2, 0, 0);
     @ <hr />
-    if( sideBySide ){
-      generate_sbsdiff(zV1, zV2);
-    }else{
-      @ <blockquote><pre>
-      @ %h(blob_str(&diff))
-      @ </pre></blockquote>
-    }
+    @ <pre style="while-space:pre;">
+    @ %h(blob_str(&diff))
+    @ </pre>
     blob_reset(&diff);
     style_footer();
   }
 }
 

Index: src/manifest.c
==================================================================
--- src/manifest.c
+++ src/manifest.c
@@ -1245,12 +1245,12 @@
   if( p->zBaseline==0 ) return 0;
   fetch_baseline(p, 1);
   pBase = p->pBaseline;
   if( pBase==0 ) return 0;
   for(i=0; i<pBase->nFile; i++){
-    if( fossil_stricmp(zName, p->aFile[i].zName)==0 ){
-      return &p->aFile[i];
+    if( fossil_stricmp(zName, pBase->aFile[i].zName)==0 ){
+      return &pBase->aFile[i];
     }
   }
   return 0;
 }
 

Index: src/skins.c
==================================================================
--- src/skins.c
+++ src/skins.c
@@ -153,55 +153,10 @@
 @ /* The label/value pairs on (for example) the vinfo page */
 @ table.label-value th {
 @   vertical-align: top;
 @   text-align: right;
 @   padding: 0.2ex 2ex;
-@ }
-@
-@ /* Side-by-side diff */
-@ table.sbsdiff {
-@   background-color: white;
-@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
-@   font-size: 8pt;
-@   border-collapse:collapse;
-@   white-space: pre;
-@   width: 98%;
-@   border: 1px #000 dashed;
-@ }
-@
-@ table.sbsdiff th.diffhdr {
-@   border-bottom: dotted;
-@   border-width: 1px;
-@ }
-@
-@ table.sbsdiff tr td {
-@   white-space: pre;
-@   padding-left: 3px;
-@   padding-right: 3px;
-@   margin: 0px;
-@ }
-@
-@ table.sbsdiff tr td.lineno {
-@   text-align: right;
-@ }
-@
-@ table.sbsdiff tr td.meta {
-@   color: white;
-@   background-color: rgb(20, 20, 20);
-@   text-align: center;
-@ }
-@
-@ table.sbsdiff tr td.added {
-@   background-color: rgb(230, 230, 230);
-@ }
-@
-@ table.sbsdiff tr td.removed {
-@   background-color: rgb(200, 200, 200);
-@ }
-@
-@ table.sbsdiff tr td.changed {
-@   background-color: rgb(220, 220, 220);
 @ }');
 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
 @ <head>
 @ <title>$<project_name>: $<title></title>
 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -400,54 +355,10 @@
 @ /* The label/value pairs on (for example) the ci page */
 @ table.label-value th {
 @   vertical-align: top;
 @   text-align: right;
 @   padding: 0.2ex 2ex;
-@ }
-@
-@ /* Side-by-side diff */
-@ table.sbsdiff {
-@   background-color: #ffffc5;
-@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
-@   font-size: 8pt;
-@   border-collapse:collapse;
-@   white-space: pre;
-@   width: 98%;
-@   border: 1px #000 dashed;
-@ }
-@
-@ table.sbsdiff th.diffhdr {
-@   border-bottom: dotted;
-@   border-width: 1px;
-@ }
-@
-@ table.sbsdiff tr td {
-@   white-space: pre;
-@   padding-left: 3px;
-@   padding-right: 3px;
-@   margin: 0px;
-@ }
-@
-@ table.sbsdiff tr td.lineno {
-@   text-align: right;
-@ }
-@
-@ table.sbsdiff tr td.meta {
-@   background-color: #a09048;
-@   text-align: center;
-@ }
-@
-@ table.sbsdiff tr td.added {
-@   background-color: rgb(210, 210, 100);
-@ }
-@
-@ table.sbsdiff tr td.removed {
-@   background-color: rgb(190, 200, 110);
-@ }
-@
-@ table.sbsdiff tr td.changed {
-@   background-color: rgb(200, 210, 120);
 @ }');
 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
 @ <head>
 @ <title>$<project_name>: $<title></title>
 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -680,56 +591,10 @@
 @ /* The label/value pairs on (for example) the ci page */
 @ table.label-value th {
 @   vertical-align: top;
 @   text-align: right;
 @   padding: 0.2ex 2ex;
-@ }
-@
-@ /* Side-by-side diff */
-@ table.sbsdiff {
-@   background-color: white;
-@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
-@   font-size: 6pt;
-@   border-collapse:collapse;
-@   white-space: pre;
-@   width: 98%;
-@   border: 1px #000 dashed;
-@ }
-@
-@ table.sbsdiff th.diffhdr {
-@   border-bottom: dotted;
-@   border-width: 1px;
-@ }
-@
-@ table.sbsdiff tr td {
-@   white-space: pre;
-@   padding-left: 3px;
-@   padding-right: 3px;
-@   margin: 0px;
-@ }
-@
-@ table.sbsdiff tr td.lineno {
-@   text-align: right;
-@ }
-@
-@ table.sbsdiff tr td.meta {
-@   color: white;
-@   background-color: black;
-@   text-align: center;
-@ }
-@
-@ table.sbsdiff tr td.added {
-@   background-color: white;
-@ }
-@
-@ table.sbsdiff tr td.removed {
-@   background-color: white;
-@   text-decoration: line-through;
-@ }
-@
-@ table.sbsdiff tr td.changed {
-@   background-color: white;
 @ }');
 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
 @ <head>
 @ <title>$<project_name>: $<title></title>
 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -1024,77 +889,10 @@
 @   padding: 3px 5px;
 @ }
 @ 
 @ textarea {
 @   font-size: 1em;
-@ }
-@
-@ /* Side-by-side diff */
-@ table.sbsdiff {
-@   background-color: white;
-@   font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
-@   font-size: 6pt;
-@   border-collapse:collapse;
-@   width: 98%;
-@   border: 1px #000 dashed;
-@   margin-left: auto;
-@   margin-right: auto;
-@ }
-@
-@ table.sbsdiff th.diffhdr {
-@   border-bottom: dotted;
-@   border-width: 1px;
-@ }
-@
-@ table.sbsdiff tr td {
-@   padding-left: 3px;
-@   padding-right: 3px;
-@   margin: 0px;
-@   vertical-align: top;
-@   white-space: pre-wrap;
-@ }
-@
-@ table.sbsdiff tr td.lineno {
-@   text-align: right;
-@   /* border-bottom: 1px solid rgb(220, 220, 220); */
-@ }
-@
-@ table.sbsdiff tr td.srcline {
-@   /* max-width: 400px; */
-@   /* Note: May partially hide long lines without whitespaces */
-@   /* overflow: hidden; */
-@   /* border-bottom: 1px solid rgb(220, 220, 220); */
-@ }
-@
-@ table.sbsdiff tr td.meta {
-@   background-color: rgb(170, 160, 255);
-@   padding-top: 0.25em;
-@   padding-bottom: 0.25em;
-@   text-align: center;
-@   -moz-border-radius: 5px;
-@   -moz-border-radius: 5px;
-@   -webkit-border-radius: 5px;
-@   -webkit-border-radius: 5px;
-@   -border-radius: 5px;
-@   -border-radius: 5px;
-@   border-radius: 5px;
-@   border-radius: 5px;
-@ }
-@
-@ table.sbsdiff tr td.added {
-@   background-color: rgb(180, 250, 180);
-@   /* border-bottom: 1px solid rgb(160, 230, 160); */
-@ }
-@
-@ table.sbsdiff tr td.removed {
-@   background-color: rgb(250, 130, 130);
-@   /* border-bottom: 1px solid rgb(230, 110, 110); */
-@ }
-@
-@ table.sbsdiff tr td.changed {
-@   background-color: rgb(210, 210, 200);
-@   /* border-bottom: 1px solid rgb(190, 190, 180); */
 @ }');
 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
 @ <head>
 @ <title>$<project_name>: $<title></title>
 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"

Index: src/style.c
==================================================================
--- src/style.c
+++ src/style.c
@@ -398,70 +398,10 @@
 @ table.label-value th {
 @   vertical-align: top;
 @   text-align: right;
 @   padding: 0.2ex 2ex;
 @ }
-@
-@ /* Side-by-side diff */
-@ table.sbsdiff {
-@   background-color: white;
-@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
-@   font-size: 8pt;
-@   border-collapse:collapse;
-@   white-space: pre;
-@   width: 98%;
-@   border: 1px #000 dashed;
-@   margin-left: auto;
-@   margin-right: auto;
-@ }
-@
-@ table.sbsdiff th.diffhdr {
-@   border-bottom: dotted;
-@   border-width: 1px;
-@ }
-@
-@ table.sbsdiff tr td {
-@   white-space: pre;
-@   padding-left: 3px;
-@   padding-right: 3px;
-@   margin: 0px;
-@   vertical-align: top;
-@ }
-@
-@ table.sbsdiff tr td.lineno {
-@   text-align: right;
-@ }
-@
-@ table.sbsdiff tr td.srcline {
-@ }
-@
-@ table.sbsdiff tr td.meta {
-@   background-color: rgb(170, 160, 255);
-@   text-align: center;
-@ }
-@
-@ table.sbsdiff tr td.added {
-@   background-color: rgb(180, 250, 180);
-@ }
-@ table.sbsdiff tr td.addedvoid {
-@   background-color: rgb(190, 190, 180);
-@ }
-@
-@ table.sbsdiff tr td.removed {
-@   background-color: rgb(250, 130, 130);
-@ }
-@ table.sbsdiff tr td.removedvoid {
-@   background-color: rgb(190, 190, 180);
-@ }
-@
-@ table.sbsdiff tr td.changed {
-@   background-color: rgb(210, 210, 200);
-@ }
-@ table.sbsdiff tr td.changedvoid {
-@   background-color: rgb(190, 190, 180);
-@ }
-@
 ;
 
 
 /* The following table contains bits of default CSS that must
 ** be included if they are not found in the application-defined
@@ -811,10 +751,41 @@
   { "ul.filelist",
     "List of files in a timeline",
     @   margin-top: 3px;
     @   line-height: 100%;
   },
+  { "div.sbsdiff",
+    "side-by-side diff display",
+    @   font-family: monospace;
+    @   font-size: smaller;
+    @   white-space: pre;
+  },
+  { "div.udiff",
+    "context diff display",
+    @   font-family: monospace;
+    @   white-space: pre;
+  },
+  { "span.diffchng",
+    "changes in a diff",
+    @   background-color: #ffffc8;
+  },
+  { "span.diffadd",
+    "added code in a diff",
+    @   background-color: #e0ffe0;
+  },
+  { "span.diffrm",
+    "deleted in a diff",
+    @   background-color: #ffe0e0;
+  },
+  { "span.diffhr",
+    "suppressed lines in a diff",
+    @   color: #0000ff;
+  },
+  { "span.diffln",
+    "line nubmers in a diff",
+    @   color: #a0a0a0;
+  },
   { 0,
     0,
     0
   }
 };