Fossil SCM

Consolidated some duplicated /json code, removed some dead code. Minor doc additions and cleanups.

stephan 2012-03-05 20:35 trunk
Commit 6ca400a31520b36a4bcf93f4de27bb800152fcfc
2 files changed +89 -85 +4
+89 -85
--- src/json.c
+++ src/json.c
@@ -490,11 +490,11 @@
490490
491491
/*
492492
** Wrapper around json_getenv() which tries to evaluate a payload/env
493493
** value as a boolean. Uses mostly the same logic as
494494
** json_getenv_int(), with the addition that string values which
495
-** either start with a digit 1..9 or the letters [tT] are considered
495
+** either start with a digit 1..9 or the letters [tTyY] are considered
496496
** to be true. If this function cannot find a matching key/value then
497497
** dflt is returned. e.g. if it finds the key but the value is-a
498498
** Object then dftl is returned.
499499
**
500500
** If an entry is found, this function guarantees that it will return
@@ -512,12 +512,14 @@
512512
}else if( cson_value_is_string(v) ){
513513
char const * sv = cson_string_cstr(cson_value_get_string(v));
514514
if(!*sv || ('0'==*sv)){
515515
return 0;
516516
}else{
517
- return (('t'==*sv) || ('T'==*sv)
518
- || (('1'<=*sv) && ('9'>=*sv)))
517
+ return ((('1'<=*sv) && ('9'>=*sv))
518
+ || ('t'==*sv) || ('T'==*sv)
519
+ || ('y'==*sv) || ('Y'==*sv)
520
+ )
519521
? 1 : 0;
520522
}
521523
}else if( cson_value_is_bool(v) ){
522524
return cson_value_get_bool(v) ? 1 : 0;
523525
}else if( cson_value_is_null(v) ){
@@ -541,11 +543,11 @@
541543
/*
542544
** An extended form of find_option() which tries to look up a combo
543545
** GET/POST/CLI argument.
544546
**
545547
** zKey must be the GET/POST parameter key. zCLILong must be the "long
546
-** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or
548
+s** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or
547549
** the "short form" CLI flag (if NULL, no short form is used).
548550
**
549551
** If argPos is >=0 and no other match is found,
550552
** json_command_arg(argPos) is also checked.
551553
**
@@ -572,11 +574,11 @@
572574
}
573575
return rc;
574576
}
575577
576578
/*
577
-** Short-hand form of json_find_option_cstr(zKey,zCLILong,zCLIShort,-1).
579
+** Short-hand form of json_find_option_cstr2(zKey,zCLILong,zCLIShort,-1).
578580
*/
579581
char const * json_find_option_cstr(char const * zKey,
580582
char const * zCLILong,
581583
char const * zCLIShort){
582584
return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1);
@@ -602,18 +604,18 @@
602604
}
603605
return (-1==rc) ? dflt : rc;
604606
}
605607
606608
/*
607
-** The integer equivalent of json_find_option_cstr().
609
+** The integer equivalent of json_find_option_cstr2().
608610
** If the option is not found, dftl is returned.
609611
*/
610612
int json_find_option_int(char const * zKey,
611613
char const * zCLILong,
612614
char const * zCLIShort,
613615
int dflt ){
614
- enum { Magic = -947 };
616
+ enum { Magic = -1947854832 };
615617
int rc = Magic;
616618
if(!g.isHTTP){
617619
/* FIXME: use strtol() for better error/dflt handling. */
618620
char const * opt = find_option(zCLILong ? zCLILong : zKey,
619621
zCLIShort, 1);
@@ -979,11 +981,11 @@
979981
** parameters as this function, but returns the results as a JSON
980982
** Array (if splitting produced tokens) or NULL (if splitting failed
981983
** in any way or produced no tokens).
982984
**
983985
** The returned value is owned by the caller. If not NULL then it
984
-** _will_ have a JSON type of Array or Null.
986
+** _will_ have a JSON type of Array.
985987
*/
986988
cson_value * json_string_split2( char const * zStr,
987989
char separator,
988990
char doDeHttp ){
989991
cson_value * v = cson_value_new_array();
@@ -1166,10 +1168,17 @@
11661168
core, which we need before we call
11671169
login_check_credentials(). */;
11681170
login_check_credentials()/* populates g.perm */;
11691171
}
11701172
else{
1173
+ /* FIXME: we need an option which allows us to skip this. At least
1174
+ one known command (/json/version) does not need an opened
1175
+ repo. The problem here is we cannot know which functions need
1176
+ it from here (because command dispatching hasn't yet happened)
1177
+ and all other commands rely on the repo being opened before
1178
+ they are called. A textbook example of lack of foresight :/.
1179
+ */
11711180
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
11721181
}
11731182
}
11741183
11751184
/*
@@ -1475,13 +1484,13 @@
14751484
** json_err_cstr() is used to get the error string. The caller may
14761485
** provide his own or may use an empty string to suppress the
14771486
** resultText property.
14781487
**
14791488
*/
1480
-cson_value * json_create_response( int resultCode,
1481
- char const * pMsg,
1482
- cson_value * payload){
1489
+static cson_value * json_create_response( int resultCode,
1490
+ char const * pMsg,
1491
+ cson_value * payload){
14831492
cson_value * v = NULL;
14841493
cson_value * tmp = NULL;
14851494
cson_object * o = NULL;
14861495
int rc;
14871496
resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode);
@@ -1751,10 +1760,13 @@
17511760
** function. If resetBlob is true then blob_reset(pSql) is called
17521761
** after preparing the query.
17531762
**
17541763
** pTgt has the same semantics as described for
17551764
** json_stmt_to_array_of_obj().
1765
+**
1766
+** FIXME: change this to take a (char const *) instead of a blob,
1767
+** to simplify the trivial use-cases (which don't need a Blob).
17561768
*/
17571769
cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt,
17581770
char resetBlob){
17591771
Stmt q = empty_Stmt;
17601772
cson_value * pay = NULL;
@@ -1774,10 +1786,13 @@
17741786
** function returns a JSON Array containing the tag names (owned by
17751787
** the caller), else it returns NULL.
17761788
**
17771789
** See info_tags_of_checkin() for more details (this is simply a JSON
17781790
** wrapper for that function).
1791
+**
1792
+** If there are no tags then this function returns NULL, not an empty
1793
+** Array.
17791794
*/
17801795
cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){
17811796
cson_value * v = NULL;
17821797
char * tags = info_tags_of_checkin(rid, propagatingOnly);
17831798
if(tags){
@@ -1813,15 +1828,11 @@
18131828
cson_object * obj = NULL;
18141829
cson_string * kRC;
18151830
cson_string * kSymbol;
18161831
cson_string * kNumber;
18171832
cson_string * kDesc;
1818
- int rc = cson_array_reserve( list, 35 );
1819
- if(rc){
1820
- assert( 0 && "Allocation error.");
1821
- exit(1);
1822
- }
1833
+ cson_array_reserve( list, 35 );
18231834
kRC = cson_new_string("resultCode",10);
18241835
kSymbol = cson_new_string("cSymbol",7);
18251836
kNumber = cson_new_string("number",6);
18261837
kDesc = cson_new_string("description",11);
18271838
#define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); \
@@ -2113,22 +2124,43 @@
21132124
}
21142125
}
21152126
return i;
21162127
}
21172128
2129
+/*
2130
+** Creates an error message using zErrPrefix and the given array of
2131
+** JSON command definitions, and sets the g.json error state to
2132
+** reflect FSL_JSON_E_MISSING_ARGS. If zErrPrefix is NULL then
2133
+** some default is used (e.g. "Try one of: "). If it is "" then
2134
+** no prefix is used.
2135
+**
2136
+** The intention is to provide the user (via the response.resultText)
2137
+** a list of available commands/subcommands.
2138
+**
2139
+*/
2140
+void json_dispatch_missing_args_err( JsonPageDef const * pCommands,
2141
+ char const * zErrPrefix ){
2142
+ Blob cmdNames = empty_blob;
2143
+ blob_init(&cmdNames,NULL,0);
2144
+ if( !zErrPrefix ) {
2145
+ zErrPrefix = "Try one of: ";
2146
+ }
2147
+ blob_append( &cmdNames, zErrPrefix, strlen(zErrPrefix) );
2148
+ json_pagedefs_to_string(pCommands, &cmdNames);
2149
+ json_set_err(FSL_JSON_E_MISSING_ARGS, "%s",
2150
+ blob_str(&cmdNames));
2151
+ blob_reset(&cmdNames);
2152
+}
21182153
21192154
cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
21202155
JsonPageDef const * def;
21212156
char const * cmd = json_command_arg(1+g.json.dispatchDepth);
21222157
assert( NULL != pages );
21232158
if( ! cmd ){
2124
- Blob cmdNames = empty_blob;
2125
- json_pagedefs_to_string(pages, &cmdNames);
2126
- json_set_err(FSL_JSON_E_MISSING_ARGS,
2127
- "No subcommand specified. Try one of (%s).",
2128
- blob_str(&cmdNames));
2129
- blob_reset(&cmdNames);
2159
+ json_dispatch_missing_args_err(pages,
2160
+ "No subcommand specified. "
2161
+ "Try one of: ");
21302162
return NULL;
21312163
}
21322164
def = json_handler_for_name( cmd, pages );
21332165
if(!def){
21342166
json_set_err(FSL_JSON_E_UNKNOWN_COMMAND,
@@ -2215,11 +2247,11 @@
22152247
{"artifact", json_page_artifact, 0},
22162248
{"branch", json_page_branch,0},
22172249
{"cap", json_page_cap, 0},
22182250
{"config", json_page_config, 0 },
22192251
{"diff", json_page_diff, 0},
2220
-{"dir", json_page_nyi, 0},
2252
+/*{"dir", json_page_nyi, 0},*/
22212253
{"finfo", json_page_finfo, 0},
22222254
{"g", json_page_g, 0},
22232255
{"HAI",json_page_version,0},
22242256
{"login",json_page_login,0},
22252257
{"logout",json_page_logout,0},
@@ -2227,49 +2259,16 @@
22272259
{"rebuild",json_page_rebuild,0},
22282260
{"report", json_page_report, 0},
22292261
{"resultCodes", json_page_resultCodes,0},
22302262
{"stat",json_page_stat,0},
22312263
{"tag", json_page_tag,0},
2232
-{"ticket", json_page_nyi,0},
2264
+/*{"ticket", json_page_nyi,0},*/
22332265
{"timeline", json_page_timeline,0},
22342266
{"user",json_page_user,0},
22352267
{"version",json_page_version,0},
22362268
{"whoami",json_page_whoami,0},
22372269
{"wiki",json_page_wiki,0},
2238
-/* Last entry MUST have a NULL name. */
2239
-{NULL,NULL,0}
2240
-};
2241
-
2242
-
2243
-/*
2244
-** Mapping of /json/ticket/XXX commands/paths to callbacks.
2245
-*/
2246
-static const JsonPageDef JsonPageDefs_Ticket[] = {
2247
-{"get", json_page_nyi, 0},
2248
-{"list", json_page_nyi, 0},
2249
-{"save", json_page_nyi, 1},
2250
-{"create", json_page_nyi, 1},
2251
-/* Last entry MUST have a NULL name. */
2252
-{NULL,NULL,0}
2253
-};
2254
-
2255
-/*
2256
-** Mapping of /json/artifact/XXX commands/paths to callbacks.
2257
-*/
2258
-static const JsonPageDef JsonPageDefs_Artifact[] = {
2259
-{"vinfo", json_page_nyi, 0},
2260
-{"finfo", json_page_nyi, 0},
2261
-/* Last entry MUST have a NULL name. */
2262
-{NULL,NULL,0}
2263
-};
2264
-
2265
-/*
2266
-** Mapping of /json/tag/XXX commands/paths to callbacks.
2267
-*/
2268
-static const JsonPageDef JsonPageDefs_Tag[] = {
2269
-{"list", json_page_nyi, 0},
2270
-{"create", json_page_nyi, 1},
22712270
/* Last entry MUST have a NULL name. */
22722271
{NULL,NULL,0}
22732272
};
22742273
22752274
/*
@@ -2299,11 +2298,11 @@
22992298
else{
23002299
rc = 0;
23012300
g.json.dispatchDepth = 1;
23022301
payload = (*pageDef->func)();
23032302
}
2304
- payload = json_create_response(rc, rc ? g.zErrMsg : NULL, payload);
2303
+ payload = json_create_response(rc, NULL, payload);
23052304
json_send_response(payload);
23062305
cson_value_free(payload);
23072306
return rc;
23082307
}
23092308
@@ -2319,26 +2318,16 @@
23192318
char const * zCommand;
23202319
BEGIN_TIMER;
23212320
json_mode_bootstrap();
23222321
zCommand = json_command_arg(1);
23232322
if(!zCommand || !*zCommand){
2324
- goto usage;
2323
+ json_dispatch_missing_args_err( JsonPageDefs,
2324
+ "No command (sub-path) specified."
2325
+ " Try one of: ");
2326
+ return;
23252327
}
23262328
json_dispatch_root_command( zCommand );
2327
- return;
2328
- usage:
2329
- {
2330
- Blob cmdNames = empty_blob;
2331
- blob_init(&cmdNames,
2332
- "No command (sub-path) specified. Try one of: ",
2333
- -1);
2334
- json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2335
- json_err(FSL_JSON_E_MISSING_ARGS,
2336
- blob_str(&cmdNames), 0);
2337
- blob_reset(&cmdNames);
2338
- }
2339
-
23402329
}
23412330
#endif /* FOSSIL_ENABLE_JSON for mkindex */
23422331
23432332
#ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */
23442333
/*
@@ -2345,27 +2334,42 @@
23452334
** This function dispatches json commands and is the CLI equivalent of
23462335
** json_page_top().
23472336
**
23482337
** COMMAND: json
23492338
**
2350
-** Usage: %fossil json SUBCOMMAND
2339
+** Usage: %fossil json SUBCOMMAND ?OPTIONS?
2340
+**
2341
+** In CLI mode, the -R REPO common option is supported. Due to limitations
2342
+** in the argument dispatching code, any -FLAGS must come after the final
2343
+** sub- (or subsub-) command.
23512344
**
23522345
** The commands include:
23532346
**
2347
+** anonymousPassord
2348
+** artifact
23542349
** branch
23552350
** cap
2351
+** diff
2352
+** g
2353
+** login
2354
+** logout
2355
+** query
2356
+** rebuild
2357
+** report
2358
+** resultCodes
23562359
** stat
2360
+** tag
23572361
** timeline
2362
+** user
23582363
** version (alias: HAI)
2364
+** whoami
23592365
** wiki
23602366
**
2361
-**
2362
-** TODOs:
2367
+** Run '%fossil json' without any subcommand to see the full list (but be
2368
+** aware that some listed might not yet be implemented).
23632369
**
2364
-** tag
2365
-** ticket
2366
-** ...
2370
+** PS: the notable TODO-commands include: config, dir, finfo, ticket
23672371
**
23682372
*/
23692373
void json_cmd_top(void){
23702374
char const * cmd = NULL;
23712375
int rc = 0;
@@ -2401,18 +2405,18 @@
24012405
fossil_exit(1);
24022406
}
24032407
return;
24042408
usage:
24052409
{
2406
- Blob cmdNames = empty_blob;
2407
- blob_init(&cmdNames,
2408
- "No subcommand specified. Try one of: ", -1);
2409
- json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2410
- json_err(FSL_JSON_E_MISSING_ARGS,
2411
- blob_str(&cmdNames), 1);
2412
- blob_reset(&cmdNames);
2410
+ cson_value * payload;
2411
+ json_dispatch_missing_args_err( JsonPageDefs,
2412
+ "No subcommand specified."
2413
+ " Try one of: ");
2414
+ payload = json_create_response(0, NULL, NULL);
2415
+ json_send_response(payload);
2416
+ cson_value_free(payload);
24132417
fossil_exit(1);
24142418
}
24152419
}
24162420
#endif /* FOSSIL_ENABLE_JSON for mkindex */
24172421
24182422
#endif /* FOSSIL_ENABLE_JSON */
24192423
--- src/json.c
+++ src/json.c
@@ -490,11 +490,11 @@
490
491 /*
492 ** Wrapper around json_getenv() which tries to evaluate a payload/env
493 ** value as a boolean. Uses mostly the same logic as
494 ** json_getenv_int(), with the addition that string values which
495 ** either start with a digit 1..9 or the letters [tT] are considered
496 ** to be true. If this function cannot find a matching key/value then
497 ** dflt is returned. e.g. if it finds the key but the value is-a
498 ** Object then dftl is returned.
499 **
500 ** If an entry is found, this function guarantees that it will return
@@ -512,12 +512,14 @@
512 }else if( cson_value_is_string(v) ){
513 char const * sv = cson_string_cstr(cson_value_get_string(v));
514 if(!*sv || ('0'==*sv)){
515 return 0;
516 }else{
517 return (('t'==*sv) || ('T'==*sv)
518 || (('1'<=*sv) && ('9'>=*sv)))
 
 
519 ? 1 : 0;
520 }
521 }else if( cson_value_is_bool(v) ){
522 return cson_value_get_bool(v) ? 1 : 0;
523 }else if( cson_value_is_null(v) ){
@@ -541,11 +543,11 @@
541 /*
542 ** An extended form of find_option() which tries to look up a combo
543 ** GET/POST/CLI argument.
544 **
545 ** zKey must be the GET/POST parameter key. zCLILong must be the "long
546 ** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or
547 ** the "short form" CLI flag (if NULL, no short form is used).
548 **
549 ** If argPos is >=0 and no other match is found,
550 ** json_command_arg(argPos) is also checked.
551 **
@@ -572,11 +574,11 @@
572 }
573 return rc;
574 }
575
576 /*
577 ** Short-hand form of json_find_option_cstr(zKey,zCLILong,zCLIShort,-1).
578 */
579 char const * json_find_option_cstr(char const * zKey,
580 char const * zCLILong,
581 char const * zCLIShort){
582 return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1);
@@ -602,18 +604,18 @@
602 }
603 return (-1==rc) ? dflt : rc;
604 }
605
606 /*
607 ** The integer equivalent of json_find_option_cstr().
608 ** If the option is not found, dftl is returned.
609 */
610 int json_find_option_int(char const * zKey,
611 char const * zCLILong,
612 char const * zCLIShort,
613 int dflt ){
614 enum { Magic = -947 };
615 int rc = Magic;
616 if(!g.isHTTP){
617 /* FIXME: use strtol() for better error/dflt handling. */
618 char const * opt = find_option(zCLILong ? zCLILong : zKey,
619 zCLIShort, 1);
@@ -979,11 +981,11 @@
979 ** parameters as this function, but returns the results as a JSON
980 ** Array (if splitting produced tokens) or NULL (if splitting failed
981 ** in any way or produced no tokens).
982 **
983 ** The returned value is owned by the caller. If not NULL then it
984 ** _will_ have a JSON type of Array or Null.
985 */
986 cson_value * json_string_split2( char const * zStr,
987 char separator,
988 char doDeHttp ){
989 cson_value * v = cson_value_new_array();
@@ -1166,10 +1168,17 @@
1166 core, which we need before we call
1167 login_check_credentials(). */;
1168 login_check_credentials()/* populates g.perm */;
1169 }
1170 else{
 
 
 
 
 
 
 
1171 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
1172 }
1173 }
1174
1175 /*
@@ -1475,13 +1484,13 @@
1475 ** json_err_cstr() is used to get the error string. The caller may
1476 ** provide his own or may use an empty string to suppress the
1477 ** resultText property.
1478 **
1479 */
1480 cson_value * json_create_response( int resultCode,
1481 char const * pMsg,
1482 cson_value * payload){
1483 cson_value * v = NULL;
1484 cson_value * tmp = NULL;
1485 cson_object * o = NULL;
1486 int rc;
1487 resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode);
@@ -1751,10 +1760,13 @@
1751 ** function. If resetBlob is true then blob_reset(pSql) is called
1752 ** after preparing the query.
1753 **
1754 ** pTgt has the same semantics as described for
1755 ** json_stmt_to_array_of_obj().
 
 
 
1756 */
1757 cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt,
1758 char resetBlob){
1759 Stmt q = empty_Stmt;
1760 cson_value * pay = NULL;
@@ -1774,10 +1786,13 @@
1774 ** function returns a JSON Array containing the tag names (owned by
1775 ** the caller), else it returns NULL.
1776 **
1777 ** See info_tags_of_checkin() for more details (this is simply a JSON
1778 ** wrapper for that function).
 
 
 
1779 */
1780 cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){
1781 cson_value * v = NULL;
1782 char * tags = info_tags_of_checkin(rid, propagatingOnly);
1783 if(tags){
@@ -1813,15 +1828,11 @@
1813 cson_object * obj = NULL;
1814 cson_string * kRC;
1815 cson_string * kSymbol;
1816 cson_string * kNumber;
1817 cson_string * kDesc;
1818 int rc = cson_array_reserve( list, 35 );
1819 if(rc){
1820 assert( 0 && "Allocation error.");
1821 exit(1);
1822 }
1823 kRC = cson_new_string("resultCode",10);
1824 kSymbol = cson_new_string("cSymbol",7);
1825 kNumber = cson_new_string("number",6);
1826 kDesc = cson_new_string("description",11);
1827 #define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); \
@@ -2113,22 +2124,43 @@
2113 }
2114 }
2115 return i;
2116 }
2117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2118
2119 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
2120 JsonPageDef const * def;
2121 char const * cmd = json_command_arg(1+g.json.dispatchDepth);
2122 assert( NULL != pages );
2123 if( ! cmd ){
2124 Blob cmdNames = empty_blob;
2125 json_pagedefs_to_string(pages, &cmdNames);
2126 json_set_err(FSL_JSON_E_MISSING_ARGS,
2127 "No subcommand specified. Try one of (%s).",
2128 blob_str(&cmdNames));
2129 blob_reset(&cmdNames);
2130 return NULL;
2131 }
2132 def = json_handler_for_name( cmd, pages );
2133 if(!def){
2134 json_set_err(FSL_JSON_E_UNKNOWN_COMMAND,
@@ -2215,11 +2247,11 @@
2215 {"artifact", json_page_artifact, 0},
2216 {"branch", json_page_branch,0},
2217 {"cap", json_page_cap, 0},
2218 {"config", json_page_config, 0 },
2219 {"diff", json_page_diff, 0},
2220 {"dir", json_page_nyi, 0},
2221 {"finfo", json_page_finfo, 0},
2222 {"g", json_page_g, 0},
2223 {"HAI",json_page_version,0},
2224 {"login",json_page_login,0},
2225 {"logout",json_page_logout,0},
@@ -2227,49 +2259,16 @@
2227 {"rebuild",json_page_rebuild,0},
2228 {"report", json_page_report, 0},
2229 {"resultCodes", json_page_resultCodes,0},
2230 {"stat",json_page_stat,0},
2231 {"tag", json_page_tag,0},
2232 {"ticket", json_page_nyi,0},
2233 {"timeline", json_page_timeline,0},
2234 {"user",json_page_user,0},
2235 {"version",json_page_version,0},
2236 {"whoami",json_page_whoami,0},
2237 {"wiki",json_page_wiki,0},
2238 /* Last entry MUST have a NULL name. */
2239 {NULL,NULL,0}
2240 };
2241
2242
2243 /*
2244 ** Mapping of /json/ticket/XXX commands/paths to callbacks.
2245 */
2246 static const JsonPageDef JsonPageDefs_Ticket[] = {
2247 {"get", json_page_nyi, 0},
2248 {"list", json_page_nyi, 0},
2249 {"save", json_page_nyi, 1},
2250 {"create", json_page_nyi, 1},
2251 /* Last entry MUST have a NULL name. */
2252 {NULL,NULL,0}
2253 };
2254
2255 /*
2256 ** Mapping of /json/artifact/XXX commands/paths to callbacks.
2257 */
2258 static const JsonPageDef JsonPageDefs_Artifact[] = {
2259 {"vinfo", json_page_nyi, 0},
2260 {"finfo", json_page_nyi, 0},
2261 /* Last entry MUST have a NULL name. */
2262 {NULL,NULL,0}
2263 };
2264
2265 /*
2266 ** Mapping of /json/tag/XXX commands/paths to callbacks.
2267 */
2268 static const JsonPageDef JsonPageDefs_Tag[] = {
2269 {"list", json_page_nyi, 0},
2270 {"create", json_page_nyi, 1},
2271 /* Last entry MUST have a NULL name. */
2272 {NULL,NULL,0}
2273 };
2274
2275 /*
@@ -2299,11 +2298,11 @@
2299 else{
2300 rc = 0;
2301 g.json.dispatchDepth = 1;
2302 payload = (*pageDef->func)();
2303 }
2304 payload = json_create_response(rc, rc ? g.zErrMsg : NULL, payload);
2305 json_send_response(payload);
2306 cson_value_free(payload);
2307 return rc;
2308 }
2309
@@ -2319,26 +2318,16 @@
2319 char const * zCommand;
2320 BEGIN_TIMER;
2321 json_mode_bootstrap();
2322 zCommand = json_command_arg(1);
2323 if(!zCommand || !*zCommand){
2324 goto usage;
 
 
 
2325 }
2326 json_dispatch_root_command( zCommand );
2327 return;
2328 usage:
2329 {
2330 Blob cmdNames = empty_blob;
2331 blob_init(&cmdNames,
2332 "No command (sub-path) specified. Try one of: ",
2333 -1);
2334 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2335 json_err(FSL_JSON_E_MISSING_ARGS,
2336 blob_str(&cmdNames), 0);
2337 blob_reset(&cmdNames);
2338 }
2339
2340 }
2341 #endif /* FOSSIL_ENABLE_JSON for mkindex */
2342
2343 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */
2344 /*
@@ -2345,27 +2334,42 @@
2345 ** This function dispatches json commands and is the CLI equivalent of
2346 ** json_page_top().
2347 **
2348 ** COMMAND: json
2349 **
2350 ** Usage: %fossil json SUBCOMMAND
 
 
 
 
2351 **
2352 ** The commands include:
2353 **
 
 
2354 ** branch
2355 ** cap
 
 
 
 
 
 
 
 
2356 ** stat
 
2357 ** timeline
 
2358 ** version (alias: HAI)
 
2359 ** wiki
2360 **
2361 **
2362 ** TODOs:
2363 **
2364 ** tag
2365 ** ticket
2366 ** ...
2367 **
2368 */
2369 void json_cmd_top(void){
2370 char const * cmd = NULL;
2371 int rc = 0;
@@ -2401,18 +2405,18 @@
2401 fossil_exit(1);
2402 }
2403 return;
2404 usage:
2405 {
2406 Blob cmdNames = empty_blob;
2407 blob_init(&cmdNames,
2408 "No subcommand specified. Try one of: ", -1);
2409 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2410 json_err(FSL_JSON_E_MISSING_ARGS,
2411 blob_str(&cmdNames), 1);
2412 blob_reset(&cmdNames);
2413 fossil_exit(1);
2414 }
2415 }
2416 #endif /* FOSSIL_ENABLE_JSON for mkindex */
2417
2418 #endif /* FOSSIL_ENABLE_JSON */
2419
--- src/json.c
+++ src/json.c
@@ -490,11 +490,11 @@
490
491 /*
492 ** Wrapper around json_getenv() which tries to evaluate a payload/env
493 ** value as a boolean. Uses mostly the same logic as
494 ** json_getenv_int(), with the addition that string values which
495 ** either start with a digit 1..9 or the letters [tTyY] are considered
496 ** to be true. If this function cannot find a matching key/value then
497 ** dflt is returned. e.g. if it finds the key but the value is-a
498 ** Object then dftl is returned.
499 **
500 ** If an entry is found, this function guarantees that it will return
@@ -512,12 +512,14 @@
512 }else if( cson_value_is_string(v) ){
513 char const * sv = cson_string_cstr(cson_value_get_string(v));
514 if(!*sv || ('0'==*sv)){
515 return 0;
516 }else{
517 return ((('1'<=*sv) && ('9'>=*sv))
518 || ('t'==*sv) || ('T'==*sv)
519 || ('y'==*sv) || ('Y'==*sv)
520 )
521 ? 1 : 0;
522 }
523 }else if( cson_value_is_bool(v) ){
524 return cson_value_get_bool(v) ? 1 : 0;
525 }else if( cson_value_is_null(v) ){
@@ -541,11 +543,11 @@
543 /*
544 ** An extended form of find_option() which tries to look up a combo
545 ** GET/POST/CLI argument.
546 **
547 ** zKey must be the GET/POST parameter key. zCLILong must be the "long
548 s** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or
549 ** the "short form" CLI flag (if NULL, no short form is used).
550 **
551 ** If argPos is >=0 and no other match is found,
552 ** json_command_arg(argPos) is also checked.
553 **
@@ -572,11 +574,11 @@
574 }
575 return rc;
576 }
577
578 /*
579 ** Short-hand form of json_find_option_cstr2(zKey,zCLILong,zCLIShort,-1).
580 */
581 char const * json_find_option_cstr(char const * zKey,
582 char const * zCLILong,
583 char const * zCLIShort){
584 return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1);
@@ -602,18 +604,18 @@
604 }
605 return (-1==rc) ? dflt : rc;
606 }
607
608 /*
609 ** The integer equivalent of json_find_option_cstr2().
610 ** If the option is not found, dftl is returned.
611 */
612 int json_find_option_int(char const * zKey,
613 char const * zCLILong,
614 char const * zCLIShort,
615 int dflt ){
616 enum { Magic = -1947854832 };
617 int rc = Magic;
618 if(!g.isHTTP){
619 /* FIXME: use strtol() for better error/dflt handling. */
620 char const * opt = find_option(zCLILong ? zCLILong : zKey,
621 zCLIShort, 1);
@@ -979,11 +981,11 @@
981 ** parameters as this function, but returns the results as a JSON
982 ** Array (if splitting produced tokens) or NULL (if splitting failed
983 ** in any way or produced no tokens).
984 **
985 ** The returned value is owned by the caller. If not NULL then it
986 ** _will_ have a JSON type of Array.
987 */
988 cson_value * json_string_split2( char const * zStr,
989 char separator,
990 char doDeHttp ){
991 cson_value * v = cson_value_new_array();
@@ -1166,10 +1168,17 @@
1168 core, which we need before we call
1169 login_check_credentials(). */;
1170 login_check_credentials()/* populates g.perm */;
1171 }
1172 else{
1173 /* FIXME: we need an option which allows us to skip this. At least
1174 one known command (/json/version) does not need an opened
1175 repo. The problem here is we cannot know which functions need
1176 it from here (because command dispatching hasn't yet happened)
1177 and all other commands rely on the repo being opened before
1178 they are called. A textbook example of lack of foresight :/.
1179 */
1180 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
1181 }
1182 }
1183
1184 /*
@@ -1475,13 +1484,13 @@
1484 ** json_err_cstr() is used to get the error string. The caller may
1485 ** provide his own or may use an empty string to suppress the
1486 ** resultText property.
1487 **
1488 */
1489 static cson_value * json_create_response( int resultCode,
1490 char const * pMsg,
1491 cson_value * payload){
1492 cson_value * v = NULL;
1493 cson_value * tmp = NULL;
1494 cson_object * o = NULL;
1495 int rc;
1496 resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode);
@@ -1751,10 +1760,13 @@
1760 ** function. If resetBlob is true then blob_reset(pSql) is called
1761 ** after preparing the query.
1762 **
1763 ** pTgt has the same semantics as described for
1764 ** json_stmt_to_array_of_obj().
1765 **
1766 ** FIXME: change this to take a (char const *) instead of a blob,
1767 ** to simplify the trivial use-cases (which don't need a Blob).
1768 */
1769 cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt,
1770 char resetBlob){
1771 Stmt q = empty_Stmt;
1772 cson_value * pay = NULL;
@@ -1774,10 +1786,13 @@
1786 ** function returns a JSON Array containing the tag names (owned by
1787 ** the caller), else it returns NULL.
1788 **
1789 ** See info_tags_of_checkin() for more details (this is simply a JSON
1790 ** wrapper for that function).
1791 **
1792 ** If there are no tags then this function returns NULL, not an empty
1793 ** Array.
1794 */
1795 cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){
1796 cson_value * v = NULL;
1797 char * tags = info_tags_of_checkin(rid, propagatingOnly);
1798 if(tags){
@@ -1813,15 +1828,11 @@
1828 cson_object * obj = NULL;
1829 cson_string * kRC;
1830 cson_string * kSymbol;
1831 cson_string * kNumber;
1832 cson_string * kDesc;
1833 cson_array_reserve( list, 35 );
 
 
 
 
1834 kRC = cson_new_string("resultCode",10);
1835 kSymbol = cson_new_string("cSymbol",7);
1836 kNumber = cson_new_string("number",6);
1837 kDesc = cson_new_string("description",11);
1838 #define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); \
@@ -2113,22 +2124,43 @@
2124 }
2125 }
2126 return i;
2127 }
2128
2129 /*
2130 ** Creates an error message using zErrPrefix and the given array of
2131 ** JSON command definitions, and sets the g.json error state to
2132 ** reflect FSL_JSON_E_MISSING_ARGS. If zErrPrefix is NULL then
2133 ** some default is used (e.g. "Try one of: "). If it is "" then
2134 ** no prefix is used.
2135 **
2136 ** The intention is to provide the user (via the response.resultText)
2137 ** a list of available commands/subcommands.
2138 **
2139 */
2140 void json_dispatch_missing_args_err( JsonPageDef const * pCommands,
2141 char const * zErrPrefix ){
2142 Blob cmdNames = empty_blob;
2143 blob_init(&cmdNames,NULL,0);
2144 if( !zErrPrefix ) {
2145 zErrPrefix = "Try one of: ";
2146 }
2147 blob_append( &cmdNames, zErrPrefix, strlen(zErrPrefix) );
2148 json_pagedefs_to_string(pCommands, &cmdNames);
2149 json_set_err(FSL_JSON_E_MISSING_ARGS, "%s",
2150 blob_str(&cmdNames));
2151 blob_reset(&cmdNames);
2152 }
2153
2154 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
2155 JsonPageDef const * def;
2156 char const * cmd = json_command_arg(1+g.json.dispatchDepth);
2157 assert( NULL != pages );
2158 if( ! cmd ){
2159 json_dispatch_missing_args_err(pages,
2160 "No subcommand specified. "
2161 "Try one of: ");
 
 
 
2162 return NULL;
2163 }
2164 def = json_handler_for_name( cmd, pages );
2165 if(!def){
2166 json_set_err(FSL_JSON_E_UNKNOWN_COMMAND,
@@ -2215,11 +2247,11 @@
2247 {"artifact", json_page_artifact, 0},
2248 {"branch", json_page_branch,0},
2249 {"cap", json_page_cap, 0},
2250 {"config", json_page_config, 0 },
2251 {"diff", json_page_diff, 0},
2252 /*{"dir", json_page_nyi, 0},*/
2253 {"finfo", json_page_finfo, 0},
2254 {"g", json_page_g, 0},
2255 {"HAI",json_page_version,0},
2256 {"login",json_page_login,0},
2257 {"logout",json_page_logout,0},
@@ -2227,49 +2259,16 @@
2259 {"rebuild",json_page_rebuild,0},
2260 {"report", json_page_report, 0},
2261 {"resultCodes", json_page_resultCodes,0},
2262 {"stat",json_page_stat,0},
2263 {"tag", json_page_tag,0},
2264 /*{"ticket", json_page_nyi,0},*/
2265 {"timeline", json_page_timeline,0},
2266 {"user",json_page_user,0},
2267 {"version",json_page_version,0},
2268 {"whoami",json_page_whoami,0},
2269 {"wiki",json_page_wiki,0},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2270 /* Last entry MUST have a NULL name. */
2271 {NULL,NULL,0}
2272 };
2273
2274 /*
@@ -2299,11 +2298,11 @@
2298 else{
2299 rc = 0;
2300 g.json.dispatchDepth = 1;
2301 payload = (*pageDef->func)();
2302 }
2303 payload = json_create_response(rc, NULL, payload);
2304 json_send_response(payload);
2305 cson_value_free(payload);
2306 return rc;
2307 }
2308
@@ -2319,26 +2318,16 @@
2318 char const * zCommand;
2319 BEGIN_TIMER;
2320 json_mode_bootstrap();
2321 zCommand = json_command_arg(1);
2322 if(!zCommand || !*zCommand){
2323 json_dispatch_missing_args_err( JsonPageDefs,
2324 "No command (sub-path) specified."
2325 " Try one of: ");
2326 return;
2327 }
2328 json_dispatch_root_command( zCommand );
 
 
 
 
 
 
 
 
 
 
 
 
 
2329 }
2330 #endif /* FOSSIL_ENABLE_JSON for mkindex */
2331
2332 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */
2333 /*
@@ -2345,27 +2334,42 @@
2334 ** This function dispatches json commands and is the CLI equivalent of
2335 ** json_page_top().
2336 **
2337 ** COMMAND: json
2338 **
2339 ** Usage: %fossil json SUBCOMMAND ?OPTIONS?
2340 **
2341 ** In CLI mode, the -R REPO common option is supported. Due to limitations
2342 ** in the argument dispatching code, any -FLAGS must come after the final
2343 ** sub- (or subsub-) command.
2344 **
2345 ** The commands include:
2346 **
2347 ** anonymousPassord
2348 ** artifact
2349 ** branch
2350 ** cap
2351 ** diff
2352 ** g
2353 ** login
2354 ** logout
2355 ** query
2356 ** rebuild
2357 ** report
2358 ** resultCodes
2359 ** stat
2360 ** tag
2361 ** timeline
2362 ** user
2363 ** version (alias: HAI)
2364 ** whoami
2365 ** wiki
2366 **
2367 ** Run '%fossil json' without any subcommand to see the full list (but be
2368 ** aware that some listed might not yet be implemented).
2369 **
2370 ** PS: the notable TODO-commands include: config, dir, finfo, ticket
 
 
2371 **
2372 */
2373 void json_cmd_top(void){
2374 char const * cmd = NULL;
2375 int rc = 0;
@@ -2401,18 +2405,18 @@
2405 fossil_exit(1);
2406 }
2407 return;
2408 usage:
2409 {
2410 cson_value * payload;
2411 json_dispatch_missing_args_err( JsonPageDefs,
2412 "No subcommand specified."
2413 " Try one of: ");
2414 payload = json_create_response(0, NULL, NULL);
2415 json_send_response(payload);
2416 cson_value_free(payload);
2417 fossil_exit(1);
2418 }
2419 }
2420 #endif /* FOSSIL_ENABLE_JSON for mkindex */
2421
2422 #endif /* FOSSIL_ENABLE_JSON */
2423
--- src/json_detail.h
+++ src/json_detail.h
@@ -33,10 +33,14 @@
3333
** for warning codes.
3434
**
3535
** Numbers evenly dividable by 100 are "categories", and error codes
3636
** for a given category have their high bits set to the category
3737
** value.
38
+**
39
+** Maintenance reminder: when entries are added to this list, update
40
+** the code in json_page_resultCodes() and json_err_cstr() (both in
41
+** json.c)!
3842
**
3943
*/
4044
enum FossilJsonCodes {
4145
FSL_JSON_W_START = 0,
4246
FSL_JSON_W_UNKNOWN /*+1*/,
4347
--- src/json_detail.h
+++ src/json_detail.h
@@ -33,10 +33,14 @@
33 ** for warning codes.
34 **
35 ** Numbers evenly dividable by 100 are "categories", and error codes
36 ** for a given category have their high bits set to the category
37 ** value.
 
 
 
 
38 **
39 */
40 enum FossilJsonCodes {
41 FSL_JSON_W_START = 0,
42 FSL_JSON_W_UNKNOWN /*+1*/,
43
--- src/json_detail.h
+++ src/json_detail.h
@@ -33,10 +33,14 @@
33 ** for warning codes.
34 **
35 ** Numbers evenly dividable by 100 are "categories", and error codes
36 ** for a given category have their high bits set to the category
37 ** value.
38 **
39 ** Maintenance reminder: when entries are added to this list, update
40 ** the code in json_page_resultCodes() and json_err_cstr() (both in
41 ** json.c)!
42 **
43 */
44 enum FossilJsonCodes {
45 FSL_JSON_W_START = 0,
46 FSL_JSON_W_UNKNOWN /*+1*/,
47

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button