| | @@ -51,16 +51,13 @@ |
| 51 | 51 | "cannot compute difference between binary files\n" |
| 52 | 52 | |
| 53 | 53 | #define DIFF_CANNOT_COMPUTE_SYMLINK \ |
| 54 | 54 | "cannot compute difference between symlink and regular file\n" |
| 55 | 55 | |
| 56 | | -#define DIFF_TOO_MANY_CHANGES_TXT \ |
| 56 | +#define DIFF_TOO_MANY_CHANGES \ |
| 57 | 57 | "more than 10,000 changes\n" |
| 58 | 58 | |
| 59 | | -#define DIFF_TOO_MANY_CHANGES_HTML \ |
| 60 | | - "<p class='generalError'>More than 10,000 changes</p>\n" |
| 61 | | - |
| 62 | 59 | /* |
| 63 | 60 | ** This macro is designed to return non-zero if the specified blob contains |
| 64 | 61 | ** data that MAY be binary in nature; otherwise, zero will be returned. |
| 65 | 62 | */ |
| 66 | 63 | #define looks_like_binary(blob) \ |
| | @@ -576,11 +573,11 @@ |
| 576 | 573 | int mxr; /* Maximum value for r */ |
| 577 | 574 | int na, nb; /* Number of lines shown from A and B */ |
| 578 | 575 | int i, j; /* Loop counters */ |
| 579 | 576 | int m; /* Number of lines to output */ |
| 580 | 577 | int skip; /* Number of lines to skip */ |
| 581 | | - int nChunk = 0; /* Number of diff chunks seen so far */ |
| 578 | + static int nChunk = 0; /* Number of diff chunks seen so far */ |
| 582 | 579 | int nContext; /* Number of lines of context */ |
| 583 | 580 | int showLn; /* Show line numbers */ |
| 584 | 581 | int html; /* Render as HTML */ |
| 585 | 582 | int showDivider = 0; /* True to show the divider between diff blocks */ |
| 586 | 583 | |
| | @@ -657,14 +654,14 @@ |
| 657 | 654 | if( !showDivider ){ |
| 658 | 655 | /* Do not show a top divider */ |
| 659 | 656 | showDivider = 1; |
| 660 | 657 | }else if( html ){ |
| 661 | 658 | blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.'); |
| 662 | | - blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); |
| 663 | 659 | }else{ |
| 664 | 660 | blob_appendf(pOut, "%.80c\n", '.'); |
| 665 | 661 | } |
| 662 | + if( html ) blob_appendf(pOut, "<span id=\"chunk%d\"></span>", nChunk); |
| 666 | 663 | }else{ |
| 667 | 664 | if( html ) blob_appendf(pOut, "<span class=\"diffln\">"); |
| 668 | 665 | /* |
| 669 | 666 | * If the patch changes an empty file or results in an empty file, |
| 670 | 667 | * the block header must use 0,0 as position indicator and not 1,0. |
| | @@ -727,12 +724,11 @@ |
| 727 | 724 | /* |
| 728 | 725 | ** Status of a single output line |
| 729 | 726 | */ |
| 730 | 727 | typedef struct SbsLine SbsLine; |
| 731 | 728 | struct SbsLine { |
| 732 | | - char *zLine; /* The output line under construction */ |
| 733 | | - int n; /* Index of next unused slot in the zLine[] */ |
| 729 | + Blob *apCols[5]; /* Array of pointers to output columns */ |
| 734 | 730 | int width; /* Maximum width of a column in the output */ |
| 735 | 731 | unsigned char escHtml; /* True to escape html characters */ |
| 736 | 732 | int iStart; /* Write zStart prior to character iStart */ |
| 737 | 733 | const char *zStart; /* A <span> tag */ |
| 738 | 734 | int iEnd; /* Write </span> prior to character iEnd */ |
| | @@ -741,125 +737,154 @@ |
| 741 | 737 | int iEnd2; /* Write </span> prior to character iEnd2 */ |
| 742 | 738 | ReCompiled *pRe; /* Only colorize matching lines, if not NULL */ |
| 743 | 739 | }; |
| 744 | 740 | |
| 745 | 741 | /* |
| 746 | | -** Flags for sbsWriteText() |
| 742 | +** Column indices |
| 743 | +*/ |
| 744 | +#define SBS_LNA 0 |
| 745 | +#define SBS_TXTA 1 |
| 746 | +#define SBS_MKR 2 |
| 747 | +#define SBS_LNB 3 |
| 748 | +#define SBS_TXTB 4 |
| 749 | + |
| 750 | +/* |
| 751 | +** Append newlines to all columns. |
| 752 | +*/ |
| 753 | +static void sbsWriteNewlines(SbsLine *p){ |
| 754 | + int i; |
| 755 | + for( i=p->escHtml ? SBS_LNA : SBS_TXTB; i<=SBS_TXTB; i++ ){ |
| 756 | + blob_append(p->apCols[i], "\n", 1); |
| 757 | + } |
| 758 | +} |
| 759 | + |
| 760 | +/* |
| 761 | +** Append n spaces to the column. |
| 747 | 762 | */ |
| 748 | | -#define SBS_NEWLINE 0x0001 /* End with \n\000 */ |
| 749 | | -#define SBS_PAD 0x0002 /* Pad output to width spaces */ |
| 763 | +static void sbsWriteSpace(SbsLine *p, int n, int col){ |
| 764 | + blob_appendf(p->apCols[col], "%*s", n, ""); |
| 765 | +} |
| 750 | 766 | |
| 751 | 767 | /* |
| 752 | | -** Write up to width characters of pLine into p->zLine[]. Translate tabs into |
| 753 | | -** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters |
| 754 | | -** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set. |
| 768 | +** Write pLine to the column. If outputting HTML, write the full line. |
| 769 | +** Otherwise, only write up to width characters. Translate tabs into |
| 770 | +** spaces. Add newlines if col is SBS_TXTB. Translate HTML characters |
| 771 | +** if escHtml is true. Pad the rendering out width bytes if col is |
| 772 | +** SBS_TXTA and escHtml is false. |
| 755 | 773 | ** |
| 756 | 774 | ** This comment contains multibyte unicode characters (ü, Æ, ð) in order |
| 757 | 775 | ** to test the ability of the diff code to handle such characters. |
| 758 | 776 | */ |
| 759 | | -static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){ |
| 777 | +static void sbsWriteText(SbsLine *p, DLine *pLine, int col){ |
| 778 | + Blob *pCol = p->apCols[col]; |
| 760 | 779 | int n = pLine->h & LENGTH_MASK; |
| 761 | 780 | int i; /* Number of input characters consumed */ |
| 762 | | - int j; /* Number of output characters generated */ |
| 763 | 781 | int k; /* Cursor position */ |
| 764 | 782 | int needEndSpan = 0; |
| 765 | 783 | const char *zIn = pLine->z; |
| 766 | | - char *z = &p->zLine[p->n]; |
| 767 | 784 | int w = p->width; |
| 768 | 785 | int colorize = p->escHtml; |
| 769 | 786 | if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){ |
| 770 | 787 | colorize = 0; |
| 771 | 788 | } |
| 772 | | - for(i=j=k=0; k<w && i<n; i++, k++){ |
| 789 | + for(i=k=0; (p->escHtml || k<w) && i<n; i++, k++){ |
| 773 | 790 | char c = zIn[i]; |
| 774 | 791 | if( colorize ){ |
| 775 | 792 | if( i==p->iStart ){ |
| 776 | 793 | int x = strlen(p->zStart); |
| 777 | | - memcpy(z+j, p->zStart, x); |
| 778 | | - j += x; |
| 794 | + blob_append(pCol, p->zStart, x); |
| 779 | 795 | needEndSpan = 1; |
| 780 | 796 | if( p->iStart2 ){ |
| 781 | 797 | p->iStart = p->iStart2; |
| 782 | 798 | p->zStart = p->zStart2; |
| 783 | 799 | p->iStart2 = 0; |
| 784 | 800 | } |
| 785 | 801 | }else if( i==p->iEnd ){ |
| 786 | | - memcpy(z+j, "</span>", 7); |
| 787 | | - j += 7; |
| 802 | + blob_append(pCol, "</span>", 7); |
| 788 | 803 | needEndSpan = 0; |
| 789 | 804 | if( p->iEnd2 ){ |
| 790 | 805 | p->iEnd = p->iEnd2; |
| 791 | 806 | p->iEnd2 = 0; |
| 792 | 807 | } |
| 793 | 808 | } |
| 794 | 809 | } |
| 795 | 810 | if( c=='\t' ){ |
| 796 | | - z[j++] = ' '; |
| 797 | | - while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; } |
| 811 | + blob_append(pCol, " ", 1); |
| 812 | + while( (k&7)!=7 && (p->escHtml || k<w) ){ |
| 813 | + blob_append(pCol, " ", 1); |
| 814 | + k++; |
| 815 | + } |
| 798 | 816 | }else if( c=='\r' || c=='\f' ){ |
| 799 | | - z[j++] = ' '; |
| 817 | + blob_append(pCol, " ", 1); |
| 800 | 818 | }else if( c=='<' && p->escHtml ){ |
| 801 | | - memcpy(&z[j], "<", 4); |
| 802 | | - j += 4; |
| 819 | + blob_append(pCol, "<", 4); |
| 803 | 820 | }else if( c=='&' && p->escHtml ){ |
| 804 | | - memcpy(&z[j], "&", 5); |
| 805 | | - j += 5; |
| 821 | + blob_append(pCol, "&", 5); |
| 806 | 822 | }else if( c=='>' && p->escHtml ){ |
| 807 | | - memcpy(&z[j], ">", 4); |
| 808 | | - j += 4; |
| 823 | + blob_append(pCol, ">", 4); |
| 809 | 824 | }else if( c=='"' && p->escHtml ){ |
| 810 | | - memcpy(&z[j], """, 6); |
| 811 | | - j += 6; |
| 825 | + blob_append(pCol, """, 6); |
| 812 | 826 | }else{ |
| 813 | | - z[j++] = c; |
| 827 | + blob_append(pCol, &zIn[i], 1); |
| 814 | 828 | if( (c&0xc0)==0x80 ) k--; |
| 815 | 829 | } |
| 816 | 830 | } |
| 817 | 831 | if( needEndSpan ){ |
| 818 | | - memcpy(&z[j], "</span>", 7); |
| 819 | | - j += 7; |
| 820 | | - } |
| 821 | | - if( (flags & SBS_PAD)!=0 ){ |
| 822 | | - while( k<w ){ k++; z[j++] = ' '; } |
| 823 | | - } |
| 824 | | - if( flags & SBS_NEWLINE ){ |
| 825 | | - z[j++] = '\n'; |
| 826 | | - } |
| 827 | | - p->n += j; |
| 828 | | -} |
| 829 | | - |
| 830 | | -/* |
| 831 | | -** Append a string to an SbSLine without coding, interpretation, or padding. |
| 832 | | -*/ |
| 833 | | -static void sbsWrite(SbsLine *p, const char *zIn, int nIn){ |
| 834 | | - memcpy(p->zLine+p->n, zIn, nIn); |
| 835 | | - p->n += nIn; |
| 836 | | -} |
| 837 | | - |
| 838 | | -/* |
| 839 | | -** Append n spaces to the string. |
| 840 | | -*/ |
| 841 | | -static void sbsWriteSpace(SbsLine *p, int n){ |
| 842 | | - while( n-- ) p->zLine[p->n++] = ' '; |
| 843 | | -} |
| 844 | | - |
| 845 | | -/* |
| 846 | | -** Append a string to the output only if we are rendering HTML. |
| 847 | | -*/ |
| 848 | | -static void sbsWriteHtml(SbsLine *p, const char *zIn){ |
| 849 | | - if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn)); |
| 850 | | -} |
| 851 | | - |
| 852 | | -/* |
| 853 | | -** Write a 6-digit line number followed by a single space onto the line. |
| 854 | | -*/ |
| 855 | | -static void sbsWriteLineno(SbsLine *p, int ln){ |
| 856 | | - sbsWriteHtml(p, "<span class=\"diffln\">"); |
| 857 | | - sqlite3_snprintf(7, &p->zLine[p->n], "%5d ", ln+1); |
| 858 | | - p->n += 6; |
| 859 | | - sbsWriteHtml(p, "</span>"); |
| 860 | | - p->zLine[p->n++] = ' '; |
| 832 | + blob_append(pCol, "</span>", 7); |
| 833 | + } |
| 834 | + if( col==SBS_TXTB ){ |
| 835 | + sbsWriteNewlines(p); |
| 836 | + }else if( !p->escHtml ){ |
| 837 | + sbsWriteSpace(p, w-k, SBS_TXTA); |
| 838 | + } |
| 839 | +} |
| 840 | + |
| 841 | +/* |
| 842 | +** Append a column to the final output blob. |
| 843 | +*/ |
| 844 | +static void sbsWriteColumn(Blob *pOut, Blob *pCol, int col){ |
| 845 | + blob_appendf(pOut, |
| 846 | + "<td><div class=\"diff%scol\">\n" |
| 847 | + "<pre>\n" |
| 848 | + "%s" |
| 849 | + "</pre>\n" |
| 850 | + "</div></td>\n", |
| 851 | + col % 3 ? (col == SBS_MKR ? "mkr" : "txt") : "ln", |
| 852 | + blob_str(pCol) |
| 853 | + ); |
| 854 | +} |
| 855 | + |
| 856 | +/* |
| 857 | +** Append separator to the column. |
| 858 | +*/ |
| 859 | +static void sbsWriteSep(SbsLine *p, int len, int col){ |
| 860 | + char ch = '.'; |
| 861 | + if( len<1 ){ |
| 862 | + len = 1; |
| 863 | + ch = ' '; |
| 864 | + } |
| 865 | + blob_appendf(p->apCols[col], "<span class=\"diffhr\">%.*c</span>\n", len, ch); |
| 866 | +} |
| 867 | + |
| 868 | +/* |
| 869 | +** Append the appropriate marker. |
| 870 | +*/ |
| 871 | +static void sbsWriteMarker(SbsLine *p, const char *zTxt, const char *zHtml){ |
| 872 | + blob_append(p->apCols[SBS_MKR], p->escHtml ? zHtml : zTxt, -1); |
| 873 | +} |
| 874 | + |
| 875 | +/* |
| 876 | +** Append a line number to the column. |
| 877 | +*/ |
| 878 | +static void sbsWriteLineno(SbsLine *p, int ln, int col){ |
| 879 | + if( p->escHtml ){ |
| 880 | + blob_appendf(p->apCols[col], "%d", ln+1); |
| 881 | + }else{ |
| 882 | + char zLn[7]; |
| 883 | + sqlite3_snprintf(7, zLn, "%5d ", ln+1); |
| 884 | + blob_appendf(p->apCols[col], "%s ", zLn); |
| 885 | + } |
| 861 | 886 | } |
| 862 | 887 | |
| 863 | 888 | /* |
| 864 | 889 | ** The two text segments zLeft and zRight are known to be different on |
| 865 | 890 | ** both ends, but they might have a common segment in the middle. If |
| | @@ -1018,43 +1043,42 @@ |
| 1018 | 1043 | } |
| 1019 | 1044 | if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0; |
| 1020 | 1045 | } |
| 1021 | 1046 | if( nPrefix+nSuffix > nShort ) nPrefix = nShort - nSuffix; |
| 1022 | 1047 | |
| 1023 | | - |
| 1024 | 1048 | /* A single chunk of text inserted on the right */ |
| 1025 | 1049 | if( nPrefix+nSuffix==nLeft ){ |
| 1026 | | - sbsWriteLineno(p, lnLeft); |
| 1050 | + sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 1027 | 1051 | p->iStart2 = p->iEnd2 = 0; |
| 1028 | 1052 | p->iStart = p->iEnd = -1; |
| 1029 | | - sbsWriteText(p, pLeft, SBS_PAD); |
| 1053 | + sbsWriteText(p, pLeft, SBS_TXTA); |
| 1030 | 1054 | if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){ |
| 1031 | | - sbsWrite(p, " ", 3); |
| 1055 | + sbsWriteMarker(p, " ", ""); |
| 1032 | 1056 | }else{ |
| 1033 | | - sbsWrite(p, " | ", 3); |
| 1057 | + sbsWriteMarker(p, " | ", "|"); |
| 1034 | 1058 | } |
| 1035 | | - sbsWriteLineno(p, lnRight); |
| 1059 | + sbsWriteLineno(p, lnRight, SBS_LNB); |
| 1036 | 1060 | p->iStart = nPrefix; |
| 1037 | 1061 | p->iEnd = nRight - nSuffix; |
| 1038 | 1062 | p->zStart = zClassAdd; |
| 1039 | | - sbsWriteText(p, pRight, SBS_NEWLINE); |
| 1063 | + sbsWriteText(p, pRight, SBS_TXTB); |
| 1040 | 1064 | return; |
| 1041 | 1065 | } |
| 1042 | 1066 | |
| 1043 | 1067 | /* A single chunk of text deleted from the left */ |
| 1044 | 1068 | if( nPrefix+nSuffix==nRight ){ |
| 1045 | 1069 | /* Text deleted from the left */ |
| 1046 | | - sbsWriteLineno(p, lnLeft); |
| 1070 | + sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 1047 | 1071 | p->iStart2 = p->iEnd2 = 0; |
| 1048 | 1072 | p->iStart = nPrefix; |
| 1049 | 1073 | p->iEnd = nLeft - nSuffix; |
| 1050 | 1074 | p->zStart = zClassRm; |
| 1051 | | - sbsWriteText(p, pLeft, SBS_PAD); |
| 1052 | | - sbsWrite(p, " | ", 3); |
| 1053 | | - sbsWriteLineno(p, lnRight); |
| 1075 | + sbsWriteText(p, pLeft, SBS_TXTA); |
| 1076 | + sbsWriteMarker(p, " | ", "|"); |
| 1077 | + sbsWriteLineno(p, lnRight, SBS_LNB); |
| 1054 | 1078 | p->iStart = p->iEnd = -1; |
| 1055 | | - sbsWriteText(p, pRight, SBS_NEWLINE); |
| 1079 | + sbsWriteText(p, pRight, SBS_TXTB); |
| 1056 | 1080 | return; |
| 1057 | 1081 | } |
| 1058 | 1082 | |
| 1059 | 1083 | /* At this point we know that there is a chunk of text that has |
| 1060 | 1084 | ** changed between the left and the right. Check to see if there |
| | @@ -1065,11 +1089,11 @@ |
| 1065 | 1089 | if( p->escHtml |
| 1066 | 1090 | && nLeftDiff >= 6 |
| 1067 | 1091 | && nRightDiff >= 6 |
| 1068 | 1092 | && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS) |
| 1069 | 1093 | ){ |
| 1070 | | - sbsWriteLineno(p, lnLeft); |
| 1094 | + sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 1071 | 1095 | p->iStart = nPrefix; |
| 1072 | 1096 | p->iEnd = nPrefix + aLCS[0]; |
| 1073 | 1097 | if( aLCS[2]==0 ){ |
| 1074 | 1098 | sbsShiftLeft(p, pLeft->z); |
| 1075 | 1099 | p->zStart = zClassRm; |
| | @@ -1078,13 +1102,13 @@ |
| 1078 | 1102 | } |
| 1079 | 1103 | p->iStart2 = nPrefix + aLCS[1]; |
| 1080 | 1104 | p->iEnd2 = nLeft - nSuffix; |
| 1081 | 1105 | p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng; |
| 1082 | 1106 | sbsSimplifyLine(p, zLeft+nPrefix); |
| 1083 | | - sbsWriteText(p, pLeft, SBS_PAD); |
| 1084 | | - sbsWrite(p, " | ", 3); |
| 1085 | | - sbsWriteLineno(p, lnRight); |
| 1107 | + sbsWriteText(p, pLeft, SBS_TXTA); |
| 1108 | + sbsWriteMarker(p, " | ", "|"); |
| 1109 | + sbsWriteLineno(p, lnRight, SBS_LNB); |
| 1086 | 1110 | p->iStart = nPrefix; |
| 1087 | 1111 | p->iEnd = nPrefix + aLCS[2]; |
| 1088 | 1112 | if( aLCS[0]==0 ){ |
| 1089 | 1113 | sbsShiftLeft(p, pRight->z); |
| 1090 | 1114 | p->zStart = zClassAdd; |
| | @@ -1093,25 +1117,25 @@ |
| 1093 | 1117 | } |
| 1094 | 1118 | p->iStart2 = nPrefix + aLCS[3]; |
| 1095 | 1119 | p->iEnd2 = nRight - nSuffix; |
| 1096 | 1120 | p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng; |
| 1097 | 1121 | sbsSimplifyLine(p, zRight+nPrefix); |
| 1098 | | - sbsWriteText(p, pRight, SBS_NEWLINE); |
| 1122 | + sbsWriteText(p, pRight, SBS_TXTB); |
| 1099 | 1123 | return; |
| 1100 | 1124 | } |
| 1101 | 1125 | |
| 1102 | 1126 | /* If all else fails, show a single big change between left and right */ |
| 1103 | | - sbsWriteLineno(p, lnLeft); |
| 1127 | + sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 1104 | 1128 | p->iStart2 = p->iEnd2 = 0; |
| 1105 | 1129 | p->iStart = nPrefix; |
| 1106 | 1130 | p->iEnd = nLeft - nSuffix; |
| 1107 | 1131 | p->zStart = zClassChng; |
| 1108 | | - sbsWriteText(p, pLeft, SBS_PAD); |
| 1109 | | - sbsWrite(p, " | ", 3); |
| 1110 | | - sbsWriteLineno(p, lnRight); |
| 1132 | + sbsWriteText(p, pLeft, SBS_TXTA); |
| 1133 | + sbsWriteMarker(p, " | ", "|"); |
| 1134 | + sbsWriteLineno(p, lnRight, SBS_LNB); |
| 1111 | 1135 | p->iEnd = nRight - nSuffix; |
| 1112 | | - sbsWriteText(p, pRight, SBS_NEWLINE); |
| 1136 | + sbsWriteText(p, pRight, SBS_TXTB); |
| 1113 | 1137 | } |
| 1114 | 1138 | |
| 1115 | 1139 | /* |
| 1116 | 1140 | ** Minimum of two values |
| 1117 | 1141 | */ |
| | @@ -1357,30 +1381,40 @@ |
| 1357 | 1381 | int mxr; /* Maximum value for r */ |
| 1358 | 1382 | int na, nb; /* Number of lines shown from A and B */ |
| 1359 | 1383 | int i, j; /* Loop counters */ |
| 1360 | 1384 | int m, ma, mb;/* Number of lines to output */ |
| 1361 | 1385 | int skip; /* Number of lines to skip */ |
| 1362 | | - int nChunk = 0; /* Number of chunks of diff output seen so far */ |
| 1386 | + static int nChunk = 0; /* Number of chunks of diff output seen so far */ |
| 1363 | 1387 | SbsLine s; /* Output line buffer */ |
| 1364 | 1388 | int nContext; /* Lines of context above and below each change */ |
| 1365 | 1389 | int showDivider = 0; /* True to show the divider */ |
| 1390 | + Blob aCols[5]; /* Array of column blobs */ |
| 1366 | 1391 | |
| 1367 | 1392 | memset(&s, 0, sizeof(s)); |
| 1368 | 1393 | s.width = diff_width(diffFlags); |
| 1369 | | - s.zLine = fossil_malloc( 15*s.width + 200 ); |
| 1370 | | - if( s.zLine==0 ) return; |
| 1371 | 1394 | nContext = diff_context_lines(diffFlags); |
| 1372 | 1395 | s.escHtml = (diffFlags & DIFF_HTML)!=0; |
| 1396 | + if( s.escHtml ){ |
| 1397 | + for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1398 | + blob_zero(&aCols[i]); |
| 1399 | + s.apCols[i] = &aCols[i]; |
| 1400 | + } |
| 1401 | + }else{ |
| 1402 | + for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1403 | + s.apCols[i] = pOut; |
| 1404 | + } |
| 1405 | + } |
| 1373 | 1406 | s.pRe = pRe; |
| 1374 | 1407 | s.iStart = -1; |
| 1375 | 1408 | s.iStart2 = 0; |
| 1376 | 1409 | s.iEnd = -1; |
| 1377 | 1410 | A = p->aFrom; |
| 1378 | 1411 | B = p->aTo; |
| 1379 | 1412 | R = p->aEdit; |
| 1380 | 1413 | mxr = p->nEdit; |
| 1381 | 1414 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| 1415 | + |
| 1382 | 1416 | for(r=0; r<mxr; r += 3*nr){ |
| 1383 | 1417 | /* Figure out how many triples to show in a single block */ |
| 1384 | 1418 | for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){} |
| 1385 | 1419 | /* printf("r=%d nr=%d\n", r, nr); */ |
| 1386 | 1420 | |
| | @@ -1436,35 +1470,39 @@ |
| 1436 | 1470 | } |
| 1437 | 1471 | |
| 1438 | 1472 | /* Draw the separator between blocks */ |
| 1439 | 1473 | if( showDivider ){ |
| 1440 | 1474 | if( s.escHtml ){ |
| 1441 | | - blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n", |
| 1442 | | - s.width*2+16, '.'); |
| 1475 | + char zLn[10]; |
| 1476 | + sqlite3_snprintf(sizeof(zLn), zLn, "%d", a+skip+1); |
| 1477 | + sbsWriteSep(&s, strlen(zLn), SBS_LNA); |
| 1478 | + sbsWriteSep(&s, s.width, SBS_TXTA); |
| 1479 | + sbsWriteSep(&s, 0, SBS_MKR); |
| 1480 | + sqlite3_snprintf(sizeof(zLn), zLn, "%d", b+skip+1); |
| 1481 | + sbsWriteSep(&s, strlen(zLn), SBS_LNB); |
| 1482 | + sbsWriteSep(&s, s.width, SBS_TXTB); |
| 1443 | 1483 | }else{ |
| 1444 | 1484 | blob_appendf(pOut, "%.*c\n", s.width*2+16, '.'); |
| 1445 | 1485 | } |
| 1446 | 1486 | } |
| 1447 | 1487 | showDivider = 1; |
| 1448 | 1488 | nChunk++; |
| 1449 | 1489 | if( s.escHtml ){ |
| 1450 | | - blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); |
| 1490 | + blob_appendf(s.apCols[SBS_LNA], "<span id=\"chunk%d\"></span>", nChunk); |
| 1451 | 1491 | } |
| 1452 | 1492 | |
| 1453 | 1493 | /* Show the initial common area */ |
| 1454 | 1494 | a += skip; |
| 1455 | 1495 | b += skip; |
| 1456 | 1496 | m = R[r] - skip; |
| 1457 | 1497 | for(j=0; j<m; j++){ |
| 1458 | | - s.n = 0; |
| 1459 | | - sbsWriteLineno(&s, a+j); |
| 1498 | + sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1460 | 1499 | s.iStart = s.iEnd = -1; |
| 1461 | | - sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 1462 | | - sbsWrite(&s, " ", 3); |
| 1463 | | - sbsWriteLineno(&s, b+j); |
| 1464 | | - sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 1465 | | - blob_append(pOut, s.zLine, s.n); |
| 1500 | + sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1501 | + sbsWriteMarker(&s, " ", ""); |
| 1502 | + sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1503 | + sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1466 | 1504 | } |
| 1467 | 1505 | a += m; |
| 1468 | 1506 | b += m; |
| 1469 | 1507 | |
| 1470 | 1508 | /* Show the differences */ |
| | @@ -1485,87 +1523,71 @@ |
| 1485 | 1523 | |
| 1486 | 1524 | alignment = sbsAlignment(&A[a], ma, &B[b], mb); |
| 1487 | 1525 | for(j=0; ma+mb>0; j++){ |
| 1488 | 1526 | if( alignment[j]==1 ){ |
| 1489 | 1527 | /* Delete one line from the left */ |
| 1490 | | - s.n = 0; |
| 1491 | | - sbsWriteLineno(&s, a); |
| 1528 | + sbsWriteLineno(&s, a, SBS_LNA); |
| 1492 | 1529 | s.iStart = 0; |
| 1493 | 1530 | s.zStart = "<span class=\"diffrm\">"; |
| 1494 | 1531 | s.iEnd = LENGTH(&A[a]); |
| 1495 | | - sbsWriteText(&s, &A[a], SBS_PAD); |
| 1496 | | - if( s.escHtml ){ |
| 1497 | | - sbsWrite(&s, " <\n", 6); |
| 1498 | | - }else{ |
| 1499 | | - sbsWrite(&s, " <\n", 3); |
| 1500 | | - } |
| 1501 | | - blob_append(pOut, s.zLine, s.n); |
| 1532 | + sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1533 | + sbsWriteMarker(&s, " <", "<"); |
| 1534 | + sbsWriteNewlines(&s); |
| 1502 | 1535 | assert( ma>0 ); |
| 1503 | 1536 | ma--; |
| 1504 | 1537 | a++; |
| 1505 | 1538 | }else if( alignment[j]==3 ){ |
| 1506 | 1539 | /* The left line is changed into the right line */ |
| 1507 | | - s.n = 0; |
| 1508 | 1540 | sbsWriteLineChange(&s, &A[a], a, &B[b], b); |
| 1509 | | - blob_append(pOut, s.zLine, s.n); |
| 1510 | 1541 | assert( ma>0 && mb>0 ); |
| 1511 | 1542 | ma--; |
| 1512 | 1543 | mb--; |
| 1513 | 1544 | a++; |
| 1514 | 1545 | b++; |
| 1515 | 1546 | }else if( alignment[j]==2 ){ |
| 1516 | 1547 | /* Insert one line on the right */ |
| 1517 | | - s.n = 0; |
| 1518 | | - sbsWriteSpace(&s, s.width + 7); |
| 1519 | | - if( s.escHtml ){ |
| 1520 | | - sbsWrite(&s, " > ", 6); |
| 1521 | | - }else{ |
| 1522 | | - sbsWrite(&s, " > ", 3); |
| 1523 | | - } |
| 1524 | | - sbsWriteLineno(&s, b); |
| 1548 | + if( !s.escHtml ){ |
| 1549 | + sbsWriteSpace(&s, s.width + 7, SBS_TXTA); |
| 1550 | + } |
| 1551 | + sbsWriteMarker(&s, " > ", ">"); |
| 1552 | + sbsWriteLineno(&s, b, SBS_LNB); |
| 1525 | 1553 | s.iStart = 0; |
| 1526 | 1554 | s.zStart = "<span class=\"diffadd\">"; |
| 1527 | 1555 | s.iEnd = LENGTH(&B[b]); |
| 1528 | | - sbsWriteText(&s, &B[b], SBS_NEWLINE); |
| 1529 | | - blob_append(pOut, s.zLine, s.n); |
| 1556 | + sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1530 | 1557 | assert( mb>0 ); |
| 1531 | 1558 | mb--; |
| 1532 | 1559 | b++; |
| 1533 | 1560 | }else{ |
| 1534 | 1561 | /* Delete from the left and insert on the right */ |
| 1535 | | - s.n = 0; |
| 1536 | | - sbsWriteLineno(&s, a); |
| 1562 | + sbsWriteLineno(&s, a, SBS_LNA); |
| 1537 | 1563 | s.iStart = 0; |
| 1538 | 1564 | s.zStart = "<span class=\"diffrm\">"; |
| 1539 | 1565 | s.iEnd = LENGTH(&A[a]); |
| 1540 | | - sbsWriteText(&s, &A[a], SBS_PAD); |
| 1541 | | - sbsWrite(&s, " | ", 3); |
| 1542 | | - sbsWriteLineno(&s, b); |
| 1566 | + sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1567 | + sbsWriteMarker(&s, " | ", "|"); |
| 1568 | + sbsWriteLineno(&s, b, SBS_LNB); |
| 1543 | 1569 | s.iStart = 0; |
| 1544 | 1570 | s.zStart = "<span class=\"diffadd\">"; |
| 1545 | 1571 | s.iEnd = LENGTH(&B[b]); |
| 1546 | | - sbsWriteText(&s, &B[b], SBS_NEWLINE); |
| 1547 | | - blob_append(pOut, s.zLine, s.n); |
| 1572 | + sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1548 | 1573 | ma--; |
| 1549 | 1574 | mb--; |
| 1550 | 1575 | a++; |
| 1551 | 1576 | b++; |
| 1552 | 1577 | } |
| 1553 | | - |
| 1554 | 1578 | } |
| 1555 | 1579 | fossil_free(alignment); |
| 1556 | 1580 | if( i<nr-1 ){ |
| 1557 | 1581 | m = R[r+i*3+3]; |
| 1558 | 1582 | for(j=0; j<m; j++){ |
| 1559 | | - s.n = 0; |
| 1560 | | - sbsWriteLineno(&s, a+j); |
| 1583 | + sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1561 | 1584 | s.iStart = s.iEnd = -1; |
| 1562 | | - sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 1563 | | - sbsWrite(&s, " ", 3); |
| 1564 | | - sbsWriteLineno(&s, b+j); |
| 1565 | | - sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 1566 | | - blob_append(pOut, s.zLine, s.n); |
| 1585 | + sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1586 | + sbsWriteMarker(&s, " ", ""); |
| 1587 | + sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1588 | + sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1567 | 1589 | } |
| 1568 | 1590 | b += m; |
| 1569 | 1591 | a += m; |
| 1570 | 1592 | } |
| 1571 | 1593 | } |
| | @@ -1573,21 +1595,27 @@ |
| 1573 | 1595 | /* Show the final common area */ |
| 1574 | 1596 | assert( nr==i ); |
| 1575 | 1597 | m = R[r+nr*3]; |
| 1576 | 1598 | if( m>nContext ) m = nContext; |
| 1577 | 1599 | for(j=0; j<m; j++){ |
| 1578 | | - s.n = 0; |
| 1579 | | - sbsWriteLineno(&s, a+j); |
| 1600 | + sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1580 | 1601 | s.iStart = s.iEnd = -1; |
| 1581 | | - sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 1582 | | - sbsWrite(&s, " ", 3); |
| 1583 | | - sbsWriteLineno(&s, b+j); |
| 1584 | | - sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 1585 | | - blob_append(pOut, s.zLine, s.n); |
| 1602 | + sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1603 | + sbsWriteMarker(&s, " ", ""); |
| 1604 | + sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1605 | + sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1586 | 1606 | } |
| 1587 | 1607 | } |
| 1588 | | - free(s.zLine); |
| 1608 | + |
| 1609 | + if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){ |
| 1610 | + blob_append(pOut, "<table class=\"sbsdiffcols\"><tr>\n", -1); |
| 1611 | + for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1612 | + sbsWriteColumn(pOut, s.apCols[i], i); |
| 1613 | + blob_reset(s.apCols[i]); |
| 1614 | + } |
| 1615 | + blob_append(pOut, "</tr></table>\n", -1); |
| 1616 | + } |
| 1589 | 1617 | } |
| 1590 | 1618 | |
| 1591 | 1619 | /* |
| 1592 | 1620 | ** Compute the optimal longest common subsequence (LCS) using an |
| 1593 | 1621 | ** exhaustive search. This version of the LCS is only used for |
| | @@ -1990,10 +2018,21 @@ |
| 1990 | 2018 | int diff_width(u64 diffFlags){ |
| 1991 | 2019 | int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1); |
| 1992 | 2020 | if( w==0 ) w = 80; |
| 1993 | 2021 | return w; |
| 1994 | 2022 | } |
| 2023 | + |
| 2024 | +/* |
| 2025 | +** Append the error message to pOut. |
| 2026 | +*/ |
| 2027 | +void diff_errmsg(Blob *pOut, const char *msg, int diffFlags){ |
| 2028 | + if( diffFlags & DIFF_HTML ){ |
| 2029 | + blob_appendf(pOut, "<p class=\"generalError\">%s</p>", msg); |
| 2030 | + }else{ |
| 2031 | + blob_append(pOut, msg, -1); |
| 2032 | + } |
| 2033 | +} |
| 1995 | 2034 | |
| 1996 | 2035 | /* |
| 1997 | 2036 | ** Generate a report of the differences between files pA and pB. |
| 1998 | 2037 | ** If pOut is not NULL then a unified diff is appended there. It |
| 1999 | 2038 | ** is assumed that pOut has already been initialized. If pOut is |
| | @@ -2032,11 +2071,11 @@ |
| 2032 | 2071 | &c.nTo, ignoreEolWs); |
| 2033 | 2072 | if( c.aFrom==0 || c.aTo==0 ){ |
| 2034 | 2073 | fossil_free(c.aFrom); |
| 2035 | 2074 | fossil_free(c.aTo); |
| 2036 | 2075 | if( pOut ){ |
| 2037 | | - blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY); |
| 2076 | + diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags); |
| 2038 | 2077 | } |
| 2039 | 2078 | return 0; |
| 2040 | 2079 | } |
| 2041 | 2080 | |
| 2042 | 2081 | /* Compute the difference */ |
| | @@ -2048,15 +2087,11 @@ |
| 2048 | 2087 | for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; } |
| 2049 | 2088 | if( n>10000 ){ |
| 2050 | 2089 | fossil_free(c.aFrom); |
| 2051 | 2090 | fossil_free(c.aTo); |
| 2052 | 2091 | fossil_free(c.aEdit); |
| 2053 | | - if( diffFlags & DIFF_HTML ){ |
| 2054 | | - blob_append(pOut, DIFF_TOO_MANY_CHANGES_HTML, -1); |
| 2055 | | - }else{ |
| 2056 | | - blob_append(pOut, DIFF_TOO_MANY_CHANGES_TXT, -1); |
| 2057 | | - } |
| 2092 | + diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags); |
| 2058 | 2093 | return 0; |
| 2059 | 2094 | } |
| 2060 | 2095 | } |
| 2061 | 2096 | if( (diffFlags & DIFF_NOOPT)==0 ){ |
| 2062 | 2097 | diff_optimize(&c); |
| | @@ -2159,10 +2194,11 @@ |
| 2159 | 2194 | if( find_option("tk",0,0)!=0 ){ |
| 2160 | 2195 | diff_tk("test-diff", 2); |
| 2161 | 2196 | return; |
| 2162 | 2197 | } |
| 2163 | 2198 | find_option("i",0,0); |
| 2199 | + find_option("v",0,0); |
| 2164 | 2200 | zRe = find_option("regexp","e",1); |
| 2165 | 2201 | if( zRe ){ |
| 2166 | 2202 | const char *zErr = re_compile(&pRe, zRe, 0); |
| 2167 | 2203 | if( zErr ) fossil_fatal("regex error: %s", zErr); |
| 2168 | 2204 | } |
| 2169 | 2205 | |