| | @@ -25,16 +25,17 @@ |
| 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 */ |
| 35 | | -#define DIFF_INLINE 0x0800000 /* Inline (not side-by-side) diff */ |
| 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 */ |
| 36 | 37 | |
| 37 | 38 | #endif /* INTERFACE */ |
| 38 | 39 | |
| 39 | 40 | /* |
| 40 | 41 | ** Maximum length of a line in a text file. (8192) |
| | @@ -300,51 +301,115 @@ |
| 300 | 301 | } |
| 301 | 302 | } |
| 302 | 303 | } |
| 303 | 304 | |
| 304 | 305 | /* |
| 305 | | -** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to |
| 306 | | -** have space for at least 7 characters. |
| 306 | +** Status of a single output line |
| 307 | +*/ |
| 308 | +typedef struct SbsLine SbsLine; |
| 309 | +struct SbsLine { |
| 310 | + char *zLine; /* The output line under construction */ |
| 311 | + int n; /* Index of next unused slot in the zLine[] */ |
| 312 | + int width; /* Maximum width of a column in the output */ |
| 313 | + unsigned char escHtml; /* True to escape html characters */ |
| 314 | +}; |
| 315 | + |
| 316 | +/* |
| 317 | +** Write a 6-digit line number followed by a single space onto the line. |
| 307 | 318 | */ |
| 308 | | -static void sbsWriteLineno(char *z, int ln){ |
| 309 | | - sqlite3_snprintf(7, z, "%6d", ln+1); |
| 310 | | - z[6] = ' '; |
| 319 | +static void sbsWriteLineno(SbsLine *p, int ln){ |
| 320 | + sqlite3_snprintf(7, &p->zLine[p->n], "%6d", ln+1); |
| 321 | + p->zLine[p->n+6] = ' '; |
| 322 | + p->n += 7; |
| 311 | 323 | } |
| 312 | 324 | |
| 325 | +/* |
| 326 | +** Flags for sbsWriteText() |
| 327 | +*/ |
| 328 | +#define SBS_NEWLINE 0x0001 /* End with \n\000 */ |
| 329 | +#define SBS_PAD 0x0002 /* Pad output to width spaces */ |
| 330 | +#define SBS_ENDSPAN 0x0004 /* Write a </span> after text */ |
| 331 | + |
| 313 | 332 | /* |
| 314 | 333 | ** Write up to width characters of pLine into z[]. Translate tabs into |
| 315 | | -** spaces. If trunc is true, then append \n\000 after the last character |
| 316 | | -** written. |
| 334 | +** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters |
| 335 | +** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set. |
| 317 | 336 | */ |
| 318 | | -static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){ |
| 337 | +static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){ |
| 319 | 338 | int n = pLine->h & LENGTH_MASK; |
| 320 | | - int i, j; |
| 339 | + int i, j, k; |
| 321 | 340 | const char *zIn = pLine->z; |
| 322 | | - for(i=j=0; i<n && j<width; i++){ |
| 341 | + char *z = &p->zLine[p->n]; |
| 342 | + int w = p->width; |
| 343 | + if( n>w ) n = w; |
| 344 | + for(i=j=0; i<n; i++){ |
| 323 | 345 | char c = zIn[i]; |
| 324 | 346 | if( c=='\t' ){ |
| 325 | 347 | z[j++] = ' '; |
| 326 | | - while( (j&7)!=0 && j<width ) z[j++] = ' '; |
| 348 | + while( (j&7)!=0 && j<n ) z[j++] = ' '; |
| 327 | 349 | }else if( c=='\r' || c=='\f' ){ |
| 328 | 350 | z[j++] = ' '; |
| 351 | + }else if( c=='<' && p->escHtml ){ |
| 352 | + memcpy(&z[j], "<", 4); |
| 353 | + j += 4; |
| 354 | + }else if( c=='&' && p->escHtml ){ |
| 355 | + memcpy(&z[j], "&", 5); |
| 356 | + j += 5; |
| 357 | + }else if( c=='>' && p->escHtml ){ |
| 358 | + memcpy(&z[j], ">", 4); |
| 359 | + j += 4; |
| 329 | 360 | }else{ |
| 330 | 361 | z[j++] = c; |
| 331 | 362 | } |
| 332 | 363 | } |
| 333 | | - if( trunc ){ |
| 364 | + if( (flags & SBS_ENDSPAN) && p->escHtml ){ |
| 365 | + memcpy(&z[j], "</span>", 7); |
| 366 | + j += 7; |
| 367 | + } |
| 368 | + if( (flags & SBS_PAD)!=0 ){ |
| 369 | + while( i<w ){ i++; z[j++] = ' '; } |
| 370 | + } |
| 371 | + if( flags & SBS_NEWLINE ){ |
| 334 | 372 | z[j++] = '\n'; |
| 335 | | - z[j] = 0; |
| 336 | 373 | } |
| 337 | | - return j; |
| 374 | + p->n += j; |
| 375 | +} |
| 376 | + |
| 377 | +/* |
| 378 | +** Append a string to an SbSLine with coding, interpretation, or padding. |
| 379 | +*/ |
| 380 | +static void sbsWrite(SbsLine *p, const char *zIn, int nIn){ |
| 381 | + memcpy(p->zLine+p->n, zIn, nIn); |
| 382 | + p->n += nIn; |
| 383 | +} |
| 384 | + |
| 385 | +/* |
| 386 | +** Append n spaces to the string. |
| 387 | +*/ |
| 388 | +static void sbsWriteSpace(SbsLine *p, int n){ |
| 389 | + while( n-- ) p->zLine[p->n++] = ' '; |
| 390 | +} |
| 391 | + |
| 392 | +/* |
| 393 | +** Append a string to the output only if we are rendering HTML. |
| 394 | +*/ |
| 395 | +static void sbsWriteHtml(SbsLine *p, const char *zIn){ |
| 396 | + if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn)); |
| 338 | 397 | } |
| 339 | 398 | |
| 340 | 399 | |
| 341 | 400 | /* |
| 342 | 401 | ** Given a diff context in which the aEdit[] array has been filled |
| 343 | 402 | ** in, compute a side-by-side diff into pOut. |
| 344 | 403 | */ |
| 345 | | -static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){ |
| 404 | +static void sbsDiff( |
| 405 | + DContext *p, /* The computed diff */ |
| 406 | + Blob *pOut, /* Write the results here */ |
| 407 | + int nContext, /* Number of lines of context around each change */ |
| 408 | + int width, /* Width of each column of output */ |
| 409 | + int escHtml /* True to generate HTML output */ |
| 410 | +){ |
| 346 | 411 | DLine *A; /* Left side of the diff */ |
| 347 | 412 | DLine *B; /* Right side of the diff */ |
| 348 | 413 | int a = 0; /* Index of next line in A[] */ |
| 349 | 414 | int b = 0; /* Index of next line in B[] */ |
| 350 | 415 | int *R; /* Array of COPY/DELETE/INSERT triples */ |
| | @@ -353,18 +418,16 @@ |
| 353 | 418 | int mxr; /* Maximum value for r */ |
| 354 | 419 | int na, nb; /* Number of lines shown from A and B */ |
| 355 | 420 | int i, j; /* Loop counters */ |
| 356 | 421 | int m, ma, mb;/* Number of lines to output */ |
| 357 | 422 | int skip; /* Number of lines to skip */ |
| 358 | | - int mxLine; /* Length of a line of text */ |
| 359 | | - char *zLine; /* A line of text being formatted */ |
| 360 | | - int len; /* Length of an output line */ |
| 361 | | - |
| 362 | | - mxLine = width*2 + 2*7 + 3 + 1; |
| 363 | | - zLine = fossil_malloc( mxLine + 1 ); |
| 364 | | - if( zLine==0 ) return; |
| 365 | | - zLine[mxLine] = 0; |
| 423 | + SbsLine s; /* Output line buffer */ |
| 424 | + |
| 425 | + s.zLine = fossil_malloc( 10*width + 100 ); |
| 426 | + if( s.zLine==0 ) return; |
| 427 | + s.width = width; |
| 428 | + s.escHtml = escHtml; |
| 366 | 429 | A = p->aFrom; |
| 367 | 430 | B = p->aTo; |
| 368 | 431 | R = p->aEdit; |
| 369 | 432 | mxr = p->nEdit; |
| 370 | 433 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| | @@ -401,23 +464,31 @@ |
| 401 | 464 | /* |
| 402 | 465 | * If the patch changes an empty file or results in an empty file, |
| 403 | 466 | * the block header must use 0,0 as position indicator and not 1,0. |
| 404 | 467 | * Otherwise, patch would be confused and may reject the diff. |
| 405 | 468 | */ |
| 406 | | - if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.'); |
| 469 | + if( r>0 ){ |
| 470 | + if( escHtml ){ |
| 471 | + blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n", |
| 472 | + width*2+16, '.'); |
| 473 | + }else{ |
| 474 | + blob_appendf(pOut, "%.*c\n", width*2+16, '.'); |
| 475 | + } |
| 476 | + } |
| 407 | 477 | |
| 408 | 478 | /* Show the initial common area */ |
| 409 | 479 | a += skip; |
| 410 | 480 | b += skip; |
| 411 | 481 | m = R[r] - skip; |
| 412 | 482 | for(j=0; j<m; j++){ |
| 413 | | - memset(zLine, ' ', mxLine); |
| 414 | | - sbsWriteLineno(zLine, a+j); |
| 415 | | - sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 416 | | - sbsWriteLineno(&zLine[width+10], b+j); |
| 417 | | - len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 418 | | - blob_append(pOut, zLine, len+width+17); |
| 483 | + s.n = 0; |
| 484 | + sbsWriteLineno(&s, a+j); |
| 485 | + sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 486 | + sbsWrite(&s, " ", 3); |
| 487 | + sbsWriteLineno(&s, b+j); |
| 488 | + sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 489 | + blob_append(pOut, s.zLine, s.n); |
| 419 | 490 | } |
| 420 | 491 | a += m; |
| 421 | 492 | b += m; |
| 422 | 493 | |
| 423 | 494 | /* Show the differences */ |
| | @@ -424,49 +495,53 @@ |
| 424 | 495 | for(i=0; i<nr; i++){ |
| 425 | 496 | ma = R[r+i*3+1]; |
| 426 | 497 | mb = R[r+i*3+2]; |
| 427 | 498 | m = ma<mb ? ma : mb; |
| 428 | 499 | for(j=0; j<m; j++){ |
| 429 | | - memset(zLine, ' ', mxLine); |
| 430 | | - sbsWriteLineno(zLine, a+j); |
| 431 | | - sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 432 | | - zLine[width+8] = '|'; |
| 433 | | - sbsWriteLineno(&zLine[width+10], b+j); |
| 434 | | - len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 435 | | - blob_append(pOut, zLine, len+width+17); |
| 500 | + s.n = 0; |
| 501 | + sbsWriteLineno(&s, a+j); |
| 502 | + sbsWriteHtml(&s, "<span class=\"diffchng\">"); |
| 503 | + sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN); |
| 504 | + sbsWrite(&s, " | ", 3); |
| 505 | + sbsWriteLineno(&s, b+j); |
| 506 | + sbsWriteHtml(&s, "<span class=\"diffchng\">"); |
| 507 | + sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN); |
| 508 | + blob_append(pOut, s.zLine, s.n); |
| 436 | 509 | } |
| 437 | 510 | a += m; |
| 438 | 511 | b += m; |
| 439 | 512 | ma -= m; |
| 440 | 513 | mb -= m; |
| 441 | 514 | for(j=0; j<ma; j++){ |
| 442 | | - memset(zLine, ' ', width+7); |
| 443 | | - sbsWriteLineno(zLine, a+j); |
| 444 | | - sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 445 | | - zLine[width+8] = '<'; |
| 446 | | - zLine[width+9] = '\n'; |
| 447 | | - zLine[width+10] = 0; |
| 448 | | - blob_append(pOut, zLine, width+10); |
| 515 | + s.n = 0; |
| 516 | + sbsWriteLineno(&s, a+j); |
| 517 | + sbsWriteHtml(&s, "<span class=\"diffrm\">"); |
| 518 | + sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN); |
| 519 | + sbsWrite(&s, " <\n", 3); |
| 520 | + blob_append(pOut, s.zLine, s.n); |
| 449 | 521 | } |
| 450 | 522 | a += ma; |
| 451 | 523 | for(j=0; j<mb; j++){ |
| 452 | | - memset(zLine, ' ', mxLine); |
| 453 | | - zLine[width+8] = '>'; |
| 454 | | - sbsWriteLineno(&zLine[width+10], b+j); |
| 455 | | - len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 456 | | - blob_append(pOut, zLine, len+width+17); |
| 524 | + s.n = 0; |
| 525 | + sbsWriteSpace(&s, width + 7); |
| 526 | + sbsWrite(&s, " > ", 3); |
| 527 | + sbsWriteLineno(&s, b+j); |
| 528 | + sbsWriteHtml(&s, "<span class=\"diffadd\">"); |
| 529 | + sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN); |
| 530 | + blob_append(pOut, s.zLine, s.n); |
| 457 | 531 | } |
| 458 | 532 | b += mb; |
| 459 | 533 | if( i<nr-1 ){ |
| 460 | 534 | m = R[r+i*3+3]; |
| 461 | 535 | for(j=0; j<m; j++){ |
| 462 | | - memset(zLine, ' ', mxLine); |
| 463 | | - sbsWriteLineno(zLine, a+j); |
| 464 | | - sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 465 | | - sbsWriteLineno(&zLine[width+10], b+j); |
| 466 | | - len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 467 | | - blob_append(pOut, zLine, len+width+17); |
| 536 | + s.n = 0; |
| 537 | + sbsWriteLineno(&s, a+j); |
| 538 | + sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 539 | + sbsWrite(&s, " ", 3); |
| 540 | + sbsWriteLineno(&s, b+j); |
| 541 | + sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 542 | + blob_append(pOut, s.zLine, s.n); |
| 468 | 543 | } |
| 469 | 544 | b += m; |
| 470 | 545 | a += m; |
| 471 | 546 | } |
| 472 | 547 | } |
| | @@ -474,19 +549,20 @@ |
| 474 | 549 | /* Show the final common area */ |
| 475 | 550 | assert( nr==i ); |
| 476 | 551 | m = R[r+nr*3]; |
| 477 | 552 | if( m>nContext ) m = nContext; |
| 478 | 553 | for(j=0; j<m; j++){ |
| 479 | | - memset(zLine, ' ', mxLine); |
| 480 | | - sbsWriteLineno(zLine, a+j); |
| 481 | | - sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 482 | | - sbsWriteLineno(&zLine[width+10], b+j); |
| 483 | | - len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 484 | | - blob_append(pOut, zLine, len+width+17); |
| 554 | + s.n = 0; |
| 555 | + sbsWriteLineno(&s, a+j); |
| 556 | + sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 557 | + sbsWrite(&s, " ", 3); |
| 558 | + sbsWriteLineno(&s, b+j); |
| 559 | + sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 560 | + blob_append(pOut, s.zLine, s.n); |
| 485 | 561 | } |
| 486 | 562 | } |
| 487 | | - free(zLine); |
| 563 | + free(s.zLine); |
| 488 | 564 | } |
| 489 | 565 | |
| 490 | 566 | /* |
| 491 | 567 | ** Compute the optimal longest common subsequence (LCS) using an |
| 492 | 568 | ** exhaustive search. This version of the LCS is only used for |
| | @@ -789,11 +865,12 @@ |
| 789 | 865 | |
| 790 | 866 | if( pOut ){ |
| 791 | 867 | /* Compute a context or side-by-side diff into pOut */ |
| 792 | 868 | if( diffFlags & DIFF_SIDEBYSIDE ){ |
| 793 | 869 | int width = diff_width(diffFlags); |
| 794 | | - sbsDiff(&c, pOut, nContext, width); |
| 870 | + int escHtml = (diffFlags & DIFF_HTML)!=0; |
| 871 | + sbsDiff(&c, pOut, nContext, width, escHtml); |
| 795 | 872 | }else{ |
| 796 | 873 | contextDiff(&c, pOut, nContext); |
| 797 | 874 | } |
| 798 | 875 | free(c.aFrom); |
| 799 | 876 | free(c.aTo); |
| | @@ -851,10 +928,11 @@ |
| 851 | 928 | if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ |
| 852 | 929 | f *= DIFF_CONTEXT_MASK+1; |
| 853 | 930 | if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK; |
| 854 | 931 | diffFlags |= f; |
| 855 | 932 | } |
| 933 | + if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML; |
| 856 | 934 | return diffFlags; |
| 857 | 935 | } |
| 858 | 936 | |
| 859 | 937 | /* |
| 860 | 938 | ** COMMAND: test-udiff |
| 861 | 939 | |