Fossil SCM
Add the %j formatting directive to the customized printf() inside of Fossil. The %j format generates a string with appropriate backslash escapes so that the string can be part of a Javascript string literal.
Commit
5e9c1d5e049b09526ebb0519b5e6b1530a59e1d554ade8ebdbb564bb77a6a8e8
Parent
003281772c841fd…
3 files changed
+8
-5
+11
+5
-7
+8
-5
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -376,13 +376,15 @@ | ||
| 376 | 376 | } |
| 377 | 377 | return c; |
| 378 | 378 | } |
| 379 | 379 | |
| 380 | 380 | /* |
| 381 | -** Encode a UTF8 string for JSON. All special characters are escaped. | |
| 381 | +** Encode a UTF8 string as a JSON string literal (without the surrounding | |
| 382 | +** "...") and return a pointer to the encoding. Space to hold the encoding | |
| 383 | +** is obtained from fossil_malloc() and must be freed by the caller. | |
| 382 | 384 | */ |
| 383 | -void blob_append_json_string(Blob *pBlob, const char *zStr){ | |
| 385 | +char *encode_json_string_literal(const char *zStr){ | |
| 384 | 386 | const unsigned char *z; |
| 385 | 387 | char *zOut; |
| 386 | 388 | u32 c; |
| 387 | 389 | int n, i, j; |
| 388 | 390 | z = (const unsigned char*)zStr; |
| @@ -398,14 +400,14 @@ | ||
| 398 | 400 | } |
| 399 | 401 | }else{ |
| 400 | 402 | n++; |
| 401 | 403 | } |
| 402 | 404 | } |
| 403 | - i = blob_size(pBlob); | |
| 404 | - blob_resize(pBlob, i+n); | |
| 405 | - zOut = blob_buffer(pBlob); | |
| 405 | + zOut = fossil_malloc(n+1); | |
| 406 | + if( zOut==0 ) return 0; | |
| 406 | 407 | z = (const unsigned char*)zStr; |
| 408 | + i = 0; | |
| 407 | 409 | while( (c = fossil_utf8_read(&z))!=0 ){ |
| 408 | 410 | if( c=='\\' ){ |
| 409 | 411 | zOut[i++] = '\\'; |
| 410 | 412 | zOut[i++] = c; |
| 411 | 413 | }else if( c<' ' || c>=0x7f ){ |
| @@ -425,10 +427,11 @@ | ||
| 425 | 427 | }else{ |
| 426 | 428 | zOut[i++] = c; |
| 427 | 429 | } |
| 428 | 430 | } |
| 429 | 431 | zOut[i] = 0; |
| 432 | + return zOut; | |
| 430 | 433 | } |
| 431 | 434 | |
| 432 | 435 | /* |
| 433 | 436 | ** The characters used for HTTP base64 encoding. |
| 434 | 437 | */ |
| 435 | 438 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -376,13 +376,15 @@ | |
| 376 | } |
| 377 | return c; |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | ** Encode a UTF8 string for JSON. All special characters are escaped. |
| 382 | */ |
| 383 | void blob_append_json_string(Blob *pBlob, const char *zStr){ |
| 384 | const unsigned char *z; |
| 385 | char *zOut; |
| 386 | u32 c; |
| 387 | int n, i, j; |
| 388 | z = (const unsigned char*)zStr; |
| @@ -398,14 +400,14 @@ | |
| 398 | } |
| 399 | }else{ |
| 400 | n++; |
| 401 | } |
| 402 | } |
| 403 | i = blob_size(pBlob); |
| 404 | blob_resize(pBlob, i+n); |
| 405 | zOut = blob_buffer(pBlob); |
| 406 | z = (const unsigned char*)zStr; |
| 407 | while( (c = fossil_utf8_read(&z))!=0 ){ |
| 408 | if( c=='\\' ){ |
| 409 | zOut[i++] = '\\'; |
| 410 | zOut[i++] = c; |
| 411 | }else if( c<' ' || c>=0x7f ){ |
| @@ -425,10 +427,11 @@ | |
| 425 | }else{ |
| 426 | zOut[i++] = c; |
| 427 | } |
| 428 | } |
| 429 | zOut[i] = 0; |
| 430 | } |
| 431 | |
| 432 | /* |
| 433 | ** The characters used for HTTP base64 encoding. |
| 434 | */ |
| 435 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -376,13 +376,15 @@ | |
| 376 | } |
| 377 | return c; |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | ** Encode a UTF8 string as a JSON string literal (without the surrounding |
| 382 | ** "...") and return a pointer to the encoding. Space to hold the encoding |
| 383 | ** is obtained from fossil_malloc() and must be freed by the caller. |
| 384 | */ |
| 385 | char *encode_json_string_literal(const char *zStr){ |
| 386 | const unsigned char *z; |
| 387 | char *zOut; |
| 388 | u32 c; |
| 389 | int n, i, j; |
| 390 | z = (const unsigned char*)zStr; |
| @@ -398,14 +400,14 @@ | |
| 400 | } |
| 401 | }else{ |
| 402 | n++; |
| 403 | } |
| 404 | } |
| 405 | zOut = fossil_malloc(n+1); |
| 406 | if( zOut==0 ) return 0; |
| 407 | z = (const unsigned char*)zStr; |
| 408 | i = 0; |
| 409 | while( (c = fossil_utf8_read(&z))!=0 ){ |
| 410 | if( c=='\\' ){ |
| 411 | zOut[i++] = '\\'; |
| 412 | zOut[i++] = c; |
| 413 | }else if( c<' ' || c>=0x7f ){ |
| @@ -425,10 +427,11 @@ | |
| 427 | }else{ |
| 428 | zOut[i++] = c; |
| 429 | } |
| 430 | } |
| 431 | zOut[i] = 0; |
| 432 | return zOut; |
| 433 | } |
| 434 | |
| 435 | /* |
| 436 | ** The characters used for HTTP base64 encoding. |
| 437 | */ |
| 438 |
+11
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -99,10 +99,11 @@ | ||
| 99 | 99 | #define etFOSSILIZE 20 /* The fossil header encoding format. */ |
| 100 | 100 | #define etPATH 21 /* Path type */ |
| 101 | 101 | #define etWIKISTR 22 /* Timeline comment text rendered from a char*: %W */ |
| 102 | 102 | #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */ |
| 103 | 103 | #define etROOT 24 /* String value of g.zTop: %R */ |
| 104 | +#define etJSONSTR 25 /* String encoded as a JSON string literal: %j */ | |
| 104 | 105 | |
| 105 | 106 | |
| 106 | 107 | /* |
| 107 | 108 | ** An "etByte" is an 8-bit unsigned value. |
| 108 | 109 | */ |
| @@ -150,10 +151,11 @@ | ||
| 150 | 151 | { 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */ |
| 151 | 152 | { 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */ |
| 152 | 153 | { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, |
| 153 | 154 | { 'F', 0, 4, etFOSSILIZE, 0, 0 }, |
| 154 | 155 | { 'S', 0, 4, etSTRINGID, 0, 0 }, |
| 156 | + { 'j', 0, 0, etJSONSTR, 0, 0 }, | |
| 155 | 157 | { 'c', 0, 0, etCHARX, 0, 0 }, |
| 156 | 158 | { 'o', 8, 0, etRADIX, 0, 2 }, |
| 157 | 159 | { 'u', 10, 0, etRADIX, 0, 0 }, |
| 158 | 160 | { 'x', 16, 0, etRADIX, 16, 1 }, |
| 159 | 161 | { 'X', 16, 0, etRADIX, 0, 4 }, |
| @@ -779,10 +781,19 @@ | ||
| 779 | 781 | case etFOSSILIZE: { |
| 780 | 782 | int limit = flag_alternateform ? va_arg(ap,int) : -1; |
| 781 | 783 | char *zMem = va_arg(ap,char*); |
| 782 | 784 | if( zMem==0 ) zMem = ""; |
| 783 | 785 | zExtra = bufpt = fossilize(zMem, limit); |
| 786 | + length = strlen(bufpt); | |
| 787 | + if( precision>=0 && precision<length ) length = precision; | |
| 788 | + break; | |
| 789 | + } | |
| 790 | + case etJSONSTR: { | |
| 791 | + int limit = flag_alternateform ? va_arg(ap,int) : -1; | |
| 792 | + char *zMem = va_arg(ap,char*); | |
| 793 | + if( zMem==0 ) zMem = ""; | |
| 794 | + zExtra = bufpt = encode_json_string_literal(zMem); | |
| 784 | 795 | length = strlen(bufpt); |
| 785 | 796 | if( precision>=0 && precision<length ) length = precision; |
| 786 | 797 | break; |
| 787 | 798 | } |
| 788 | 799 | case etWIKISTR: { |
| 789 | 800 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -99,10 +99,11 @@ | |
| 99 | #define etFOSSILIZE 20 /* The fossil header encoding format. */ |
| 100 | #define etPATH 21 /* Path type */ |
| 101 | #define etWIKISTR 22 /* Timeline comment text rendered from a char*: %W */ |
| 102 | #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */ |
| 103 | #define etROOT 24 /* String value of g.zTop: %R */ |
| 104 | |
| 105 | |
| 106 | /* |
| 107 | ** An "etByte" is an 8-bit unsigned value. |
| 108 | */ |
| @@ -150,10 +151,11 @@ | |
| 150 | { 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */ |
| 151 | { 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */ |
| 152 | { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, |
| 153 | { 'F', 0, 4, etFOSSILIZE, 0, 0 }, |
| 154 | { 'S', 0, 4, etSTRINGID, 0, 0 }, |
| 155 | { 'c', 0, 0, etCHARX, 0, 0 }, |
| 156 | { 'o', 8, 0, etRADIX, 0, 2 }, |
| 157 | { 'u', 10, 0, etRADIX, 0, 0 }, |
| 158 | { 'x', 16, 0, etRADIX, 16, 1 }, |
| 159 | { 'X', 16, 0, etRADIX, 0, 4 }, |
| @@ -779,10 +781,19 @@ | |
| 779 | case etFOSSILIZE: { |
| 780 | int limit = flag_alternateform ? va_arg(ap,int) : -1; |
| 781 | char *zMem = va_arg(ap,char*); |
| 782 | if( zMem==0 ) zMem = ""; |
| 783 | zExtra = bufpt = fossilize(zMem, limit); |
| 784 | length = strlen(bufpt); |
| 785 | if( precision>=0 && precision<length ) length = precision; |
| 786 | break; |
| 787 | } |
| 788 | case etWIKISTR: { |
| 789 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -99,10 +99,11 @@ | |
| 99 | #define etFOSSILIZE 20 /* The fossil header encoding format. */ |
| 100 | #define etPATH 21 /* Path type */ |
| 101 | #define etWIKISTR 22 /* Timeline comment text rendered from a char*: %W */ |
| 102 | #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */ |
| 103 | #define etROOT 24 /* String value of g.zTop: %R */ |
| 104 | #define etJSONSTR 25 /* String encoded as a JSON string literal: %j */ |
| 105 | |
| 106 | |
| 107 | /* |
| 108 | ** An "etByte" is an 8-bit unsigned value. |
| 109 | */ |
| @@ -150,10 +151,11 @@ | |
| 151 | { 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */ |
| 152 | { 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */ |
| 153 | { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, |
| 154 | { 'F', 0, 4, etFOSSILIZE, 0, 0 }, |
| 155 | { 'S', 0, 4, etSTRINGID, 0, 0 }, |
| 156 | { 'j', 0, 0, etJSONSTR, 0, 0 }, |
| 157 | { 'c', 0, 0, etCHARX, 0, 0 }, |
| 158 | { 'o', 8, 0, etRADIX, 0, 2 }, |
| 159 | { 'u', 10, 0, etRADIX, 0, 0 }, |
| 160 | { 'x', 16, 0, etRADIX, 16, 1 }, |
| 161 | { 'X', 16, 0, etRADIX, 0, 4 }, |
| @@ -779,10 +781,19 @@ | |
| 781 | case etFOSSILIZE: { |
| 782 | int limit = flag_alternateform ? va_arg(ap,int) : -1; |
| 783 | char *zMem = va_arg(ap,char*); |
| 784 | if( zMem==0 ) zMem = ""; |
| 785 | zExtra = bufpt = fossilize(zMem, limit); |
| 786 | length = strlen(bufpt); |
| 787 | if( precision>=0 && precision<length ) length = precision; |
| 788 | break; |
| 789 | } |
| 790 | case etJSONSTR: { |
| 791 | int limit = flag_alternateform ? va_arg(ap,int) : -1; |
| 792 | char *zMem = va_arg(ap,char*); |
| 793 | if( zMem==0 ) zMem = ""; |
| 794 | zExtra = bufpt = encode_json_string_literal(zMem); |
| 795 | length = strlen(bufpt); |
| 796 | if( precision>=0 && precision<length ) length = precision; |
| 797 | break; |
| 798 | } |
| 799 | case etWIKISTR: { |
| 800 |
+5
-7
| --- src/unversioned.c | ||
| +++ src/unversioned.c | ||
| @@ -619,18 +619,16 @@ | ||
| 619 | 619 | sqlite3_int64 mtime = db_column_int(&q, 1); |
| 620 | 620 | const char *zHash = db_column_text(&q, 2); |
| 621 | 621 | int fullSize = db_column_int(&q, 3); |
| 622 | 622 | const char *zLogin = db_column_text(&q, 4); |
| 623 | 623 | if( zLogin==0 ) zLogin = ""; |
| 624 | - blob_appendf(&json, "%s{\"name\":\"", zSep); | |
| 624 | + blob_appendf(&json, "%s{\"name\":\"%j\",\n", zSep, zName); | |
| 625 | 625 | zSep = ",\n "; |
| 626 | - blob_append_json_string(&json, zName); | |
| 627 | - blob_appendf(&json, "\",\n \"mtime\":%lld,\n \"hash\":\"", mtime); | |
| 628 | - blob_append_json_string(&json, zHash); | |
| 629 | - blob_appendf(&json, "\",\n \"size\":%d,\n \"user\":\"", fullSize); | |
| 630 | - blob_append_json_string(&json, zLogin); | |
| 631 | - blob_appendf(&json, "\"}"); | |
| 626 | + blob_appendf(&json, " \"mtime\":%lld,\n", mtime); | |
| 627 | + blob_appendf(&json, " \"hash\":\"%j\",\n", zHash); | |
| 628 | + blob_appendf(&json, " \"size\":%d,\n", fullSize); | |
| 629 | + blob_appendf(&json, " \"user\":\"%j\"}", zLogin); | |
| 632 | 630 | } |
| 633 | 631 | db_finalize(&q); |
| 634 | 632 | blob_appendf(&json,"]\n"); |
| 635 | 633 | cgi_set_content(&json); |
| 636 | 634 | } |
| 637 | 635 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -619,18 +619,16 @@ | |
| 619 | sqlite3_int64 mtime = db_column_int(&q, 1); |
| 620 | const char *zHash = db_column_text(&q, 2); |
| 621 | int fullSize = db_column_int(&q, 3); |
| 622 | const char *zLogin = db_column_text(&q, 4); |
| 623 | if( zLogin==0 ) zLogin = ""; |
| 624 | blob_appendf(&json, "%s{\"name\":\"", zSep); |
| 625 | zSep = ",\n "; |
| 626 | blob_append_json_string(&json, zName); |
| 627 | blob_appendf(&json, "\",\n \"mtime\":%lld,\n \"hash\":\"", mtime); |
| 628 | blob_append_json_string(&json, zHash); |
| 629 | blob_appendf(&json, "\",\n \"size\":%d,\n \"user\":\"", fullSize); |
| 630 | blob_append_json_string(&json, zLogin); |
| 631 | blob_appendf(&json, "\"}"); |
| 632 | } |
| 633 | db_finalize(&q); |
| 634 | blob_appendf(&json,"]\n"); |
| 635 | cgi_set_content(&json); |
| 636 | } |
| 637 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -619,18 +619,16 @@ | |
| 619 | sqlite3_int64 mtime = db_column_int(&q, 1); |
| 620 | const char *zHash = db_column_text(&q, 2); |
| 621 | int fullSize = db_column_int(&q, 3); |
| 622 | const char *zLogin = db_column_text(&q, 4); |
| 623 | if( zLogin==0 ) zLogin = ""; |
| 624 | blob_appendf(&json, "%s{\"name\":\"%j\",\n", zSep, zName); |
| 625 | zSep = ",\n "; |
| 626 | blob_appendf(&json, " \"mtime\":%lld,\n", mtime); |
| 627 | blob_appendf(&json, " \"hash\":\"%j\",\n", zHash); |
| 628 | blob_appendf(&json, " \"size\":%d,\n", fullSize); |
| 629 | blob_appendf(&json, " \"user\":\"%j\"}", zLogin); |
| 630 | } |
| 631 | db_finalize(&q); |
| 632 | blob_appendf(&json,"]\n"); |
| 633 | cgi_set_content(&json); |
| 634 | } |
| 635 |