Fossil

Check-in [576425e8]
Login

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

Overview
Comment:More work on the warnings infrastructure. json_warn() now (experimentally) disallows (elides) duplicate warning codes to simplify downstream loops. Still undecided on that behaviour, though.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1:576425e862f08acf94e6e006832d89543e669b93
User & Date: stephan 2011-09-21 17:38:36
Context
2011-09-21
18:10
Minor timeline output cleanups. Added /json/branch/list. check-in: f266ebdd user: stephan tags: json
17:38
More work on the warnings infrastructure. json_warn() now (experimentally) disallows (elides) duplicate warning codes to simplify downstream loops. Still undecided on that behaviour, though. check-in: 576425e8 user: stephan tags: json
16:31
started adding infrastructure to report non-fatal warnings. check-in: ad50fe95 user: stephan tags: json
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/json.c.

79
80
81
82
83
84
85












86
87
88
89
90
91
92
...
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
490
...
494
495
496
497
498
499
500

501
502
503
504
505
506
507
...
551
552
553
554
555
556
557








558
559
560
561
562
563
564
...
565
566
567
568
569
570
571


























572
573
574
575
576
577
578
...
797
798
799
800
801
802
803
804
805



806
807
808

809
810
811
812
813
814
815
816
....
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611

1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622


1623


1624
1625


1626
1627










1628
1629
1630
1631
1632
1633
1634
....
1785
1786
1787
1788
1789
1790
1791
1792

1793
1794
1795
1796
1797
1798
1799
....
1819
1820
1821
1822
1823
1824
1825






** stdout, as that will effectively corrupt any JSON output, and
** almost certainly will corrupt any HTTP response headers. Output
** sent to stderr ends up in my apache log, so that might be useful
** for debuggering in some cases, but so such code should be left
** enabled for non-debuggering builds.
*/
typedef cson_value * (*fossil_json_f)(unsigned int depth);













/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.
*/
static cson_value * json_page_nyi(unsigned int depth){
  g.json.resultCode = FSL_JSON_E_NYI;
................................................................................
  v = cson_value_new_object();
  g.json.param.v = v;
  g.json.param.o = cson_value_get_object(v);
  json_gc_add("$PARAMS", v, 1);
}

/*
** Appends a warning object to the response.
**
** TODO: specify what the code must be.
**
** A Warning object has this JSON structure:
**
** { "code":integer, "text":"string" }
**
** But the text part is optional.
**





** If msg is non-NULL and not empty then it is used
** as the "text" property's value.









*/
void json_add_warning( int code, char const * msg ){
  cson_value * objV = NULL;
  cson_object * obj = NULL;






  if(!g.json.warnings.v){
    g.json.warnings.v = cson_value_new_array();
    assert((NULL != g.json.warnings.v) && "Alloc error.");
    g.json.warnings.a = cson_value_get_array(g.json.warnings.v);
    json_gc_add("$WARNINGS",g.json.warnings.v,0);
  }
  objV = cson_value_new_object();
................................................................................
  cson_object_set(obj,"code",cson_value_new_integer(code));
  if(msg && *msg){
    /* FIXME: treat NULL msg as standard warning message for
       the code, but we don't have those yet.
    */
    cson_object_set(obj,"text",cson_value_new_string(msg,strlen(msg)));
  }

}

/*
** Splits zStr (which must not be NULL) into tokens separated by the
** given separator character. If doDeHttp is true then each element
** will be passed through dehttpize(), otherwise they are used
** as-is. Each new element is appended to the given target array
................................................................................
          }else{
            cson_value_free(part);
            rc = rc ? -rc : 0;
            break;
          }
        }else{
          assert(0 && "i didn't think this was possible!");








        }
        free(zPart);
        len = 0;
      }
      if( !*p ){
        break;
      }
................................................................................
      head = p+1;
      continue;
    }
    ++len;
  }
  return rc;
}



























/*
** Performs some common initialization of JSON-related state.  Must be
** called by the json_page_top() and json_cmd_top() dispatching
** functions to set up the JSON stat used by the dispatched functions.
**
** Implicitly sets up the login information state in CGI mode, but
................................................................................
}

/*
** Given a Fossil/JSON result code, this function "dumbs it down"
** according to the current value of g.json.errorDetailParanoia. The
** dumbed-down value is returned.
**
** This function assert()s that code is either 0
** or between the range of 1000 and 9999.



*/
static int json_dumbdown_rc( int code ){
  if( !code ){

    return 0;
  }else{
    int modulo = 0;
    assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
    switch( g.json.errorDetailParanoia ){
      case 1: modulo = 10; break;
      case 2: modulo = 100; break;
      case 3: modulo = 1000; break;
................................................................................
                                 a JSON array*/
              " tagId AS tagId,"
              " brief AS briefText"
              " FROM json_timeline"
              " ORDER BY mtime DESC",
              -1);
  db_prepare(&q,blob_buffer(&sql));
  tmp = NULL;
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  tmp = listV;
  SET("timeline");
  while( (SQLITE_ROW == db_step(&q) )){
    /* convert each row into a JSON object...*/
    cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt);
    cson_object * row = cson_value_get_object(rowV);
    cson_string const * tagsStr = NULL;
    if(!row){
      /* need a way of warning about this */

      continue;
    }

    /* Split tags string field into JSON Array... */
    cson_array_append(list, rowV);
    tagsStr = cson_value_get_string(cson_object_get(row,"tags"));
    if(tagsStr){
      cson_value * tagsV = cson_value_new_array();
      cson_array * tags = cson_value_get_array(tagsV);
      if( 0 < json_string_split( cson_string_cstr(tagsStr), ',', 0, tags)){
        cson_object_set(row,"tags",tagsV)


          /*replaces/deletes old tags value, invalidating tagsStr!*/;


      }else{
        cson_value_free(tagsV);


      }
    }










  }
  db_finalize(&q);
#undef SET
  goto ok;
  error:
  cson_value_free(payV);
  payV = NULL;
................................................................................
  json_main_bootstrap();
  json_mode_bootstrap();
  if( g.argc<3 ){
    goto usage;
  }
  db_find_and_open_repository(0, 0);
#if 0
  json_add_warning(-1, "Just testing.");

#endif
  cmd = json_command_arg(1);
  if( !cmd || !*cmd ){
    goto usage;
  }
  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
................................................................................
      fossil_exit(1);
    }
  }
  return;
  usage:
  usage("subcommand");
}













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







 







|

|







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

|


>
>
>
>
>
>







 







>







 







>
>
>
>
>
>
>
>







 







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







 







|
|
>
>
>


<
>
|







 







<










|
>


<




|
|
|
|
>
>
|
>
>

<
>
>


>
>
>
>
>
>
>
>
>
>







 







|
>







 







>
>
>
>
>
>
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
...
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
...
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
...
864
865
866
867
868
869
870
871
872
873
874
875
876
877

878
879
880
881
882
883
884
885
886
....
1663
1664
1665
1666
1667
1668
1669

1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683

1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697

1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
....
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
....
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
** stdout, as that will effectively corrupt any JSON output, and
** almost certainly will corrupt any HTTP response headers. Output
** sent to stderr ends up in my apache log, so that might be useful
** for debuggering in some cases, but so such code should be left
** enabled for non-debuggering builds.
*/
typedef cson_value * (*fossil_json_f)(unsigned int depth);

/*
** Internal helpers to manipulate a byte array as a bitset. The B
** argument must be-a array at least (BIT/8+1) bytes long.
** The BIT argument is the bit number to query/set/clear/toggle.
*/
#define BITSET_BYTEFOR(B,BIT) ((B)[ BIT / 8 ])
#define BITSET_SET(B,BIT) ((BITSET_BYTEFOR(B,BIT) |= (0x01 << (BIT%8))),0x01)
#define BITSET_UNSET(B,BIT) ((BITSET_BYTEFOR(B,BIT) &= ~(0x01 << (BIT%8))),0x00)
#define BITSET_GET(B,BIT) ((BITSET_BYTEFOR(B,BIT) & (0x01 << (BIT%8))) ? 0x01 : 0x00)
#define BITSET_TOGGLE(B,BIT) (BITSET_GET(B,BIT) ? (BITSET_UNSET(B,BIT)) : (BITSET_SET(B,BIT)))


/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.
*/
static cson_value * json_page_nyi(unsigned int depth){
  g.json.resultCode = FSL_JSON_E_NYI;
................................................................................
  v = cson_value_new_object();
  g.json.param.v = v;
  g.json.param.o = cson_value_get_object(v);
  json_gc_add("$PARAMS", v, 1);
}

/*
** Appends a warning object to the (pending) JSON response.
**
** Code must be a FSL_JSON_W_xxx value from the FossilJsonCodes enum.
**
** A Warning object has this JSON structure:
**
** { "code":integer, "text":"string" }
**
** But the text part is optional.
**
** FIXME FIXME FIXME: i am EXPERIMENTALLY using integer codes instead
** of FOSSIL-XXXX codes here. i may end up switching FOSSIL-XXXX
** string-form codes to integers. Let's ask the mailing list for
** opinions...
**
** If msg is non-NULL and not empty then it is used as the "text"
** property's value. It is copied, and need not refer to static
** memory.
**
** CURRENTLY this code only allows a given warning code to be
** added one time, and elides subsequent warnings. The intention
** is to remove that burden from loops which produce warnings.
**
** FIXME: if msg is NULL then use a standard string for
** the given code. If !*msg then elide the "text" property,
** for consistency with how json_err() works.
*/
void json_warn( int code, char const * msg ){
  cson_value * objV = NULL;
  cson_object * obj = NULL;
  assert( (code>FSL_JSON_W_START)
          && (code<FSL_JSON_W_END)
          && "Invalid warning code.");
  if( BITSET_GET(g.json.warnings.bitset,code) ){
    return;
  }
  if(!g.json.warnings.v){
    g.json.warnings.v = cson_value_new_array();
    assert((NULL != g.json.warnings.v) && "Alloc error.");
    g.json.warnings.a = cson_value_get_array(g.json.warnings.v);
    json_gc_add("$WARNINGS",g.json.warnings.v,0);
  }
  objV = cson_value_new_object();
................................................................................
  cson_object_set(obj,"code",cson_value_new_integer(code));
  if(msg && *msg){
    /* FIXME: treat NULL msg as standard warning message for
       the code, but we don't have those yet.
    */
    cson_object_set(obj,"text",cson_value_new_string(msg,strlen(msg)));
  }
  BITSET_SET(g.json.warnings.bitset,code);
}

/*
** Splits zStr (which must not be NULL) into tokens separated by the
** given separator character. If doDeHttp is true then each element
** will be passed through dehttpize(), otherwise they are used
** as-is. Each new element is appended to the given target array
................................................................................
          }else{
            cson_value_free(part);
            rc = rc ? -rc : 0;
            break;
          }
        }else{
          assert(0 && "i didn't think this was possible!");
          fprintf(stderr,"%s:%d: My God! It's full of stars!\n",
                  __FILE__, __LINE__);
          fossil_exit(1)
            /* Not fossil_panic() b/c this code needs to be able to
              run before some of the fossil/json bits are initialized,
              and fossil_panic() calls into the JSON API.
            */
            ;
        }
        free(zPart);
        len = 0;
      }
      if( !*p ){
        break;
      }
................................................................................
      head = p+1;
      continue;
    }
    ++len;
  }
  return rc;
}

/*
** Wrapper around json_string_split(), taking the same first 3
** parameters as this function, but returns the results as
** a JSON Array (if splitting produced tokens)
** OR a JSON null value (if splitting produced no tokens)
** OR NULL (if splitting failed in any way).
**
** The returned value is owned by the caller. If not NULL then it
** _will_ have a JSON type of Array or Null.
*/
cson_value * json_string_split2( char const * zStr,
                                 char separator,
                                 char doDeHttp ){
  cson_value * v = cson_value_new_array();
  cson_array * a = cson_value_get_array(v);
  int rc = json_string_split( zStr, separator, doDeHttp, a );
  if( 0 == rc ){
    cson_value_free(v);
    v = cson_value_null();
  }else if(rc<0){
    cson_value_free(v);
    v = NULL;
  }
  return v;
}

/*
** Performs some common initialization of JSON-related state.  Must be
** called by the json_page_top() and json_cmd_top() dispatching
** functions to set up the JSON stat used by the dispatched functions.
**
** Implicitly sets up the login information state in CGI mode, but
................................................................................
}

/*
** Given a Fossil/JSON result code, this function "dumbs it down"
** according to the current value of g.json.errorDetailParanoia. The
** dumbed-down value is returned.
**
** This function assert()s that code is in the inclusive range 0 to
** 9999.
**
** Note that WARNING codes (1..999) are never dumbed down.
**
*/
static int json_dumbdown_rc( int code ){

  if(!code || ((code>FSL_JSON_W_START) && (code>FSL_JSON_W_END))){
    return code;
  }else{
    int modulo = 0;
    assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
    switch( g.json.errorDetailParanoia ){
      case 1: modulo = 10; break;
      case 2: modulo = 100; break;
      case 3: modulo = 1000; break;
................................................................................
                                 a JSON array*/
              " tagId AS tagId,"
              " brief AS briefText"
              " FROM json_timeline"
              " ORDER BY mtime DESC",
              -1);
  db_prepare(&q,blob_buffer(&sql));

  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  tmp = listV;
  SET("timeline");
  while( (SQLITE_ROW == db_step(&q) )){
    /* convert each row into a JSON object...*/
    cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt);
    cson_object * row = cson_value_get_object(rowV);
    cson_string const * tagsStr = NULL;
    if(!row){
      json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
                 "Could not convert at least one timeline result row to JSON." );
      continue;
    }

    /* Split tags string field into JSON Array... */
    cson_array_append(list, rowV);
    tagsStr = cson_value_get_string(cson_object_get(row,"tags"));
    if(tagsStr){
      cson_value * tags = json_string_split2( cson_string_cstr(tagsStr),
                                              ',', 0);
      if( tags ){
        if(0 != cson_object_set(row,"tags",tags)){
          cson_value_free(tags);
        }else{
          /*replaced/deleted old tags value, invalidating tagsStr*/;
          tagsStr = NULL;
        }
      }else{

        json_warn(FSL_JSON_W_STRING_TO_ARRAY_FAILED,
                  "Could not convert tags string to array.");
      }
    }

    /* replace isLeaf int w/ JSON bool */
    tmp = cson_object_get(row,"isLeaf");
    if(tmp && cson_value_is_integer(tmp)){
      cson_object_set(row,"isLeaf",
                      cson_value_get_integer(tmp)
                      ? cson_value_true()
                      : cson_value_false());
      tmp = NULL;
    }
  }
  db_finalize(&q);
#undef SET
  goto ok;
  error:
  cson_value_free(payV);
  payV = NULL;
................................................................................
  json_main_bootstrap();
  json_mode_bootstrap();
  if( g.argc<3 ){
    goto usage;
  }
  db_find_and_open_repository(0, 0);
#if 0
  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing.");
  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again.");
#endif
  cmd = json_command_arg(1);
  if( !cmd || !*cmd ){
    goto usage;
  }
  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
................................................................................
      fossil_exit(1);
    }
  }
  return;
  usage:
  usage("subcommand");
}

#undef BITSET_BYTEFOR
#undef BITSET_SET
#undef BITSET_UNSET
#undef BITSET_GET
#undef BITSET_TOGGLE

Changes to src/json_detail.h.

1
2
3
4
5
6
7
8
9
10

11

12
13
14
15

16
17
18



19

20
21
22
23
24
25
26
..
47
48
49
50
51
52
53

54
55
/*
** Impl details for the JSON API which need to be shared
** across multiple C files.
*/

/*
** The "official" list of Fossil/JSON error codes.
** Their values might very well change during initial
** development but after their first public release
** they must stay stable.

** Values must be in the range 1..9999

**
** Numbers evenly dividable by 100 are "categories",
** and error codes for a given category have their
** high bits set to the category value.

**
*/
enum FossilJsonCodes {





FSL_JSON_E_GENERIC = 1000,
FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100,
FSL_JSON_E_INVALID_REQUEST = FSL_JSON_E_GENERIC_SUB1 + 1,
FSL_JSON_E_UNKNOWN_COMMAND = FSL_JSON_E_GENERIC_SUB1 + 2,
FSL_JSON_E_UNKNOWN = FSL_JSON_E_GENERIC_SUB1 + 3,
FSL_JSON_E_RESOURCE_NOT_FOUND = FSL_JSON_E_GENERIC_SUB1 + 4,
FSL_JSON_E_TIMEOUT = FSL_JSON_E_GENERIC_SUB1 + 5,
................................................................................
FSL_JSON_E_DB = 4000,
FSL_JSON_E_STMT_PREP = FSL_JSON_E_DB + 1,
FSL_JSON_E_STMT_BIND = FSL_JSON_E_DB + 2,
FSL_JSON_E_STMT_EXEC = FSL_JSON_E_DB + 3,
FSL_JSON_E_DB_LOCKED = FSL_JSON_E_DB + 4,

FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101

};







|
|
<
|
>
|
>

|
|
<
>



>
>
>

>







 







>


1
2
3
4
5
6
7
8

9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
52
53
54
55
56
57
58
59
60
61
/*
** Impl details for the JSON API which need to be shared
** across multiple C files.
*/

/*
** The "official" list of Fossil/JSON error codes.  Their values might
** very well change during initial development but after their first

** public release they must stay stable.
**
** Values must be in the range 1000..9999 for error codes and 1..999
** for warning codes.
**
** Numbers evenly dividable by 100 are "categories", and error codes
** for a given category have their high bits set to the category

** value.
**
*/
enum FossilJsonCodes {
FSL_JSON_W_START = 0,
FSL_JSON_W_ROW_TO_JSON_FAILED = FSL_JSON_W_START + 1,
FSL_JSON_W_STRING_TO_ARRAY_FAILED = FSL_JSON_W_START + 2,

FSL_JSON_W_END = 1000,
FSL_JSON_E_GENERIC = 1000,
FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100,
FSL_JSON_E_INVALID_REQUEST = FSL_JSON_E_GENERIC_SUB1 + 1,
FSL_JSON_E_UNKNOWN_COMMAND = FSL_JSON_E_GENERIC_SUB1 + 2,
FSL_JSON_E_UNKNOWN = FSL_JSON_E_GENERIC_SUB1 + 3,
FSL_JSON_E_RESOURCE_NOT_FOUND = FSL_JSON_E_GENERIC_SUB1 + 4,
FSL_JSON_E_TIMEOUT = FSL_JSON_E_GENERIC_SUB1 + 5,
................................................................................
FSL_JSON_E_DB = 4000,
FSL_JSON_E_STMT_PREP = FSL_JSON_E_DB + 1,
FSL_JSON_E_STMT_BIND = FSL_JSON_E_DB + 2,
FSL_JSON_E_STMT_EXEC = FSL_JSON_E_DB + 3,
FSL_JSON_E_DB_LOCKED = FSL_JSON_E_DB + 4,

FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101

};

Changes to src/main.c.

25
26
27
28
29
30
31

32
33
34
35
36
37
38
...
208
209
210
211
212
213
214






215
216
217
218
219
220
221
...
474
475
476
477
478
479
480

481
482
483
484
485





486
487
488
489
490
491
492

493
494
495
496
497
498
499
500
501
502
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h> /* atexit() */

#if INTERFACE
#include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */


/*
** Number of elements in an array
*/
#define count(X)  (sizeof(X)/sizeof(X[0]))

/*
................................................................................
    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */
    struct {                   /* response warnings */
      cson_value * v;
      cson_array * a;






    } warnings;
  } json;
};

/*
** Macro for debugging:
*/
................................................................................
** fossil_fatal() which never return, this routine might return if
** the fatal error handing is already in process.  The caller must
** be prepared for this routine to return.
*/
void fossil_fatal_recursive(const char *zFormat, ...){
  char *z;
  va_list ap;

  if( mainInFatalError ) return;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);





  if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();
  }else{
    char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
    fossil_puts(zOut, 1);

  }
  db_force_rollback();
  fossil_exit(1);
}


/* Print a warning message */
void fossil_warning(const char *zFormat, ...){
  char *z;
  va_list ap;







>







 







>
>
>
>
>
>







 







>





>
>
>
>
>
|






>


|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
...
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h> /* atexit() */

#if INTERFACE
#include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */
#include "json_detail.h"

/*
** Number of elements in an array
*/
#define count(X)  (sizeof(X)/sizeof(X[0]))

/*
................................................................................
    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */
    struct {                   /* response warnings */
      cson_value * v;
      cson_array * a;
      int bitset[FSL_JSON_W_END/8/sizeof(int)+1]
      /* allows json_add_warning() to know if a given warning
         has been set or not (we don't produce dupes, to simplify
         downstream loop logic).
      */
      ;
    } warnings;
  } json;
};

/*
** Macro for debugging:
*/
................................................................................
** fossil_fatal() which never return, this routine might return if
** the fatal error handing is already in process.  The caller must
** be prepared for this routine to return.
*/
void fossil_fatal_recursive(const char *zFormat, ...){
  char *z;
  va_list ap;
  int rc = 1;
  if( mainInFatalError ) return;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.json.isJsonMode ){
    json_err( g.json.resultCode, z, 1 );
    if( g.isHTTP ){
      rc = 0 /* avoid HTTP 500 */;
    }
  }else if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();
  }else{
    char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
    fossil_puts(zOut, 1);
    free(zOut);
  }
  db_force_rollback();
  fossil_exit(rc);
}


/* Print a warning message */
void fossil_warning(const char *zFormat, ...){
  char *z;
  va_list ap;