Fossil SCM
Fix handling of backslash and tab in quoted JSON text for the --json option.
Commit
9a30e83d88d7ab4cd51a5a8fddc994be06f537df1735e39208815edaccfd092e
Parent
5d759da71116fba…
1 file changed
+98
-34
+98
-34
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -1722,62 +1722,126 @@ | ||
| 1722 | 1722 | p->pOut = pOut; |
| 1723 | 1723 | return p; |
| 1724 | 1724 | } |
| 1725 | 1725 | |
| 1726 | 1726 | /************************* DiffBuilderJson ********************************/ |
| 1727 | + | |
| 1728 | +/* Convert raw text into content suitable for a JSON string. Escape | |
| 1729 | +** charaters that are special to HTML and to JSON: | |
| 1730 | +** | |
| 1731 | +** < -> < | |
| 1732 | +** > -> > | |
| 1733 | +** & -> & | |
| 1734 | +** " -> " | |
| 1735 | +** ' -> ' | |
| 1736 | +** \ -> \ | |
| 1737 | +** | |
| 1738 | +** In addition, TAB characters are converted into an appropriate number | |
| 1739 | +** of spaces. The *piCol value is the number of prior columns in the | |
| 1740 | +** current line. Update the column count before returning, so that | |
| 1741 | +** subsequent invocations can figure out the right number of spaces to | |
| 1742 | +** insert for each tab. | |
| 1743 | +*/ | |
| 1744 | +static void jsonize_to_blob(Blob *p, const char *zIn, int n, int *piCol){ | |
| 1745 | + int c, i, x; | |
| 1746 | + int iCol = *piCol; | |
| 1747 | + for(i=0; i<n; i++){ | |
| 1748 | + c = zIn[i]; | |
| 1749 | + switch( c ){ | |
| 1750 | + case '<': | |
| 1751 | + blob_append(p, "<", 4); | |
| 1752 | + iCol++; | |
| 1753 | + break; | |
| 1754 | + case '>': | |
| 1755 | + blob_append(p, ">", 4); | |
| 1756 | + iCol++; | |
| 1757 | + break; | |
| 1758 | + case '&': | |
| 1759 | + blob_append(p, "&", 5); | |
| 1760 | + iCol++; | |
| 1761 | + break; | |
| 1762 | + case '"': | |
| 1763 | + blob_append(p, """, 6); | |
| 1764 | + iCol++; | |
| 1765 | + break; | |
| 1766 | + case '\'': | |
| 1767 | + blob_append(p, "'", 5); | |
| 1768 | + iCol++; | |
| 1769 | + break; | |
| 1770 | + case '\\': | |
| 1771 | + blob_append(p, "\", 5); | |
| 1772 | + iCol++; | |
| 1773 | + break; | |
| 1774 | + case '\t': | |
| 1775 | + x = 1 + (iCol+7)%8; | |
| 1776 | + blob_appendf(p, "%*s", x, ""); | |
| 1777 | + iCol += x; | |
| 1778 | + break; | |
| 1779 | + default: | |
| 1780 | + blob_append_char(p, c); | |
| 1781 | + if( (c&0xc0)!=0x80 ) iCol++; | |
| 1782 | + break; | |
| 1783 | + } | |
| 1784 | + } | |
| 1785 | + *piCol = iCol; | |
| 1786 | +} | |
| 1727 | 1787 | static void dfjsonSkip(DiffBuilder *p, unsigned int n){ |
| 1728 | 1788 | blob_appendf(p->pOut, "1,%u,\n", n); |
| 1729 | 1789 | } |
| 1730 | 1790 | static void dfjsonCommon(DiffBuilder *p, const DLine *pLine){ |
| 1791 | + int iCol = 0; | |
| 1731 | 1792 | blob_append(p->pOut, "2,\"",3); |
| 1732 | - htmlize_to_blob(p->pOut, pLine->z, (int)pLine->n); | |
| 1793 | + jsonize_to_blob(p->pOut, pLine->z, (int)pLine->n, &iCol); | |
| 1733 | 1794 | blob_append(p->pOut, "\",\n",3); |
| 1734 | 1795 | } |
| 1735 | 1796 | static void dfjsonInsert(DiffBuilder *p, const DLine *pLine){ |
| 1797 | + int iCol = 0; | |
| 1736 | 1798 | blob_append(p->pOut, "3,\"",3); |
| 1737 | - htmlize_to_blob(p->pOut, pLine->z, (int)pLine->n); | |
| 1799 | + jsonize_to_blob(p->pOut, pLine->z, (int)pLine->n, &iCol); | |
| 1738 | 1800 | blob_append(p->pOut, "\",\n",3); |
| 1739 | 1801 | } |
| 1740 | 1802 | static void dfjsonDelete(DiffBuilder *p, const DLine *pLine){ |
| 1803 | + int iCol = 0; | |
| 1741 | 1804 | blob_append(p->pOut, "4,\"",3); |
| 1742 | - htmlize_to_blob(p->pOut, pLine->z, (int)pLine->n); | |
| 1805 | + jsonize_to_blob(p->pOut, pLine->z, (int)pLine->n, &iCol); | |
| 1743 | 1806 | blob_append(p->pOut, "\",\n",3); |
| 1744 | 1807 | } |
| 1745 | 1808 | static void dfjsonEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ |
| 1746 | 1809 | int i; |
| 1747 | 1810 | int x; |
| 1748 | - ChangeSpan span; | |
| 1749 | - oneLineChange(pX, pY, &span); | |
| 1750 | - blob_appendf(p->pOut, "5,\""); | |
| 1751 | - for(i=x=0; i<span.n; i++){ | |
| 1752 | - int ofst = span.a[i].iStart1; | |
| 1753 | - int len = span.a[i].iLen1; | |
| 1754 | - if( len ){ | |
| 1755 | - htmlize_to_blob(p->pOut, pX->z+x, ofst - x); | |
| 1756 | - x += ofst; | |
| 1757 | - blob_append(p->pOut, "<mark>", 6); | |
| 1758 | - htmlize_to_blob(p->pOut, pX->z+x, len); | |
| 1759 | - x += len; | |
| 1760 | - blob_append(p->pOut, "</mark>", 7); | |
| 1761 | - } | |
| 1762 | - } | |
| 1763 | - if( x<pX->n ) htmlize_to_blob(p->pOut, pX->z+x, pX->n - x); | |
| 1764 | - blob_append(p->pOut, "\",\n \"", -1); | |
| 1765 | - for(i=x=0; i<span.n; i++){ | |
| 1766 | - int ofst = span.a[i].iStart2; | |
| 1767 | - int len = span.a[i].iLen2; | |
| 1768 | - if( len ){ | |
| 1769 | - htmlize_to_blob(p->pOut, pY->z+x, ofst - x); | |
| 1770 | - x += ofst; | |
| 1771 | - blob_append(p->pOut, "<mark>", 6); | |
| 1772 | - htmlize_to_blob(p->pOut, pY->z+x, len); | |
| 1773 | - x += len; | |
| 1774 | - blob_append(p->pOut, "</mark>", 7); | |
| 1775 | - } | |
| 1776 | - } | |
| 1777 | - if( x<pY->n ) htmlize_to_blob(p->pOut, pY->z+x, pY->n - x); | |
| 1778 | - blob_append(p->pOut, "\"\n,", -1); | |
| 1811 | + int iCol; | |
| 1812 | + ChangeSpan span; | |
| 1813 | + oneLineChange(pX, pY, &span); | |
| 1814 | + blob_appendf(p->pOut, "5,\""); | |
| 1815 | + for(i=x=iCol=0; i<span.n; i++){ | |
| 1816 | + int ofst = span.a[i].iStart1; | |
| 1817 | + int len = span.a[i].iLen1; | |
| 1818 | + if( len ){ | |
| 1819 | + jsonize_to_blob(p->pOut, pX->z+x, ofst - x, &iCol); | |
| 1820 | + x += ofst; | |
| 1821 | + blob_append(p->pOut, "<mark>", 6); | |
| 1822 | + jsonize_to_blob(p->pOut, pX->z+x, len, &iCol); | |
| 1823 | + x += len; | |
| 1824 | + blob_append(p->pOut, "</mark>", 7); | |
| 1825 | + } | |
| 1826 | + } | |
| 1827 | + if( x<pX->n ) jsonize_to_blob(p->pOut, pX->z+x, pX->n - x, &iCol); | |
| 1828 | + blob_append(p->pOut, "\",\n \"", -1); | |
| 1829 | + for(i=x=iCol=0; i<span.n; i++){ | |
| 1830 | + int ofst = span.a[i].iStart2; | |
| 1831 | + int len = span.a[i].iLen2; | |
| 1832 | + if( len ){ | |
| 1833 | + jsonize_to_blob(p->pOut, pY->z+x, ofst - x, &iCol); | |
| 1834 | + x += ofst; | |
| 1835 | + blob_append(p->pOut, "<mark>", 6); | |
| 1836 | + jsonize_to_blob(p->pOut, pY->z+x, len, &iCol); | |
| 1837 | + x += len; | |
| 1838 | + blob_append(p->pOut, "</mark>", 7); | |
| 1839 | + } | |
| 1840 | + } | |
| 1841 | + if( x<pY->n ) jsonize_to_blob(p->pOut, pY->z+x, pY->n - x, &iCol); | |
| 1842 | + blob_append(p->pOut, "\",\n", -1); | |
| 1779 | 1843 | } |
| 1780 | 1844 | static void dfjsonEnd(DiffBuilder *p){ |
| 1781 | 1845 | blob_append(p->pOut, "0]", 2); |
| 1782 | 1846 | fossil_free(p); |
| 1783 | 1847 | } |
| 1784 | 1848 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -1722,62 +1722,126 @@ | |
| 1722 | p->pOut = pOut; |
| 1723 | return p; |
| 1724 | } |
| 1725 | |
| 1726 | /************************* DiffBuilderJson ********************************/ |
| 1727 | static void dfjsonSkip(DiffBuilder *p, unsigned int n){ |
| 1728 | blob_appendf(p->pOut, "1,%u,\n", n); |
| 1729 | } |
| 1730 | static void dfjsonCommon(DiffBuilder *p, const DLine *pLine){ |
| 1731 | blob_append(p->pOut, "2,\"",3); |
| 1732 | htmlize_to_blob(p->pOut, pLine->z, (int)pLine->n); |
| 1733 | blob_append(p->pOut, "\",\n",3); |
| 1734 | } |
| 1735 | static void dfjsonInsert(DiffBuilder *p, const DLine *pLine){ |
| 1736 | blob_append(p->pOut, "3,\"",3); |
| 1737 | htmlize_to_blob(p->pOut, pLine->z, (int)pLine->n); |
| 1738 | blob_append(p->pOut, "\",\n",3); |
| 1739 | } |
| 1740 | static void dfjsonDelete(DiffBuilder *p, const DLine *pLine){ |
| 1741 | blob_append(p->pOut, "4,\"",3); |
| 1742 | htmlize_to_blob(p->pOut, pLine->z, (int)pLine->n); |
| 1743 | blob_append(p->pOut, "\",\n",3); |
| 1744 | } |
| 1745 | static void dfjsonEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ |
| 1746 | int i; |
| 1747 | int x; |
| 1748 | ChangeSpan span; |
| 1749 | oneLineChange(pX, pY, &span); |
| 1750 | blob_appendf(p->pOut, "5,\""); |
| 1751 | for(i=x=0; i<span.n; i++){ |
| 1752 | int ofst = span.a[i].iStart1; |
| 1753 | int len = span.a[i].iLen1; |
| 1754 | if( len ){ |
| 1755 | htmlize_to_blob(p->pOut, pX->z+x, ofst - x); |
| 1756 | x += ofst; |
| 1757 | blob_append(p->pOut, "<mark>", 6); |
| 1758 | htmlize_to_blob(p->pOut, pX->z+x, len); |
| 1759 | x += len; |
| 1760 | blob_append(p->pOut, "</mark>", 7); |
| 1761 | } |
| 1762 | } |
| 1763 | if( x<pX->n ) htmlize_to_blob(p->pOut, pX->z+x, pX->n - x); |
| 1764 | blob_append(p->pOut, "\",\n \"", -1); |
| 1765 | for(i=x=0; i<span.n; i++){ |
| 1766 | int ofst = span.a[i].iStart2; |
| 1767 | int len = span.a[i].iLen2; |
| 1768 | if( len ){ |
| 1769 | htmlize_to_blob(p->pOut, pY->z+x, ofst - x); |
| 1770 | x += ofst; |
| 1771 | blob_append(p->pOut, "<mark>", 6); |
| 1772 | htmlize_to_blob(p->pOut, pY->z+x, len); |
| 1773 | x += len; |
| 1774 | blob_append(p->pOut, "</mark>", 7); |
| 1775 | } |
| 1776 | } |
| 1777 | if( x<pY->n ) htmlize_to_blob(p->pOut, pY->z+x, pY->n - x); |
| 1778 | blob_append(p->pOut, "\"\n,", -1); |
| 1779 | } |
| 1780 | static void dfjsonEnd(DiffBuilder *p){ |
| 1781 | blob_append(p->pOut, "0]", 2); |
| 1782 | fossil_free(p); |
| 1783 | } |
| 1784 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -1722,62 +1722,126 @@ | |
| 1722 | p->pOut = pOut; |
| 1723 | return p; |
| 1724 | } |
| 1725 | |
| 1726 | /************************* DiffBuilderJson ********************************/ |
| 1727 | |
| 1728 | /* Convert raw text into content suitable for a JSON string. Escape |
| 1729 | ** charaters that are special to HTML and to JSON: |
| 1730 | ** |
| 1731 | ** < -> < |
| 1732 | ** > -> > |
| 1733 | ** & -> & |
| 1734 | ** " -> " |
| 1735 | ** ' -> ' |
| 1736 | ** \ -> \ |
| 1737 | ** |
| 1738 | ** In addition, TAB characters are converted into an appropriate number |
| 1739 | ** of spaces. The *piCol value is the number of prior columns in the |
| 1740 | ** current line. Update the column count before returning, so that |
| 1741 | ** subsequent invocations can figure out the right number of spaces to |
| 1742 | ** insert for each tab. |
| 1743 | */ |
| 1744 | static void jsonize_to_blob(Blob *p, const char *zIn, int n, int *piCol){ |
| 1745 | int c, i, x; |
| 1746 | int iCol = *piCol; |
| 1747 | for(i=0; i<n; i++){ |
| 1748 | c = zIn[i]; |
| 1749 | switch( c ){ |
| 1750 | case '<': |
| 1751 | blob_append(p, "<", 4); |
| 1752 | iCol++; |
| 1753 | break; |
| 1754 | case '>': |
| 1755 | blob_append(p, ">", 4); |
| 1756 | iCol++; |
| 1757 | break; |
| 1758 | case '&': |
| 1759 | blob_append(p, "&", 5); |
| 1760 | iCol++; |
| 1761 | break; |
| 1762 | case '"': |
| 1763 | blob_append(p, """, 6); |
| 1764 | iCol++; |
| 1765 | break; |
| 1766 | case '\'': |
| 1767 | blob_append(p, "'", 5); |
| 1768 | iCol++; |
| 1769 | break; |
| 1770 | case '\\': |
| 1771 | blob_append(p, "\", 5); |
| 1772 | iCol++; |
| 1773 | break; |
| 1774 | case '\t': |
| 1775 | x = 1 + (iCol+7)%8; |
| 1776 | blob_appendf(p, "%*s", x, ""); |
| 1777 | iCol += x; |
| 1778 | break; |
| 1779 | default: |
| 1780 | blob_append_char(p, c); |
| 1781 | if( (c&0xc0)!=0x80 ) iCol++; |
| 1782 | break; |
| 1783 | } |
| 1784 | } |
| 1785 | *piCol = iCol; |
| 1786 | } |
| 1787 | static void dfjsonSkip(DiffBuilder *p, unsigned int n){ |
| 1788 | blob_appendf(p->pOut, "1,%u,\n", n); |
| 1789 | } |
| 1790 | static void dfjsonCommon(DiffBuilder *p, const DLine *pLine){ |
| 1791 | int iCol = 0; |
| 1792 | blob_append(p->pOut, "2,\"",3); |
| 1793 | jsonize_to_blob(p->pOut, pLine->z, (int)pLine->n, &iCol); |
| 1794 | blob_append(p->pOut, "\",\n",3); |
| 1795 | } |
| 1796 | static void dfjsonInsert(DiffBuilder *p, const DLine *pLine){ |
| 1797 | int iCol = 0; |
| 1798 | blob_append(p->pOut, "3,\"",3); |
| 1799 | jsonize_to_blob(p->pOut, pLine->z, (int)pLine->n, &iCol); |
| 1800 | blob_append(p->pOut, "\",\n",3); |
| 1801 | } |
| 1802 | static void dfjsonDelete(DiffBuilder *p, const DLine *pLine){ |
| 1803 | int iCol = 0; |
| 1804 | blob_append(p->pOut, "4,\"",3); |
| 1805 | jsonize_to_blob(p->pOut, pLine->z, (int)pLine->n, &iCol); |
| 1806 | blob_append(p->pOut, "\",\n",3); |
| 1807 | } |
| 1808 | static void dfjsonEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ |
| 1809 | int i; |
| 1810 | int x; |
| 1811 | int iCol; |
| 1812 | ChangeSpan span; |
| 1813 | oneLineChange(pX, pY, &span); |
| 1814 | blob_appendf(p->pOut, "5,\""); |
| 1815 | for(i=x=iCol=0; i<span.n; i++){ |
| 1816 | int ofst = span.a[i].iStart1; |
| 1817 | int len = span.a[i].iLen1; |
| 1818 | if( len ){ |
| 1819 | jsonize_to_blob(p->pOut, pX->z+x, ofst - x, &iCol); |
| 1820 | x += ofst; |
| 1821 | blob_append(p->pOut, "<mark>", 6); |
| 1822 | jsonize_to_blob(p->pOut, pX->z+x, len, &iCol); |
| 1823 | x += len; |
| 1824 | blob_append(p->pOut, "</mark>", 7); |
| 1825 | } |
| 1826 | } |
| 1827 | if( x<pX->n ) jsonize_to_blob(p->pOut, pX->z+x, pX->n - x, &iCol); |
| 1828 | blob_append(p->pOut, "\",\n \"", -1); |
| 1829 | for(i=x=iCol=0; i<span.n; i++){ |
| 1830 | int ofst = span.a[i].iStart2; |
| 1831 | int len = span.a[i].iLen2; |
| 1832 | if( len ){ |
| 1833 | jsonize_to_blob(p->pOut, pY->z+x, ofst - x, &iCol); |
| 1834 | x += ofst; |
| 1835 | blob_append(p->pOut, "<mark>", 6); |
| 1836 | jsonize_to_blob(p->pOut, pY->z+x, len, &iCol); |
| 1837 | x += len; |
| 1838 | blob_append(p->pOut, "</mark>", 7); |
| 1839 | } |
| 1840 | } |
| 1841 | if( x<pY->n ) jsonize_to_blob(p->pOut, pY->z+x, pY->n - x, &iCol); |
| 1842 | blob_append(p->pOut, "\",\n", -1); |
| 1843 | } |
| 1844 | static void dfjsonEnd(DiffBuilder *p){ |
| 1845 | blob_append(p->pOut, "0]", 2); |
| 1846 | fossil_free(p); |
| 1847 | } |
| 1848 |