| | @@ -25,15 +25,18 @@ |
| 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) |
| | @@ -146,15 +149,49 @@ |
| 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){ |
| | @@ -198,11 +235,17 @@ |
| 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 */ |
| | @@ -252,40 +295,57 @@ |
| 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 | } |
| | @@ -293,57 +353,124 @@ |
| 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 |
| 366 | +*/ |
| 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() |
| 306 | 377 | */ |
| 307 | | -static void sbsWriteLineno(char *z, int ln){ |
| 308 | | - sqlite3_snprintf(7, z, "%6d", ln+1); |
| 309 | | - z[6] = ' '; |
| 310 | | -} |
| 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], "<", 4); |
| 403 | + j += 4; |
| 404 | + }else if( c=='&' && p->escHtml ){ |
| 405 | + memcpy(&z[j], "&", 5); |
| 406 | + j += 5; |
| 407 | + }else if( c=='>' && p->escHtml ){ |
| 408 | + memcpy(&z[j], ">", 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 */ |
| | @@ -352,18 +479,16 @@ |
| 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 */ |
| 360 | | - |
| 361 | | - mxLine = width*2 + 2*7 + 3 + 1; |
| 362 | | - zLine = fossil_malloc( mxLine + 1 ); |
| 363 | | - if( zLine==0 ) return; |
| 364 | | - zLine[mxLine] = 0; |
| 484 | + SbsLine s; /* Output line buffer */ |
| 485 | + |
| 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; } |
| | @@ -400,23 +525,31 @@ |
| 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,49 +556,53 @@ |
| 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 | } |
| | @@ -473,19 +610,20 @@ |
| 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 |
| | @@ -786,15 +924,17 @@ |
| 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; |
| | @@ -805,194 +945,10 @@ |
| 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><</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>></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){ |
| | @@ -1019,10 +975,12 @@ |
| 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; |
| | @@ -1034,10 +992,12 @@ |
| 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 | |