Fossil SCM
Consolidated some duplicated /json code, removed some dead code. Minor doc additions and cleanups.
Commit
6ca400a31520b36a4bcf93f4de27bb800152fcfc
Parent
48255fa98108484…
2 files changed
+89
-85
+4
+89
-85
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -490,11 +490,11 @@ | ||
| 490 | 490 | |
| 491 | 491 | /* |
| 492 | 492 | ** Wrapper around json_getenv() which tries to evaluate a payload/env |
| 493 | 493 | ** value as a boolean. Uses mostly the same logic as |
| 494 | 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 | |
| 495 | +** either start with a digit 1..9 or the letters [tTyY] are considered | |
| 496 | 496 | ** to be true. If this function cannot find a matching key/value then |
| 497 | 497 | ** dflt is returned. e.g. if it finds the key but the value is-a |
| 498 | 498 | ** Object then dftl is returned. |
| 499 | 499 | ** |
| 500 | 500 | ** If an entry is found, this function guarantees that it will return |
| @@ -512,12 +512,14 @@ | ||
| 512 | 512 | }else if( cson_value_is_string(v) ){ |
| 513 | 513 | char const * sv = cson_string_cstr(cson_value_get_string(v)); |
| 514 | 514 | if(!*sv || ('0'==*sv)){ |
| 515 | 515 | return 0; |
| 516 | 516 | }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 | + ) | |
| 519 | 521 | ? 1 : 0; |
| 520 | 522 | } |
| 521 | 523 | }else if( cson_value_is_bool(v) ){ |
| 522 | 524 | return cson_value_get_bool(v) ? 1 : 0; |
| 523 | 525 | }else if( cson_value_is_null(v) ){ |
| @@ -541,11 +543,11 @@ | ||
| 541 | 543 | /* |
| 542 | 544 | ** An extended form of find_option() which tries to look up a combo |
| 543 | 545 | ** GET/POST/CLI argument. |
| 544 | 546 | ** |
| 545 | 547 | ** 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 | |
| 547 | 549 | ** the "short form" CLI flag (if NULL, no short form is used). |
| 548 | 550 | ** |
| 549 | 551 | ** If argPos is >=0 and no other match is found, |
| 550 | 552 | ** json_command_arg(argPos) is also checked. |
| 551 | 553 | ** |
| @@ -572,11 +574,11 @@ | ||
| 572 | 574 | } |
| 573 | 575 | return rc; |
| 574 | 576 | } |
| 575 | 577 | |
| 576 | 578 | /* |
| 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). | |
| 578 | 580 | */ |
| 579 | 581 | char const * json_find_option_cstr(char const * zKey, |
| 580 | 582 | char const * zCLILong, |
| 581 | 583 | char const * zCLIShort){ |
| 582 | 584 | return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1); |
| @@ -602,18 +604,18 @@ | ||
| 602 | 604 | } |
| 603 | 605 | return (-1==rc) ? dflt : rc; |
| 604 | 606 | } |
| 605 | 607 | |
| 606 | 608 | /* |
| 607 | -** The integer equivalent of json_find_option_cstr(). | |
| 609 | +** The integer equivalent of json_find_option_cstr2(). | |
| 608 | 610 | ** If the option is not found, dftl is returned. |
| 609 | 611 | */ |
| 610 | 612 | int json_find_option_int(char const * zKey, |
| 611 | 613 | char const * zCLILong, |
| 612 | 614 | char const * zCLIShort, |
| 613 | 615 | int dflt ){ |
| 614 | - enum { Magic = -947 }; | |
| 616 | + enum { Magic = -1947854832 }; | |
| 615 | 617 | int rc = Magic; |
| 616 | 618 | if(!g.isHTTP){ |
| 617 | 619 | /* FIXME: use strtol() for better error/dflt handling. */ |
| 618 | 620 | char const * opt = find_option(zCLILong ? zCLILong : zKey, |
| 619 | 621 | zCLIShort, 1); |
| @@ -979,11 +981,11 @@ | ||
| 979 | 981 | ** parameters as this function, but returns the results as a JSON |
| 980 | 982 | ** Array (if splitting produced tokens) or NULL (if splitting failed |
| 981 | 983 | ** in any way or produced no tokens). |
| 982 | 984 | ** |
| 983 | 985 | ** 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. | |
| 985 | 987 | */ |
| 986 | 988 | cson_value * json_string_split2( char const * zStr, |
| 987 | 989 | char separator, |
| 988 | 990 | char doDeHttp ){ |
| 989 | 991 | cson_value * v = cson_value_new_array(); |
| @@ -1166,10 +1168,17 @@ | ||
| 1166 | 1168 | core, which we need before we call |
| 1167 | 1169 | login_check_credentials(). */; |
| 1168 | 1170 | login_check_credentials()/* populates g.perm */; |
| 1169 | 1171 | } |
| 1170 | 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 | + */ | |
| 1171 | 1180 | db_find_and_open_repository(OPEN_ANY_SCHEMA,0); |
| 1172 | 1181 | } |
| 1173 | 1182 | } |
| 1174 | 1183 | |
| 1175 | 1184 | /* |
| @@ -1475,13 +1484,13 @@ | ||
| 1475 | 1484 | ** json_err_cstr() is used to get the error string. The caller may |
| 1476 | 1485 | ** provide his own or may use an empty string to suppress the |
| 1477 | 1486 | ** resultText property. |
| 1478 | 1487 | ** |
| 1479 | 1488 | */ |
| 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){ | |
| 1483 | 1492 | cson_value * v = NULL; |
| 1484 | 1493 | cson_value * tmp = NULL; |
| 1485 | 1494 | cson_object * o = NULL; |
| 1486 | 1495 | int rc; |
| 1487 | 1496 | resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode); |
| @@ -1751,10 +1760,13 @@ | ||
| 1751 | 1760 | ** function. If resetBlob is true then blob_reset(pSql) is called |
| 1752 | 1761 | ** after preparing the query. |
| 1753 | 1762 | ** |
| 1754 | 1763 | ** pTgt has the same semantics as described for |
| 1755 | 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). | |
| 1756 | 1768 | */ |
| 1757 | 1769 | cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt, |
| 1758 | 1770 | char resetBlob){ |
| 1759 | 1771 | Stmt q = empty_Stmt; |
| 1760 | 1772 | cson_value * pay = NULL; |
| @@ -1774,10 +1786,13 @@ | ||
| 1774 | 1786 | ** function returns a JSON Array containing the tag names (owned by |
| 1775 | 1787 | ** the caller), else it returns NULL. |
| 1776 | 1788 | ** |
| 1777 | 1789 | ** See info_tags_of_checkin() for more details (this is simply a JSON |
| 1778 | 1790 | ** wrapper for that function). |
| 1791 | +** | |
| 1792 | +** If there are no tags then this function returns NULL, not an empty | |
| 1793 | +** Array. | |
| 1779 | 1794 | */ |
| 1780 | 1795 | cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){ |
| 1781 | 1796 | cson_value * v = NULL; |
| 1782 | 1797 | char * tags = info_tags_of_checkin(rid, propagatingOnly); |
| 1783 | 1798 | if(tags){ |
| @@ -1813,15 +1828,11 @@ | ||
| 1813 | 1828 | cson_object * obj = NULL; |
| 1814 | 1829 | cson_string * kRC; |
| 1815 | 1830 | cson_string * kSymbol; |
| 1816 | 1831 | cson_string * kNumber; |
| 1817 | 1832 | 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 ); | |
| 1823 | 1834 | kRC = cson_new_string("resultCode",10); |
| 1824 | 1835 | kSymbol = cson_new_string("cSymbol",7); |
| 1825 | 1836 | kNumber = cson_new_string("number",6); |
| 1826 | 1837 | kDesc = cson_new_string("description",11); |
| 1827 | 1838 | #define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); \ |
| @@ -2113,22 +2124,43 @@ | ||
| 2113 | 2124 | } |
| 2114 | 2125 | } |
| 2115 | 2126 | return i; |
| 2116 | 2127 | } |
| 2117 | 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 | +} | |
| 2118 | 2153 | |
| 2119 | 2154 | cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ |
| 2120 | 2155 | JsonPageDef const * def; |
| 2121 | 2156 | char const * cmd = json_command_arg(1+g.json.dispatchDepth); |
| 2122 | 2157 | assert( NULL != pages ); |
| 2123 | 2158 | 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: "); | |
| 2130 | 2162 | return NULL; |
| 2131 | 2163 | } |
| 2132 | 2164 | def = json_handler_for_name( cmd, pages ); |
| 2133 | 2165 | if(!def){ |
| 2134 | 2166 | json_set_err(FSL_JSON_E_UNKNOWN_COMMAND, |
| @@ -2215,11 +2247,11 @@ | ||
| 2215 | 2247 | {"artifact", json_page_artifact, 0}, |
| 2216 | 2248 | {"branch", json_page_branch,0}, |
| 2217 | 2249 | {"cap", json_page_cap, 0}, |
| 2218 | 2250 | {"config", json_page_config, 0 }, |
| 2219 | 2251 | {"diff", json_page_diff, 0}, |
| 2220 | -{"dir", json_page_nyi, 0}, | |
| 2252 | +/*{"dir", json_page_nyi, 0},*/ | |
| 2221 | 2253 | {"finfo", json_page_finfo, 0}, |
| 2222 | 2254 | {"g", json_page_g, 0}, |
| 2223 | 2255 | {"HAI",json_page_version,0}, |
| 2224 | 2256 | {"login",json_page_login,0}, |
| 2225 | 2257 | {"logout",json_page_logout,0}, |
| @@ -2227,49 +2259,16 @@ | ||
| 2227 | 2259 | {"rebuild",json_page_rebuild,0}, |
| 2228 | 2260 | {"report", json_page_report, 0}, |
| 2229 | 2261 | {"resultCodes", json_page_resultCodes,0}, |
| 2230 | 2262 | {"stat",json_page_stat,0}, |
| 2231 | 2263 | {"tag", json_page_tag,0}, |
| 2232 | -{"ticket", json_page_nyi,0}, | |
| 2264 | +/*{"ticket", json_page_nyi,0},*/ | |
| 2233 | 2265 | {"timeline", json_page_timeline,0}, |
| 2234 | 2266 | {"user",json_page_user,0}, |
| 2235 | 2267 | {"version",json_page_version,0}, |
| 2236 | 2268 | {"whoami",json_page_whoami,0}, |
| 2237 | 2269 | {"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 | 2270 | /* Last entry MUST have a NULL name. */ |
| 2272 | 2271 | {NULL,NULL,0} |
| 2273 | 2272 | }; |
| 2274 | 2273 | |
| 2275 | 2274 | /* |
| @@ -2299,11 +2298,11 @@ | ||
| 2299 | 2298 | else{ |
| 2300 | 2299 | rc = 0; |
| 2301 | 2300 | g.json.dispatchDepth = 1; |
| 2302 | 2301 | payload = (*pageDef->func)(); |
| 2303 | 2302 | } |
| 2304 | - payload = json_create_response(rc, rc ? g.zErrMsg : NULL, payload); | |
| 2303 | + payload = json_create_response(rc, NULL, payload); | |
| 2305 | 2304 | json_send_response(payload); |
| 2306 | 2305 | cson_value_free(payload); |
| 2307 | 2306 | return rc; |
| 2308 | 2307 | } |
| 2309 | 2308 | |
| @@ -2319,26 +2318,16 @@ | ||
| 2319 | 2318 | char const * zCommand; |
| 2320 | 2319 | BEGIN_TIMER; |
| 2321 | 2320 | json_mode_bootstrap(); |
| 2322 | 2321 | zCommand = json_command_arg(1); |
| 2323 | 2322 | 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; | |
| 2325 | 2327 | } |
| 2326 | 2328 | 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 | 2329 | } |
| 2341 | 2330 | #endif /* FOSSIL_ENABLE_JSON for mkindex */ |
| 2342 | 2331 | |
| 2343 | 2332 | #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ |
| 2344 | 2333 | /* |
| @@ -2345,27 +2334,42 @@ | ||
| 2345 | 2334 | ** This function dispatches json commands and is the CLI equivalent of |
| 2346 | 2335 | ** json_page_top(). |
| 2347 | 2336 | ** |
| 2348 | 2337 | ** COMMAND: json |
| 2349 | 2338 | ** |
| 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. | |
| 2351 | 2344 | ** |
| 2352 | 2345 | ** The commands include: |
| 2353 | 2346 | ** |
| 2347 | +** anonymousPassord | |
| 2348 | +** artifact | |
| 2354 | 2349 | ** branch |
| 2355 | 2350 | ** cap |
| 2351 | +** diff | |
| 2352 | +** g | |
| 2353 | +** login | |
| 2354 | +** logout | |
| 2355 | +** query | |
| 2356 | +** rebuild | |
| 2357 | +** report | |
| 2358 | +** resultCodes | |
| 2356 | 2359 | ** stat |
| 2360 | +** tag | |
| 2357 | 2361 | ** timeline |
| 2362 | +** user | |
| 2358 | 2363 | ** version (alias: HAI) |
| 2364 | +** whoami | |
| 2359 | 2365 | ** wiki |
| 2360 | 2366 | ** |
| 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). | |
| 2363 | 2369 | ** |
| 2364 | -** tag | |
| 2365 | -** ticket | |
| 2366 | -** ... | |
| 2370 | +** PS: the notable TODO-commands include: config, dir, finfo, ticket | |
| 2367 | 2371 | ** |
| 2368 | 2372 | */ |
| 2369 | 2373 | void json_cmd_top(void){ |
| 2370 | 2374 | char const * cmd = NULL; |
| 2371 | 2375 | int rc = 0; |
| @@ -2401,18 +2405,18 @@ | ||
| 2401 | 2405 | fossil_exit(1); |
| 2402 | 2406 | } |
| 2403 | 2407 | return; |
| 2404 | 2408 | usage: |
| 2405 | 2409 | { |
| 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); | |
| 2413 | 2417 | fossil_exit(1); |
| 2414 | 2418 | } |
| 2415 | 2419 | } |
| 2416 | 2420 | #endif /* FOSSIL_ENABLE_JSON for mkindex */ |
| 2417 | 2421 | |
| 2418 | 2422 | #endif /* FOSSIL_ENABLE_JSON */ |
| 2419 | 2423 |
| --- 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 |
+4
| --- src/json_detail.h | ||
| +++ src/json_detail.h | ||
| @@ -33,10 +33,14 @@ | ||
| 33 | 33 | ** for warning codes. |
| 34 | 34 | ** |
| 35 | 35 | ** Numbers evenly dividable by 100 are "categories", and error codes |
| 36 | 36 | ** for a given category have their high bits set to the category |
| 37 | 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)! | |
| 38 | 42 | ** |
| 39 | 43 | */ |
| 40 | 44 | enum FossilJsonCodes { |
| 41 | 45 | FSL_JSON_W_START = 0, |
| 42 | 46 | FSL_JSON_W_UNKNOWN /*+1*/, |
| 43 | 47 |
| --- 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 |