Fossil

Check-in [b3653265]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:More cleanups to the cson_cgi removal refactoring. Added common "indent" parameter to control indentation of JSON (uses cson_output_opt.indentation semantics).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1: b3653265d1552d8392c93088ac9f9efd3c1ec364
User & Date: stephan 2011-09-18 05:45:43
Context
2011-09-18
05:51
merged trunk [b54b8e751a]. check-in: 76c4ae5e user: stephan tags: json
05:45
More cleanups to the cson_cgi removal refactoring. Added common "indent" parameter to control indentation of JSON (uses cson_output_opt.indentation semantics). check-in: b3653265 user: stephan tags: json
04:31
Factored out cson_cgi bits - now using fossil's CGI bits. Removed cson_cgi from cson_amalgamation (cuts its size considerably). Seems to still work, and this removes some discrepancies in how CGI/server modes are handled. check-in: 4cf96814 user: stephan tags: json
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/cgi.c.

721
722
723
724
725
726
727
728
729
730

731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755

756
757
758
759
760
761
762
763
764
765
766
767
768

769
770
771
772
773
774
775
...
807
808
809
810
811
812
813
814
815

816
817


818










819
820
821
822
823
824
825
          return 0;
        }
      }
    }
}

/*
** Reads a JSON object from the first contentLen bytes of zIn.
** On success 0 is returned and g.json.post is updated to hold
** the content. On error non-0 is returned.

*/
static int cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
  cson_value * jv = NULL;
  int rc;
  CgiPostReadState state;
  cson_parse_info pinfo = cson_parse_info_empty;
  assert( 0 != contentLen );
  state.fh = zIn;
  state.len = contentLen;
  state.pos = 0;
  rc = cson_parse( &jv, cson_data_source_FILE_n, &state, NULL, &pinfo );
  if( rc ){
#if 0
    fprintf(stderr, "%s: Parsing POST as JSON failed: code=%d (%s) line=%u, col=%u\n",
            __FILE__, rc, cson_rc_string(rc), pinfo.line, pinfo.col );
#endif
    return rc;
  }
  rc = json_gc_add( "$POST", jv, 1 );
  if( 0 == rc ){
    g.json.post.v = jv;
    g.json.post.o = cson_value_get_object( jv );
    assert( g.json.post.o && "FIXME: also support an Array as POST data node." ); 
  }
  return rc;

}


/*
** Initialize the query parameter database.  Information is pulled from
** the QUERY_STRING environment variable (if it exists), from standard
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;
  json_main_bootstrap();

  cgi_destination(CGI_BODY);

  z = (char*)P("HTTP_COOKIE");
  if( z ){
    z = mprintf("%s",z);
    add_param_list(z, ';');
  }
................................................................................
    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
    }else if( fossil_strcmp(zType, "application/json")
              || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
              || fossil_strcmp(zType,"application/javascript")){
      g.json.isJsonMode = 1;
      cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
      cgi_set_content_type("application/json")
        /* FIXME: guess a proper content type value based on the

           Accept header.  We have code for this in cson_cgi.
        */


        ;










    }
  }

}

/*
** This is the comparison function used to sort the aParamQP[] array of







|
|
|
>

|



<




|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>













>







 







|
<
>
|
<
>
>
|
>
>
>
>
>
>
>
>
>
>







721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736

737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
...
809
810
811
812
813
814
815
816

817
818

819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
          return 0;
        }
      }
    }
}

/*
** Reads a JSON object from the first contentLen bytes of zIn.  On
** g.json.post is updated to hold the content. On error a
** FSL_JSON_E_INVALID_REQUEST response is output and fossil_exit(0) is
** called.
*/
static void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
  cson_value * jv = NULL;
  int rc;
  CgiPostReadState state;

  assert( 0 != contentLen );
  state.fh = zIn;
  state.len = contentLen;
  state.pos = 0;
  rc = cson_parse( &jv, cson_data_source_FILE_n, &state, NULL, NULL );
  if(rc){
    goto invalidRequest;
  }else{
    json_gc_add( "POST.JSON", jv, 1 );
    g.json.post.v = jv;
    g.json.post.o = cson_value_get_object( jv );
    if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
      goto invalidRequest;
    }
  }
  return;
  invalidRequest:
  cgi_set_content_type(json_guess_content_type());
  json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
  fossil_exit(0);
}


/*
** Initialize the query parameter database.  Information is pulled from
** the QUERY_STRING environment variable (if it exists), from standard
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;
  json_main_bootstrap();
  g.isCGI = 1;
  cgi_destination(CGI_BODY);

  z = (char*)P("HTTP_COOKIE");
  if( z ){
    z = mprintf("%s",z);
    add_param_list(z, ';');
  }
................................................................................
    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
    }else if( fossil_strcmp(zType, "application/json")
              || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
              || fossil_strcmp(zType,"application/javascript")){
      g.json.isJsonMode = 1;
      cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
      /* FIXMEs:


      - See if fossil really needs g.cgiIn to be set for this purpose

      (i don't think it does). If it does then fill g.cgiIn and
      refactor to parse the JSON from there.
      
      - After parsing POST JSON, copy the "first layer" of keys/values
      to cgi_setenv(), honoring the upper-case distinction used
      in add_param_list(). However...

      - If we do that then we might get a disconnect in precedence of
      GET/POST arguments. i prefer for GET entries to take precedence
      over like-named POST entries, but in order for that to happen we
      need to process QUERY_STRING _after_ reading the POST data.
      */
      cgi_set_content_type(json_guess_content_type());
    }
  }

}

/*
** This is the comparison function used to sort the aParamQP[] array of

Changes to src/json.c.

224
225
226
227
228
229
230














































231
232
233
234
235
236
237
...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338







339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412





















413
414
415
416
417
418
419
...
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944

945
946
947
948
949
950
951
....
1114
1115
1116
1117
1118
1119
1120









1121
1122
1123
1124
1125
1126
1127
** On succes, transfers ownership of v to g.json.param.o.
** On error ownership of v is not modified.
*/
int json_setenv( char const * zKey, cson_value * v ){
  return cson_object_set( g.json.param.o, zKey, v );
}
















































/*
** Returns the current request's JSON authentication token, or NULL if
** none is found. The token's memory is owned by (or shared with)
** g.json.
**
** If an auth token is found in the GET/POST JSON request data then
................................................................................
**
** This must only be called once, or an assertion may be triggered.
*/
static void json_mode_bootstrap(){
  static char once = 0  /* guard against multiple runs */;
  char const * zPath = P("PATH_INFO");
  cson_value * pathSplit = NULL;
  cson_array * ar = NULL;
  assert( (0==once) && "json_mode_bootstrap() called too many times!");
  if( once ){
    return;
  }else{
    once = 1;
  }
  g.json.isJsonMode = 1;
  g.json.resultCode = 0;
  g.json.cmd.offset = -1;
  if( !g.isCGI && g.fullHttpReply ){
    /* workaround for server mode, so we see it as CGI mode. */
    g.isCGI = 1;
  }







#if defined(NDEBUG)
  /* avoids debug messages on stderr in JSON mode */
  sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0);
#endif

  g.json.cmd.v = cson_value_new_array();
  ar = g.json.cmd.a = cson_value_get_array(g.json.cmd.v);
  json_gc_add( FossilJsonKeys.commandPath, g.json.cmd.v, 1 );
  /*
    The following if/else block translates the PATH_INFO path (in
    CLI/server modes) or g.argv (CLI mode) into an internal list so
    that we can simplify command dispatching later on.

    Note that translating g.argv this way is overkill but allows us to
    avoid CLI-only special-case handling in other code, e.g.
    json_command_arg().
  */
  if( zPath ){/* either CLI or server mode... */
    /* Translate fossil's PATH_INFO into cson_cgi for later
       convenience, to help consolidate how we handle CGI/server
       modes. This block is hit when running in plain server mode.
    */
    char const * p = zPath /* current byte */;
    char const * head = p  /* current start-of-token */;
    unsigned int len = 0   /* current token's lengh */;
    assert( g.isCGI && "g.isCGI should have been set by now." );
    for( ;*p!='?'; ++p){
      if( !*p || ('/' == *p) ){
        if( len ){
          cson_value * part;
          char * zPart;
          assert( head != p );
          zPart = (char*)malloc(len+1);
          assert( zPart != NULL );
          memcpy(zPart, head, len);
          zPart[len] = 0;
          dehttpize(zPart);
          part = cson_value_new_string(zPart, strlen(zPart));
          free(zPart);
          cson_array_append( ar, part );
          len = 0;
        }
        if( !*p ){
          break;
        }
        head = p+1;
        continue;
................................................................................
    cson_value * part;
    for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
      arg = g.argv[i];
      if( !arg || !*arg ){
        continue;
      }
      part = cson_value_new_string(arg,strlen(arg));
      cson_array_append(ar, part);
    }
  }
  
  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc. */
  g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
  if( g.json.reqPayload.v ){
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }





















  json_auth_token()/* will copy our auth token, if any, to fossil's core. */;
  if( g.isCGI ){
    login_check_credentials()/* populates g.perm */;
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
  }
................................................................................
** Implementation of the /json/stat page/command.
**
*/
cson_value * json_page_stat(void){
  i64 t, fsize;
  int n, m;
  const char *zDb;
  enum { BufLen = 200 };
  char zBuf[BufLen];
  cson_value * jv = NULL;
  cson_object * jo = NULL;
  cson_value * jv2 = NULL;
  cson_object * jo2 = NULL;
#if 0 /* FIXME: credentials */
  login_check_credentials();
  if( !g.okRead ){ login_needed(); return NULL; }
#endif

#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));

  jv = cson_value_new_object();
  jo = cson_value_get_object(jv);

  sqlite3_snprintf(BufLen, zBuf, db_get("project-name",""));
  SETBUF(jo, "projectName");
................................................................................
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = 1002;
  cson_value * payload = NULL;
  JsonPageDef const * pageDef;









  json_main_bootstrap();
  json_mode_bootstrap();
  if( g.argc<3 ){
    goto usage;
  }
  db_find_and_open_repository(0, 0);
  cmd = json_command_arg(1);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<













>
>
>
>
>
>
>






|










|
|
<
<
<




|












|







 







|













>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|





|
|
|
|
>







 







>
>
>
>
>
>
>
>
>







224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
...
364
365
366
367
368
369
370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
...
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
...
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
....
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
** On succes, transfers ownership of v to g.json.param.o.
** On error ownership of v is not modified.
*/
int json_setenv( char const * zKey, cson_value * v ){
  return cson_object_set( g.json.param.o, zKey, v );
}

/*
** Guesses a RESPONSE Content-Type value based (primarily) on the
** HTTP_ACCEPT header.
**
** It will try to figure out if the client can support
** application/json or application/javascript, and will fall back to
** text/plain if it cannot figure out anything more specific.
**
** Returned memory is static and immutable.
**
*/
char const * json_guess_content_type(){
  char const * cset;
  char doUtf8;
  cset = PD("HTTP_ACCEPT_CHARSET",NULL);
  doUtf8 = ((NULL == cset) || (NULL!=strstr("utf-8",cset)))
    ? 1 : 0;
  if( g.json.jsonp ){
    return doUtf8
      ? "application/javascript; charset=utf-8"
      : "application/javascript";
  }else{
    /*
      Content-type
      
      If the browser does not sent an ACCEPT for application/json
      then we fall back to text/plain.
    */
    char const * cstr;
    cstr = PD("HTTP_ACCEPT",NULL);
    if( NULL == cstr ){
      return doUtf8
        ? "application/json; charset=utf-8"
        : "application/json";
    }else{
      if( strstr( cstr, "application/json" )
          || strstr( cstr, "*/*" ) ){
        return doUtf8
          ? "application/json; charset=utf-8"
          : "application/json";
      }else{
        return "text/plain";
      }
    }
  }
}

/*
** Returns the current request's JSON authentication token, or NULL if
** none is found. The token's memory is owned by (or shared with)
** g.json.
**
** If an auth token is found in the GET/POST JSON request data then
................................................................................
**
** This must only be called once, or an assertion may be triggered.
*/
static void json_mode_bootstrap(){
  static char once = 0  /* guard against multiple runs */;
  char const * zPath = P("PATH_INFO");
  cson_value * pathSplit = NULL;

  assert( (0==once) && "json_mode_bootstrap() called too many times!");
  if( once ){
    return;
  }else{
    once = 1;
  }
  g.json.isJsonMode = 1;
  g.json.resultCode = 0;
  g.json.cmd.offset = -1;
  if( !g.isCGI && g.fullHttpReply ){
    /* workaround for server mode, so we see it as CGI mode. */
    g.isCGI = 1;
  }
  if(! g.json.post.v ){
    /* If cgi_init() reads POSTed JSON then it sets the content type.
       If it did not then we need to set it.
    */
    cgi_set_content_type(json_guess_content_type());
  }

#if defined(NDEBUG)
  /* avoids debug messages on stderr in JSON mode */
  sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0);
#endif

  g.json.cmd.v = cson_value_new_array();
  g.json.cmd.a = cson_value_get_array(g.json.cmd.v);
  json_gc_add( FossilJsonKeys.commandPath, g.json.cmd.v, 1 );
  /*
    The following if/else block translates the PATH_INFO path (in
    CLI/server modes) or g.argv (CLI mode) into an internal list so
    that we can simplify command dispatching later on.

    Note that translating g.argv this way is overkill but allows us to
    avoid CLI-only special-case handling in other code, e.g.
    json_command_arg().
  */
  if( zPath ){/* Either CGI or server mode... */
    /* Translate PATH_INFO into JSON for later convenience. */



    char const * p = zPath /* current byte */;
    char const * head = p  /* current start-of-token */;
    unsigned int len = 0   /* current token's lengh */;
    assert( g.isCGI && "g.isCGI should have been set by now." );
    for( ; ; ++p){
      if( !*p || ('/' == *p) ){
        if( len ){
          cson_value * part;
          char * zPart;
          assert( head != p );
          zPart = (char*)malloc(len+1);
          assert( zPart != NULL );
          memcpy(zPart, head, len);
          zPart[len] = 0;
          dehttpize(zPart);
          part = cson_value_new_string(zPart, strlen(zPart));
          free(zPart);
          cson_array_append( g.json.cmd.a, part );
          len = 0;
        }
        if( !*p ){
          break;
        }
        head = p+1;
        continue;
................................................................................
    cson_value * part;
    for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
      arg = g.argv[i];
      if( !arg || !*arg ){
        continue;
      }
      part = cson_value_new_string(arg,strlen(arg));
      cson_array_append(g.json.cmd.a, part);
    }
  }
  
  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc. */
  g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
  if( g.json.reqPayload.v ){
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }

  do{/* set up JSON out formatting options. */
    unsigned char indent = g.isCGI ? 0 : 1;
    cson_value const * indentV = json_getenv("indent");
    if(indentV){
      if(cson_value_is_string(indentV)){
        int n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
        indent = (n>0)
          ? (unsigned char)n
          : 0;
      }else if(cson_value_is_number(indentV)){
        double n = cson_value_get_integer(indentV);
        indent = (n>0)
          ? (unsigned char)n
          : 0;
      }
    }
    g.json.outOpt.indentation = indent;
    g.json.outOpt.addNewline = g.isCGI ? 0 : 1;
  }while(0);

  json_auth_token()/* will copy our auth token, if any, to fossil's core. */;
  if( g.isCGI ){
    login_check_credentials()/* populates g.perm */;
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
  }
................................................................................
** Implementation of the /json/stat page/command.
**
*/
cson_value * json_page_stat(void){
  i64 t, fsize;
  int n, m;
  const char *zDb;
  enum { BufLen = 1000 };
  char zBuf[BufLen];
  cson_value * jv = NULL;
  cson_object * jo = NULL;
  cson_value * jv2 = NULL;
  cson_object * jo2 = NULL;
  login_check_credentials();
  if( !g.perm.Read ){
    g.json.resultCode = FSL_JSON_E_DENIED;
    return NULL;
  }
#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));

  jv = cson_value_new_object();
  jo = cson_value_get_object(jv);

  sqlite3_snprintf(BufLen, zBuf, db_get("project-name",""));
  SETBUF(jo, "projectName");
................................................................................
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = 1002;
  cson_value * payload = NULL;
  JsonPageDef const * pageDef;
  memset( &g.perm, 0xff, sizeof(g.perm) )
    /* In CLI mode fossil does not use permissions
       and they all default to false. We enable them
       here because (A) fossil doesn't use them in local
       mode but (B) having them set gives us one less
       difference in the CLI/CGI/Server-mode JSON
       handling.
    */
    ;
  json_main_bootstrap();
  json_mode_bootstrap();
  if( g.argc<3 ){
    goto usage;
  }
  db_find_and_open_repository(0, 0);
  cmd = json_command_arg(1);

Changes to src/login.c.

949
950
951
952
953
954
955




956
957
958
959

960
961
962
963
964
965
966
}

/*
** Call this routine when the credential check fails.  It causes
** a redirect to the "login" page.
*/
void login_needed(void){




  const char *zUrl = PD("REQUEST_URI", "index");
  cgi_redirect(mprintf("login?g=%T", zUrl));
  /* NOTREACHED */
  assert(0);

}

/*
** Call this routine if the user lacks okHistory permission.  If
** the anonymous user has okHistory permission, then paint a mesage
** to inform the user that much more information is available by
** logging in as anonymous.







>
>
>
>
|
|
|
|
>







949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
}

/*
** Call this routine when the credential check fails.  It causes
** a redirect to the "login" page.
*/
void login_needed(void){
  if(g.json.isJsonMode){
    json_err( FSL_JSON_E_DENIED, NULL, 1 );
    fossil_exit(0);
  }else{
    const char *zUrl = PD("REQUEST_URI", "index");
    cgi_redirect(mprintf("login?g=%T", zUrl));
    /* NOTREACHED */
    assert(0);
  }
}

/*
** Call this routine if the user lacks okHistory permission.  If
** the anonymous user has okHistory permission, then paint a mesage
** to inform the user that much more information is available by
** logging in as anonymous.

Changes to src/main.c.

178
179
180
181
182
183
184

185
186
187
188
189
190
191
...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
                                  responses and always exit() with
                                  code 0 to avoid an HTTP 500 error.
                               */
    int resultCode;            /* used for passing back specific codes from /json callbacks. */
    int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
    cson_output_opt outOpt;    /* formatting options for JSON mode. */
    cson_value * authToken;    /* authentication token */

    struct {                   /* "garbage collector" */
      cson_value * v;
      cson_object * o;
    } gc;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_array * a;
................................................................................
                                    code is needed before the db is opened,
                                    so we can't sql for it.*/;
#else
  g.json.errorDetailParanoia = 0;
#endif
  g.json.outOpt = cson_output_opt_empty;
  g.json.outOpt.addNewline = 1;
  g.json.outOpt.indentation = 1 /* FIXME: make configurable */;
  for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
  if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
    zCmdName = "cgi";
    g.isCGI = 1;
  }else if( argc<2 ){
    fossil_fatal("Usage: %s COMMAND ...\n"
                 "\"%s help\" for a list of available commands\n"







>







 







|







178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
                                  responses and always exit() with
                                  code 0 to avoid an HTTP 500 error.
                               */
    int resultCode;            /* used for passing back specific codes from /json callbacks. */
    int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
    cson_output_opt outOpt;    /* formatting options for JSON mode. */
    cson_value * authToken;    /* authentication token */
    char const * jsonp;        /* Name of JSONP function wrapper. */
    struct {                   /* "garbage collector" */
      cson_value * v;
      cson_object * o;
    } gc;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_array * a;
................................................................................
                                    code is needed before the db is opened,
                                    so we can't sql for it.*/;
#else
  g.json.errorDetailParanoia = 0;
#endif
  g.json.outOpt = cson_output_opt_empty;
  g.json.outOpt.addNewline = 1;
  g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
  for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
  if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
    zCmdName = "cgi";
    g.isCGI = 1;
  }else if( argc<2 ){
    fossil_fatal("Usage: %s COMMAND ...\n"
                 "\"%s help\" for a list of available commands\n"