Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | More work on the warnings infrastructure. json_warn() now (experimentally) disallows (elides) duplicate warning codes to simplify downstream loops. Still undecided on that behaviour, though. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | json |
| Files: | files | file ages | folders |
| SHA1: |
576425e862f08acf94e6e006832d8954 |
| User & Date: | stephan 2011-09-21 17:38:36.148 |
Context
|
2011-09-21
| ||
| 18:10 | Minor timeline output cleanups. Added /json/branch/list. ... (check-in: f266ebdd3f user: stephan tags: json) | |
| 17:38 | More work on the warnings infrastructure. json_warn() now (experimentally) disallows (elides) duplicate warning codes to simplify downstream loops. Still undecided on that behaviour, though. ... (check-in: 576425e862 user: stephan tags: json) | |
| 16:31 | started adding infrastructure to report non-fatal warnings. ... (check-in: ad50fe9584 user: stephan tags: json) | |
Changes
Changes to src/json.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
** stdout, as that will effectively corrupt any JSON output, and
** almost certainly will corrupt any HTTP response headers. Output
** sent to stderr ends up in my apache log, so that might be useful
** for debuggering in some cases, but so such code should be left
** enabled for non-debuggering builds.
*/
typedef cson_value * (*fossil_json_f)(unsigned int depth);
/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.
*/
static cson_value * json_page_nyi(unsigned int depth){
g.json.resultCode = FSL_JSON_E_NYI;
| > > > > > > > > > > > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
** stdout, as that will effectively corrupt any JSON output, and
** almost certainly will corrupt any HTTP response headers. Output
** sent to stderr ends up in my apache log, so that might be useful
** for debuggering in some cases, but so such code should be left
** enabled for non-debuggering builds.
*/
typedef cson_value * (*fossil_json_f)(unsigned int depth);
/*
** Internal helpers to manipulate a byte array as a bitset. The B
** argument must be-a array at least (BIT/8+1) bytes long.
** The BIT argument is the bit number to query/set/clear/toggle.
*/
#define BITSET_BYTEFOR(B,BIT) ((B)[ BIT / 8 ])
#define BITSET_SET(B,BIT) ((BITSET_BYTEFOR(B,BIT) |= (0x01 << (BIT%8))),0x01)
#define BITSET_UNSET(B,BIT) ((BITSET_BYTEFOR(B,BIT) &= ~(0x01 << (BIT%8))),0x00)
#define BITSET_GET(B,BIT) ((BITSET_BYTEFOR(B,BIT) & (0x01 << (BIT%8))) ? 0x01 : 0x00)
#define BITSET_TOGGLE(B,BIT) (BITSET_GET(B,BIT) ? (BITSET_UNSET(B,BIT)) : (BITSET_SET(B,BIT)))
/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.
*/
static cson_value * json_page_nyi(unsigned int depth){
g.json.resultCode = FSL_JSON_E_NYI;
|
| ︙ | ︙ | |||
461 462 463 464 465 466 467 |
v = cson_value_new_object();
g.json.param.v = v;
g.json.param.o = cson_value_get_object(v);
json_gc_add("$PARAMS", v, 1);
}
/*
| | | > > > > > | | > > > > > > > > > | > > > > > > > | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
v = cson_value_new_object();
g.json.param.v = v;
g.json.param.o = cson_value_get_object(v);
json_gc_add("$PARAMS", v, 1);
}
/*
** Appends a warning object to the (pending) JSON response.
**
** Code must be a FSL_JSON_W_xxx value from the FossilJsonCodes enum.
**
** A Warning object has this JSON structure:
**
** { "code":integer, "text":"string" }
**
** But the text part is optional.
**
** FIXME FIXME FIXME: i am EXPERIMENTALLY using integer codes instead
** of FOSSIL-XXXX codes here. i may end up switching FOSSIL-XXXX
** string-form codes to integers. Let's ask the mailing list for
** opinions...
**
** If msg is non-NULL and not empty then it is used as the "text"
** property's value. It is copied, and need not refer to static
** memory.
**
** CURRENTLY this code only allows a given warning code to be
** added one time, and elides subsequent warnings. The intention
** is to remove that burden from loops which produce warnings.
**
** FIXME: if msg is NULL then use a standard string for
** the given code. If !*msg then elide the "text" property,
** for consistency with how json_err() works.
*/
void json_warn( int code, char const * msg ){
cson_value * objV = NULL;
cson_object * obj = NULL;
assert( (code>FSL_JSON_W_START)
&& (code<FSL_JSON_W_END)
&& "Invalid warning code.");
if( BITSET_GET(g.json.warnings.bitset,code) ){
return;
}
if(!g.json.warnings.v){
g.json.warnings.v = cson_value_new_array();
assert((NULL != g.json.warnings.v) && "Alloc error.");
g.json.warnings.a = cson_value_get_array(g.json.warnings.v);
json_gc_add("$WARNINGS",g.json.warnings.v,0);
}
objV = cson_value_new_object();
assert((NULL != objV) && "Alloc error.");
cson_array_append(g.json.warnings.a, objV);
obj = cson_value_get_object(objV);
cson_object_set(obj,"code",cson_value_new_integer(code));
if(msg && *msg){
/* FIXME: treat NULL msg as standard warning message for
the code, but we don't have those yet.
*/
cson_object_set(obj,"text",cson_value_new_string(msg,strlen(msg)));
}
BITSET_SET(g.json.warnings.bitset,code);
}
/*
** Splits zStr (which must not be NULL) into tokens separated by the
** given separator character. If doDeHttp is true then each element
** will be passed through dehttpize(), otherwise they are used
** as-is. Each new element is appended to the given target array
|
| ︙ | ︙ | |||
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 |
}else{
cson_value_free(part);
rc = rc ? -rc : 0;
break;
}
}else{
assert(0 && "i didn't think this was possible!");
}
free(zPart);
len = 0;
}
if( !*p ){
break;
}
head = p+1;
continue;
}
++len;
}
return rc;
}
/*
** Performs some common initialization of JSON-related state. Must be
** called by the json_page_top() and json_cmd_top() dispatching
** functions to set up the JSON stat used by the dispatched functions.
**
** Implicitly sets up the login information state in CGI mode, but
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
}else{
cson_value_free(part);
rc = rc ? -rc : 0;
break;
}
}else{
assert(0 && "i didn't think this was possible!");
fprintf(stderr,"%s:%d: My God! It's full of stars!\n",
__FILE__, __LINE__);
fossil_exit(1)
/* Not fossil_panic() b/c this code needs to be able to
run before some of the fossil/json bits are initialized,
and fossil_panic() calls into the JSON API.
*/
;
}
free(zPart);
len = 0;
}
if( !*p ){
break;
}
head = p+1;
continue;
}
++len;
}
return rc;
}
/*
** Wrapper around json_string_split(), taking the same first 3
** parameters as this function, but returns the results as
** a JSON Array (if splitting produced tokens)
** OR a JSON null value (if splitting produced no tokens)
** OR NULL (if splitting failed in any way).
**
** The returned value is owned by the caller. If not NULL then it
** _will_ have a JSON type of Array or Null.
*/
cson_value * json_string_split2( char const * zStr,
char separator,
char doDeHttp ){
cson_value * v = cson_value_new_array();
cson_array * a = cson_value_get_array(v);
int rc = json_string_split( zStr, separator, doDeHttp, a );
if( 0 == rc ){
cson_value_free(v);
v = cson_value_null();
}else if(rc<0){
cson_value_free(v);
v = NULL;
}
return v;
}
/*
** Performs some common initialization of JSON-related state. Must be
** called by the json_page_top() and json_cmd_top() dispatching
** functions to set up the JSON stat used by the dispatched functions.
**
** Implicitly sets up the login information state in CGI mode, but
|
| ︙ | ︙ | |||
797 798 799 800 801 802 803 | } /* ** Given a Fossil/JSON result code, this function "dumbs it down" ** according to the current value of g.json.errorDetailParanoia. The ** dumbed-down value is returned. ** | | | > > > | | | 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 |
}
/*
** Given a Fossil/JSON result code, this function "dumbs it down"
** according to the current value of g.json.errorDetailParanoia. The
** dumbed-down value is returned.
**
** This function assert()s that code is in the inclusive range 0 to
** 9999.
**
** Note that WARNING codes (1..999) are never dumbed down.
**
*/
static int json_dumbdown_rc( int code ){
if(!code || ((code>FSL_JSON_W_START) && (code>FSL_JSON_W_END))){
return code;
}else{
int modulo = 0;
assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
switch( g.json.errorDetailParanoia ){
case 1: modulo = 10; break;
case 2: modulo = 100; break;
case 3: modulo = 1000; break;
|
| ︙ | ︙ | |||
1593 1594 1595 1596 1597 1598 1599 |
a JSON array*/
" tagId AS tagId,"
" brief AS briefText"
" FROM json_timeline"
" ORDER BY mtime DESC",
-1);
db_prepare(&q,blob_buffer(&sql));
| < | > < | > | < | > > | > > | > > > > > > > > > > > | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 |
a JSON array*/
" tagId AS tagId,"
" brief AS briefText"
" FROM json_timeline"
" ORDER BY mtime DESC",
-1);
db_prepare(&q,blob_buffer(&sql));
listV = cson_value_new_array();
list = cson_value_get_array(listV);
tmp = listV;
SET("timeline");
while( (SQLITE_ROW == db_step(&q) )){
/* convert each row into a JSON object...*/
cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt);
cson_object * row = cson_value_get_object(rowV);
cson_string const * tagsStr = NULL;
if(!row){
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
"Could not convert at least one timeline result row to JSON." );
continue;
}
/* Split tags string field into JSON Array... */
cson_array_append(list, rowV);
tagsStr = cson_value_get_string(cson_object_get(row,"tags"));
if(tagsStr){
cson_value * tags = json_string_split2( cson_string_cstr(tagsStr),
',', 0);
if( tags ){
if(0 != cson_object_set(row,"tags",tags)){
cson_value_free(tags);
}else{
/*replaced/deleted old tags value, invalidating tagsStr*/;
tagsStr = NULL;
}
}else{
json_warn(FSL_JSON_W_STRING_TO_ARRAY_FAILED,
"Could not convert tags string to array.");
}
}
/* replace isLeaf int w/ JSON bool */
tmp = cson_object_get(row,"isLeaf");
if(tmp && cson_value_is_integer(tmp)){
cson_object_set(row,"isLeaf",
cson_value_get_integer(tmp)
? cson_value_true()
: cson_value_false());
tmp = NULL;
}
}
db_finalize(&q);
#undef SET
goto ok;
error:
cson_value_free(payV);
payV = NULL;
|
| ︙ | ︙ | |||
1785 1786 1787 1788 1789 1790 1791 |
json_main_bootstrap();
json_mode_bootstrap();
if( g.argc<3 ){
goto usage;
}
db_find_and_open_repository(0, 0);
#if 0
| | > | 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 |
json_main_bootstrap();
json_mode_bootstrap();
if( g.argc<3 ){
goto usage;
}
db_find_and_open_repository(0, 0);
#if 0
json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing.");
json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again.");
#endif
cmd = json_command_arg(1);
if( !cmd || !*cmd ){
goto usage;
}
pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
if( ! pageDef ){
|
| ︙ | ︙ | |||
1819 1820 1821 1822 1823 1824 1825 |
fossil_exit(1);
}
}
return;
usage:
usage("subcommand");
}
| > > > > > > | 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 |
fossil_exit(1);
}
}
return;
usage:
usage("subcommand");
}
#undef BITSET_BYTEFOR
#undef BITSET_SET
#undef BITSET_UNSET
#undef BITSET_GET
#undef BITSET_TOGGLE
|
Changes to src/json_detail.h.
1 2 3 4 5 6 | /* ** Impl details for the JSON API which need to be shared ** across multiple C files. */ /* | | | < | > | > | | | > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/*
** Impl details for the JSON API which need to be shared
** across multiple C files.
*/
/*
** The "official" list of Fossil/JSON error codes. Their values might
** very well change during initial development but after their first
** public release they must stay stable.
**
** Values must be in the range 1000..9999 for error codes and 1..999
** for warning codes.
**
** Numbers evenly dividable by 100 are "categories", and error codes
** for a given category have their high bits set to the category
** value.
**
*/
enum FossilJsonCodes {
FSL_JSON_W_START = 0,
FSL_JSON_W_ROW_TO_JSON_FAILED = FSL_JSON_W_START + 1,
FSL_JSON_W_STRING_TO_ARRAY_FAILED = FSL_JSON_W_START + 2,
FSL_JSON_W_END = 1000,
FSL_JSON_E_GENERIC = 1000,
FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100,
FSL_JSON_E_INVALID_REQUEST = FSL_JSON_E_GENERIC_SUB1 + 1,
FSL_JSON_E_UNKNOWN_COMMAND = FSL_JSON_E_GENERIC_SUB1 + 2,
FSL_JSON_E_UNKNOWN = FSL_JSON_E_GENERIC_SUB1 + 3,
FSL_JSON_E_RESOURCE_NOT_FOUND = FSL_JSON_E_GENERIC_SUB1 + 4,
FSL_JSON_E_TIMEOUT = FSL_JSON_E_GENERIC_SUB1 + 5,
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 54 55 | FSL_JSON_E_DB = 4000, FSL_JSON_E_STMT_PREP = FSL_JSON_E_DB + 1, FSL_JSON_E_STMT_BIND = FSL_JSON_E_DB + 2, FSL_JSON_E_STMT_EXEC = FSL_JSON_E_DB + 3, FSL_JSON_E_DB_LOCKED = FSL_JSON_E_DB + 4, FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101 }; | > | 52 53 54 55 56 57 58 59 60 61 | FSL_JSON_E_DB = 4000, FSL_JSON_E_STMT_PREP = FSL_JSON_E_DB + 1, FSL_JSON_E_STMT_BIND = FSL_JSON_E_DB + 2, FSL_JSON_E_STMT_EXEC = FSL_JSON_E_DB + 3, FSL_JSON_E_DB_LOCKED = FSL_JSON_E_DB + 4, FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101 }; |
Changes to src/main.c.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #if INTERFACE #include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */ /* ** Number of elements in an array */ #define count(X) (sizeof(X)/sizeof(X[0])) /* | > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #if INTERFACE #include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */ #include "json_detail.h" /* ** Number of elements in an array */ #define count(X) (sizeof(X)/sizeof(X[0])) /* |
| ︙ | ︙ | |||
208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
struct {
cson_value * v;
cson_object * o;
} reqPayload; /* request payload object (if any) */
struct { /* response warnings */
cson_value * v;
cson_array * a;
} warnings;
} json;
};
/*
** Macro for debugging:
*/
| > > > > > > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
struct {
cson_value * v;
cson_object * o;
} reqPayload; /* request payload object (if any) */
struct { /* response warnings */
cson_value * v;
cson_array * a;
int bitset[FSL_JSON_W_END/8/sizeof(int)+1]
/* allows json_add_warning() to know if a given warning
has been set or not (we don't produce dupes, to simplify
downstream loop logic).
*/
;
} warnings;
} json;
};
/*
** Macro for debugging:
*/
|
| ︙ | ︙ | |||
474 475 476 477 478 479 480 481 482 483 484 485 |
** fossil_fatal() which never return, this routine might return if
** the fatal error handing is already in process. The caller must
** be prepared for this routine to return.
*/
void fossil_fatal_recursive(const char *zFormat, ...){
char *z;
va_list ap;
if( mainInFatalError ) return;
mainInFatalError = 1;
va_start(ap, zFormat);
z = vmprintf(zFormat, ap);
va_end(ap);
| > > > > > > | > | | 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
** fossil_fatal() which never return, this routine might return if
** the fatal error handing is already in process. The caller must
** be prepared for this routine to return.
*/
void fossil_fatal_recursive(const char *zFormat, ...){
char *z;
va_list ap;
int rc = 1;
if( mainInFatalError ) return;
mainInFatalError = 1;
va_start(ap, zFormat);
z = vmprintf(zFormat, ap);
va_end(ap);
if( g.json.isJsonMode ){
json_err( g.json.resultCode, z, 1 );
if( g.isHTTP ){
rc = 0 /* avoid HTTP 500 */;
}
}else if( g.cgiOutput ){
g.cgiOutput = 0;
cgi_printf("<p class=\"generalError\">%h</p>", z);
cgi_reply();
}else{
char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
fossil_puts(zOut, 1);
free(zOut);
}
db_force_rollback();
fossil_exit(rc);
}
/* Print a warning message */
void fossil_warning(const char *zFormat, ...){
char *z;
va_list ap;
|
| ︙ | ︙ |