ADDED ajax/README
Index: ajax/README
==================================================================
--- /dev/null
+++ ajax/README
@@ -0,0 +1,38 @@
+This is the README for how to set up the Fossil/JSON test web page
+under Apache on Unix systems. This is only intended only for
+Fossil/JSON developers/tinkerers:
+
+First, copy cgi-bin/fossil-json.cgi.example to
+cgi-bin/fossil-json.cgi. Edit it and correct the paths to the fossil
+binary and the repo you want to serve. Make it executable.
+
+MAKE SURE that the fossil repo you use is world-writable OR that your
+Web/CGI server is set up to run as the user ID of the owner of the
+fossil file. ALSO: the DIRECTORY CONTAINING the repo file must be
+writable by the CGI process.
+
+Next, set up an apache vhost entry. Mine looks like:
+
+
| POST data | +Request AJAJ options | +
|---|---|
| + + + | ++ + + | +
| Response | +|
| + + | +|
| Page List | +Content | +
|---|---|
|
+ + + |
+
+ + + |
+
| Response | +|
| + + | +|
+ It is allowed, not required. It is allowed so that JSON can be + safely embedded in HTML, which can freak out when seeing + strings containing "". JSON tolerates "<\/" for this reason. ++ + (from an email on 2011-04-08) + + The default value is 0 (because it's just damned ugly). + */ + char escapeForwardSlashes; +}; +typedef struct cson_output_opt cson_output_opt; + +/** + Empty-initialized cson_output_opt object. +*/ +#define cson_output_opt_empty_m { 0/*indentation*/,\ + 25/*maxDepth*/, \ + 0/*addNewline*/, \ + 0/*addSpaceAfterColon*/, \ + 0/*indentSingleMemberValues*/, \ + 0/*escapeForwardSlashes*/ \ + } + +/** + Empty-initialized cson_output_opt object. +*/ +extern const cson_output_opt cson_output_opt_empty; + +/** + Typedef for functions which act as an input source for + the cson JSON parser. + + The arguments are: + + - state: implementation-specific state needed by the function. + + - n: when called, *n will be the number of bytes the function + should read and copy to dest. The function MUST NOT copy more than + *n bytes to dest. Before returning, *n must be set to the number of + bytes actually copied to dest. If that number is smaller than the + original *n value, the input is assumed to be completed (thus this + is not useful with non-blocking readers). + + - dest: the destination memory to copy the data do. + + Must return 0 on success, non-0 on error (preferably a value from + cson_rc). + + The parser allows this routine to return a partial character from a + UTF multi-byte character. The input routine does not need to + concern itself with character boundaries. +*/ +typedef int (*cson_data_source_f)( void * state, void * dest, unsigned int * n ); + +/** + Typedef for functions which act as an output destination for + generated JSON. + + The arguments are: + + - state: implementation-specific state needed by the function. + + - n: the length, in bytes, of src. + + - src: the source bytes which the output function should consume. + The src pointer will be invalidated shortly after this function + returns, so the implementation must copy or ignore the data, but not + hold a copy of the src pointer. + + Must return 0 on success, non-0 on error (preferably a value from + cson_rc). + + These functions are called relatively often during the JSON-output + process, and should try to be fast. +*/ +typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n ); + +/** + Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the + src function to fetch all input. This function fetches each input character + from the source function, which is calls like src(srcState, buffer, bufferSize), + and processes them. If anything is not JSON-kosher then this function + fails and returns one of the non-0 cson_rc codes. + + This function is only intended to read root nodes of a JSON tree, either + a single object or a single array, containing any number of child elements. + + On success, *tgt is assigned the value of the root node of the + JSON input, and the caller takes over ownership of that memory. + On error, *tgt is not modified and the caller need not do any + special cleanup, except possibly for the input source. + + + The opt argument may point to an initialized cson_parse_opt object + which contains any settings the caller wants. If it is NULL then + default settings (the values defined in cson_parse_opt_empty) are + used. + + The info argument may be NULL. If it is not NULL then the parser + populates it with information which is useful in error + reporting. Namely, it contains the line/column of parse errors. + + The srcState argument is ignored by this function but is passed on to src, + so any output-destination-specific state can be stored there and accessed + via the src callback. + + Non-parse error conditions include: + + - (!tgt) or !src: cson_rc.ArgError + - cson_rc.AllocError can happen at any time during the input phase + + Here's a complete example of using a custom input source: + + @code + // Internal type to hold state for a JSON input string. + typedef struct + { + char const * str; // start of input string + char const * pos; // current internal cursor position + char const * end; // logical EOF (one-past-the-end) + } StringSource; + + // cson_data_source_f() impl which uses StringSource. + static int cson_data_source_StringSource( void * state, void * dest, + unsigned int * n ) + { + StringSource * ss = (StringSource*) state; + unsigned int i; + unsigned char * tgt = (unsigned char *)dest; + if( ! ss || ! n || !dest ) return cson_rc.ArgError; + else if( !*n ) return cson_rc.RangeError; + for( i = 0; + (i < *n) && (ss->pos < ss->end); + ++i, ++ss->pos, ++tgt ) + { + *tgt = *ss->pos; + } + *n = i; + return 0; + } + + ... + // Now use StringSource together with cson_parse() + StringSource ss; + cson_value * root = NULL; + char const * json = "{\"k1\":123}"; + ss.str = ss.pos = json; + ss.end = json + strlen(json); + int rc = cson_parse( &root, cson_data_source_StringSource, &ss, NULL, NULL ); + @endcode + + It is recommended that clients wrap such utility code into + type-safe wrapper functions which also initialize the internal + state object and check the user-provided parameters for legality + before passing them on to cson_parse(). For examples of this, see + cson_parse_FILE() or cson_parse_string(). + + TODOs: + + - Buffer the input in larger chunks. We currently read + byte-by-byte, but i'm too tired to write/test the looping code for + the buffering. + + @see cson_parse_FILE() + @see cson_parse_string() +*/ +int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState, + cson_parse_opt const * opt, cson_parse_info * info ); +/** + A cson_data_source_f() implementation which requires the state argument + to be a readable (FILE*) handle. +*/ +int cson_data_source_FILE( void * state, void * dest, unsigned int * n ); + +/** + Equivalent to cson_parse( tgt, cson_data_source_FILE, src, opt ). + + @see cson_parse_filename() +*/ +int cson_parse_FILE( cson_value ** tgt, FILE * src, + cson_parse_opt const * opt, cson_parse_info * info ); + +/** + Convenience wrapper around cson_parse_FILE() which opens the given filename. + + Returns cson_rc.IOError if the file cannot be opened. + + @see cson_parse_FILE() +*/ +int cson_parse_filename( cson_value ** tgt, char const * src, + cson_parse_opt const * opt, cson_parse_info * info ); + +/** + Uses an internal helper class to pass src through cson_parse(). + See that function for the return value and argument semantics. + + src must be a string containing JSON code, at least len bytes long, + and the parser will attempt to parse exactly len bytes from src. + + If len is less than 2 (the minimum length of a legal top-node JSON + object) then cson_rc.RangeError is returned. +*/ +int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len, + cson_parse_opt const * opt, cson_parse_info * info ); + + + +/** + Outputs the given value as a JSON-formatted string, sending all + output to the given callback function. It is intended for top-level + objects or arrays, but can be used with any cson_value. + + If opt is NULL then default options (the values defined in + cson_output_opt_empty) are used. + + If opt->maxDepth is exceeded while traversing the value tree, + cson_rc.RangeError is returned. + + The destState parameter is ignored by this function and is passed + on to the dest function. + + Returns 0 on success. On error, any amount of output might have been + generated before the error was triggered. + + Example: + + @code + int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL ); + // basically equivalent to: cson_output_FILE( myValue, stdout, NULL ); + // but note that cson_output_FILE() actually uses different defaults + // for the output options. + @endcode +*/ +int cson_output( cson_value const * src, cson_data_dest_f dest, void * destState, cson_output_opt const * opt ); + + +/** + A cson_data_dest_f() implementation which requires the state argument + to be a writable (FILE*) handle. +*/ +int cson_data_dest_FILE( void * state, void const * src, unsigned int n ); + +/** + Almost equivalent to cson_output( src, cson_data_dest_FILE, dest, opt ), + with one minor difference: if opt is NULL then the default options + always include the addNewline option, since that is normally desired + for FILE output. + + @see cson_output_filename() +*/ +int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * opt ); +/** + Convenience wrapper around cson_output_FILE() which writes to the given filename, destroying + any existing contents. Returns cson_rc.IOError if the file cannot be opened. + + @see cson_output_FILE() +*/ +int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt ); + +/** Returns true if v is null, v->api is NULL, or v holds the special undefined value. */ +char cson_value_is_undef( cson_value const * v ); +/** Returns true if v contains a null value. */ +char cson_value_is_null( cson_value const * v ); +/** Returns true if v contains a bool value. */ +char cson_value_is_bool( cson_value const * v ); +/** Returns true if v contains an integer value. */ +char cson_value_is_integer( cson_value const * v ); +/** Returns true if v contains a double value. */ +char cson_value_is_double( cson_value const * v ); +/** Returns true if v contains a number (double, integer) value. */ +char cson_value_is_number( cson_value const * v ); +/** Returns true if v contains a string value. */ +char cson_value_is_string( cson_value const * v ); +/** Returns true if v contains an array value. */ +char cson_value_is_array( cson_value const * v ); +/** Returns true if v contains an object value. */ +char cson_value_is_object( cson_value const * v ); + +/** @struct cson_object + + cson_object is an opaque handle to an Object value. + + They are used like: + + @code + cson_object * obj = cson_value_get_object(myValue); + ... + @endcode + + They can be created like: + + @code + cson_value * objV = cson_value_new_object(); + cson_object * obj = cson_value_get_object(objV); + // obj is owned by objV and objV must eventually be freed + // using cson_value_free() or added to a container + // object/array (which transfers ownership to that container). + @endcode + + @see cson_value_new_object() + @see cson_value_get_object() + @see cson_value_free() +*/ + +typedef struct cson_object cson_object; + +/** @struct cson_array + + cson_array is an opaque handle to an Array value. + + They are used like: + + @code + cson_array * obj = cson_value_get_array(myValue); + ... + @endcode + + They can be created like: + + @code + cson_value * arV = cson_value_new_array(); + cson_array * ar = cson_value_get_array(arV); + // ar is owned by arV and arV must eventually be freed + // using cson_value_free() or added to a container + // object/array (which transfers ownership to that container). + @endcode + + @see cson_value_new_array() + @see cson_value_get_array() + @see cson_value_free() + +*/ +typedef struct cson_array cson_array; + +/** @struct cson_string + + cson-internal string type, opaque to client code. Strings in cson + are immutable and allocated only by library internals, never + directly by client code. + + The actual string bytes are to be allocated together in the same + memory chunk as the cson_string object, which saves us 1 malloc() + and 1 pointer member in this type (because we no longer have a + direct pointer to the memory). + + Potential TODOs: + + @see cson_string_cstr() +*/ +typedef struct cson_string cson_string; + +/** + Converts the given value to a boolean, using JavaScript semantics depending + on the concrete type of val: + + undef or null: false + + boolean: same + + integer, double: 0 or 0.0 == false, else true + + object, array: true + + Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1. + On error (val is NULL) then v is not modified. +*/ +int cson_value_fetch_bool( cson_value const * val, char * v ); +/** + Similar to cson_value_fetch_bool(), but fetches an integer value. + + The conversion, if any, depends on the concrete type of val: + + NULL, null, undefined: *v is set to 0 and 0 is returned. + + string, object, array: *v is set to 0 and + cson_rc.TypeError is returned. The error may normally be safely + ignored, but it is provided for those wanted to know whether a direct + conversion was possible. + + integer: *v is set to the int value and 0 is returned. + + double: *v is set to the value truncated to int and 0 is returned. +*/ +int cson_value_fetch_integer( cson_value const * val, cson_int_t * v ); +/** + The same conversions and return values as + cson_value_fetch_integer(), except that the roles of int/double are + swapped. +*/ +int cson_value_fetch_double( cson_value const * val, cson_double_t * v ); + +/** + If cson_value_is_string(val) then this function assigns *str to the + contents of the string. str may be NULL, in which case this function + functions like cson_value_is_string() but returns 0 on success. + + Returns 0 if val is-a string, else non-0, in which case *str is not + modified. + + The bytes are owned by the given value and may be invalidated in any of + the following ways: + + - The value is cleaned up or freed. + + - An array or object containing the value peforms a re-allocation + (it shrinks or grows). + + And thus the bytes should be consumed before any further operations + on val or any container which holds it. + + Note that this routine does not convert non-String values to their + string representations. (Adding that ability would add more + overhead to every cson_value instance.) +*/ +int cson_value_fetch_string( cson_value const * val, cson_string const ** str ); + +/** + If cson_value_is_object(val) then this function assigns *obj to the underlying + object value and returns 0, otherwise non-0 is returned and *obj is not modified. + + obj may be NULL, in which case this function works like cson_value_is_object() + but with inverse return value semantics (0==success) (and it's a few + CPU cycles slower). + + The *obj pointer is owned by val, and will be invalidated when val + is cleaned up. + + Achtung: for best results, ALWAYS pass a pointer to NULL as the + second argument, e.g.: + + @code + cson_object * obj = NULL; + int rc = cson_value_fetch_object( val, &obj ); + + // Or, more simply: + obj = cson_value_get_object( val ); + @endcode + + @see cson_value_get_object() +*/ +int cson_value_fetch_object( cson_value const * val, cson_object ** obj ); + +/** + Identical to cson_value_fetch_object(), but works on array values. + + @see cson_value_get_array() +*/ +int cson_value_fetch_array( cson_value const * val, cson_array ** tgt ); + +/** + Simplified form of cson_value_fetch_bool(). Returns 0 if val + is NULL. +*/ +char cson_value_get_bool( cson_value const * val ); + +/** + Simplified form of cson_value_fetch_integer(). Returns 0 if val + is NULL. +*/ +cson_int_t cson_value_get_integer( cson_value const * val ); + +/** + Simplified form of cson_value_fetch_double(). Returns 0.0 if val + is NULL. +*/ +cson_double_t cson_value_get_double( cson_value const * val ); + +/** + Simplified form of cson_value_fetch_string(). Returns NULL if val + is-not-a string value. +*/ +cson_string const * cson_value_get_string( cson_value const * val ); + +/** + Returns a pointer to the NULL-terminated string bytes of str. + The bytes are owned by string and will be invalided when it + is cleaned up. + + If str is NULL then NULL is returned. + + @see cson_string_length_bytes() + @see cson_value_get_string() +*/ +char const * cson_string_cstr( cson_string const * str ); + +/** + Convenience function which returns the string bytes of + the given value if it is-a string, otherwise it returns + NULL. Note that this does no conversion of non-string types + to strings. + + Equivalent to cson_string_cstr(cson_value_get_string(val)). +*/ +char const * cson_value_get_cstr( cson_value const * val ); + +/** + Equivalent to cson_string_cmp_cstr_n(lhs, cson_string_cstr(rhs), cson_string_length_bytes(rhs)). +*/ +int cson_string_cmp( cson_string const * lhs, cson_string const * rhs ); + +/** + Compares lhs to rhs using memcmp()/strcmp() semantics. Generically + speaking it returns a negative number if lhs is less-than rhs, 0 if + they are equivalent, or a positive number if lhs is greater-than + rhs. It has the following rules for equivalence: + + - The maximum number of bytes compared is the lesser of rhsLen and + the length of lhs. If the strings do not match, but compare equal + up to the just-described comparison length, the shorter string is + considered to be less-than the longer one. + + - If lhs and rhs are both NULL, or both have a length of 0 then they will + compare equal. + + - If lhs is null/length-0 but rhs is not then lhs is considered to be less-than + rhs. + + - If rhs is null/length-0 but lhs is not then rhs is considered to be less-than + rhs. + + - i have no clue if the results are exactly correct for UTF strings. + +*/ +int cson_string_cmp_cstr_n( cson_string const * lhs, char const * rhs, unsigned int rhsLen ); + +/** + Equivalent to cson_string_cmp_cstr_n( lhs, rhs, (rhs&&*rhs)?strlen(rhs):0 ). +*/ +int cson_string_cmp_cstr( cson_string const * lhs, char const * rhs ); + +/** + Returns the length, in bytes, of str, or 0 if str is NULL. This is + an O(1) operation. + + TODO: add cson_string_length_chars() (is O(N) unless we add another + member to store the char length). + + @see cson_string_cstr() +*/ +unsigned int cson_string_length_bytes( cson_string const * str ); + +/** + Returns the number of UTF8 characters in str. This value will + be at most as long as cson_string_length_bytes() for the + same string, and less if it has multi-byte characters. + + Returns 0 if str is NULL. +*/ +unsigned int cson_string_length_utf8( cson_string const * str ); + +/** + Like cson_value_get_string(), but returns a copy of the underying + string bytes, which the caller owns and must eventually free + using free(). +*/ +char * cson_value_get_string_copy( cson_value const * val ); + +/** + Simplified form of cson_value_fetch_object(). Returns NULL if val + is-not-a object value. +*/ +cson_object * cson_value_get_object( cson_value const * val ); + +/** + Simplified form of cson_value_fetch_array(). Returns NULL if val + is-not-a array value. +*/ +cson_array * cson_value_get_array( cson_value const * val ); + +/** + Const-correct form of cson_value_get_array(). +*/ +cson_array const * cson_value_get_array_c( cson_value const * val ); + +/** + If ar is-a array and is at least (pos+1) entries long then *v (if v is not NULL) + is assigned to the value at that position (which may be NULL). + + Ownership of the *v return value is unchanged by this call. (The + containing array may share ownership of the value with other + containers.) + + If pos is out of range, non-0 is returned and *v is not modified. + + If v is NULL then this function returns 0 if pos is in bounds, but does not + otherwise return a value to the caller. +*/ +int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v ); + +/** + Simplified form of cson_array_value_fetch() which returns NULL if + ar is NULL, pos is out of bounds or if ar has no element at that + position. +*/ +cson_value * cson_array_get( cson_array const * ar, unsigned int pos ); + +/** + Ensures that ar has allocated space for at least the given + number of entries. This never shrinks the array and never + changes its logical size, but may pre-allocate space in the + array for storing new (as-yet-unassigned) values. + + Returns 0 on success, or non-zero on error: + + - If ar is NULL: cson_rc.ArgError + + - If allocation fails: cson_rc.AllocError +*/ +int cson_array_reserve( cson_array * ar, unsigned int size ); + +/** + If ar is not NULL, sets *v (if v is not NULL) to the length of the array + and returns 0. Returns cson_rc.ArgError if ar is NULL. +*/ +int cson_array_length_fetch( cson_array const * ar, unsigned int * v ); + +/** + Simplified form of cson_array_length_fetch() which returns 0 if ar + is NULL. +*/ +unsigned int cson_array_length_get( cson_array const * ar ); + +/** + Sets the given index of the given array to the given value. + + If ar already has an item at that index then it is cleaned up and + freed before inserting the new item. + + ar is expanded, if needed, to be able to hold at least (ndx+1) + items, and any new entries created by that expansion are empty + (NULL values). + + On success, 0 is returned and ownership of v is transfered to ar. + + On error ownership of v is NOT modified, and the caller may still + need to clean it up. For example, the following code will introduce + a leak if this function fails: + + @code + cson_array_append( myArray, cson_value_new_integer(42) ); + @endcode + + Because the value created by cson_value_new_integer() has no owner + and is not cleaned up. The "more correct" way to do this is: + + @code + cson_value * v = cson_value_new_integer(42); + int rc = cson_array_append( myArray, v ); + if( 0 != rc ) { + cson_value_free( v ); + ... handle error ... + } + @endcode + +*/ +int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v ); + +/** + Appends the given value to the given array, transfering ownership of + v to ar. On error, ownership of v is not modified. Ownership of ar + is never changed by this function. + + This is functionally equivalent to + cson_array_set(ar,cson_array_length_get(ar),v), but this + implementation has slightly different array-preallocation policy + (it grows more eagerly). + + Returns 0 on success, non-zero on error. Error cases include: + + - ar or v are NULL: cson_rc.ArgError + + - Array cannot be expanded to hold enough elements: cson_rc.AllocError. + + - Appending would cause a numeric overlow in the array's size: + cson_rc.RangeError. (However, you'll get an AllocError long before + that happens!) + + On error ownership of v is NOT modified, and the caller may still + need to clean it up. See cson_array_set() for the details. + +*/ +int cson_array_append( cson_array * ar, cson_value * v ); + + +/** + Creates a new cson_value from the given boolean value. + + Ownership of the new value is passed to the caller, who must + eventually either free the value using cson_value_free() or + inserting it into a container (array or object), which transfers + ownership to the container. See the cson_value class documentation + for more details. + + Returns NULL on allocation error. +*/ +cson_value * cson_value_new_bool( char v ); + + +/** + Returns the special JSON "null" value. When outputing JSON, + its string representation is "null" (without the quotes). + + See cson_value_new_bool() for notes regarding the returned + value's memory. +*/ +cson_value * cson_value_null(); + +/** + Equivalent to cson_value_new_bool(1). +*/ +cson_value * cson_value_true(); + +/** + Equivalent to cson_value_new_bool(0). +*/ +cson_value * cson_value_false(); + +/** + Semantically the same as cson_value_new_bool(), but for integers. +*/ +cson_value * cson_value_new_integer( cson_int_t v ); + +/** + Semantically the same as cson_value_new_bool(), but for doubles. +*/ +cson_value * cson_value_new_double( cson_double_t v ); + +/** + Semantically the same as cson_value_new_bool(), but for strings. + This creates a JSON value which copies the first n bytes of str. + The string will automatically be NUL-terminated. + + Note that if str is NULL or n is 0, this function still + returns non-NULL value representing that empty string. + + Returns NULL on allocation error. + + See cson_value_new_bool() for important information about the + returned memory. +*/ +cson_value * cson_value_new_string( char const * str, unsigned int n ); + +/** + Allocates a new "object" value and transfers ownership of it to the + caller. It must eventually be destroyed, by the caller or its + owning container, by passing it to cson_value_free(). + + Returns NULL on allocation error. + + Post-conditions: cson_value_is_object(value) will return true. + + @see cson_value_new_array() + @see cson_value_free() +*/ +cson_value * cson_value_new_object(); + +/** + Allocates a new "array" value and transfers ownership of it to the + caller. It must eventually be destroyed, by the caller or its + owning container, by passing it to cson_value_free(). + + Returns NULL on allocation error. + + Post-conditions: cson_value_is_array(value) will return true. + + @see cson_value_new_object() + @see cson_value_free() +*/ +cson_value * cson_value_new_array(); + +/** + Frees any resources owned by v, then frees v. If v is a container + type (object or array) its children are also freed (recursively). + + If v is NULL, this is a no-op. + + This function decrements a reference count and only destroys the + value if its reference count drops to 0. Reference counts are + increased by either inserting the value into a container or via + cson_value_add_reference(). Even if this function does not + immediately destroy the value, the value must be considered, from + the perspective of that client code, to have been + destroyed/invalidated by this call. + + + @see cson_value_new_object() + @see cson_value_new_array() + @see cson_value_add_reference() +*/ +void cson_value_free(cson_value * v); + +/** + Functionally similar to cson_array_set(), but uses a string key + as an index. Like arrays, if a value already exists for the given key, + it is destroyed by this function before inserting the new value. + + If v is NULL then this call is equivalent to + cson_object_unset(obj,key). Note that (v==NULL) is treated + differently from v having the special null value. In the latter + case, the key is set to the special null value. + + The key may be encoded as ASCII or UTF8. Results are undefined + with other encodings, and the errors won't show up here, but may + show up later, e.g. during output. + + Returns 0 on success, non-0 on error. It has the following error + cases: + + - cson_rc.ArgError: obj or key are NULL or strlen(key) is 0. + + - cson_rc.AllocError: an out-of-memory error + + On error ownership of v is NOT modified, and the caller may still + need to clean it up. For example, the following code will introduce + a leak if this function fails: + + @code + cson_object_set( myObj, "foo", cson_value_new_integer(42) ); + @endcode + + Because the value created by cson_value_new_integer() has no owner + and is not cleaned up. The "more correct" way to do this is: + + @code + cson_value * v = cson_value_new_integer(42); + int rc = cson_object_set( myObj, "foo", v ); + if( 0 != rc ) { + cson_value_free( v ); + ... handle error ... + } + @endcode + + Potential TODOs: + + - Add an overload which takes a cson_value key instead. To get + any value out of that we first need to be able to convert arbitrary + value types to strings. We could simply to-JSON them and use those + as keys. +*/ +int cson_object_set( cson_object * obj, char const * key, cson_value * v ); + +/** + Removes a property from an object. + + If obj contains the given key, it is removed and 0 is returned. If + it is not found, cson_rc.NotFoundError is returned (which can + normally be ignored by client code). + + cson_rc.ArgError is returned if obj or key are NULL or key has + a length of 0. + + Returns 0 if the given key is found and removed. + + This is functionally equivalent calling + cson_object_set(obj,key,NULL). +*/ +int cson_object_unset( cson_object * obj, char const * key ); + +/** + Searches the given object for a property with the given key. If found, + it is returned. If no match is found, or any arguments are NULL, NULL is + returned. The returned object is owned by obj, and may be invalidated + by ANY operations which change obj's property list (i.e. add or remove + properties). + + FIXME: allocate the key/value pairs like we do for cson_array, + to get improve the lifetimes of fetched values. + + @see cson_object_fetch_sub() + @see cson_object_get_sub() +*/ +cson_value * cson_object_get( cson_object const * obj, char const * key ); + +/** + Similar to cson_object_get(), but removes the value from the parent + object's ownership. If no item is found then NULL is returned, else + the object (now owned by the caller or possibly shared with other + containers) is returned. + + Returns NULL if either obj or key are NULL or key has a length + of 0. + + This function reduces the returned value's reference count but has + the specific property that it does not treat refcounts 0 and 1 + identically, meaning that the returned object may have a refcount + of 0. This behaviour works around a corner-case where we want to + extract a child element from its parent and then destroy the parent + (which leaves us in an undesireable (normally) reference count + state). +*/ +cson_value * cson_object_take( cson_object * obj, char const * key ); + +/** + Fetches a property from a child (or [great-]*grand-child) object. + + obj is the object to search. + + path is a delimited string, where the delimiter is the given + separator character. + + This function searches for the given path, starting at the given object + and traversing its properties as the path specifies. If a given part of the + path is not found, then this function fails with cson_rc.NotFoundError. + + If it finds the given path, it returns the value by assiging *tgt + to it. If tgt is NULL then this function has no side-effects but + will return 0 if the given path is found within the object, so it can be used + to test for existence without fetching it. + + Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds + no item, and any other non-zero error code on a "real" error. Errors include: + + - obj or path are NULL: cson_rc.ArgError + + - separator is 0, or path is an empty string or contains only + separator characters: cson_rc.RangeError + + - There is an upper limit on how long a single path component may + be (some "reasonable" internal size), and cson_rc.RangeError is + returned if that length is violated. + + + Limitations: + + - It has no way to fetch data from arrays this way. i could + imagine, e.g., a path of "subobj.subArray.0" for + subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too + lazy/tired to add this. + + Example usage: + + + Assume we have a JSON structure which abstractly looks like: + + @code + {"subobj":{"subsubobj":{"myValue":[1,2,3]}}} + @endcode + + Out goal is to get the value of myValue. We can do that with: + + @code + cson_value * v = NULL; + int rc = cson_object_fetch_sub( object, &v, "subobj.subsubobj.myValue", '.' ); + @endcode + + Note that because keys in JSON may legally contain a '.', the + separator must be specified by the caller. e.g. the path + "subobj/subsubobj/myValue" with separator='/' is equivalent the + path "subobj.subsubobj.myValue" with separator='.'. The value of 0 + is not legal as a separator character because we cannot + distinguish that use from the real end-of-string without requiring + the caller to also pass in the length of the string. + + Multiple successive separators in the list are collapsed into a + single separator for parsing purposes. e.g. the path "a...b...c" + (separator='.') is equivalent to "a.b.c". + + @see cson_object_get_sub() +*/ +int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char separator ); + +/** + Convenience form of cson_object_fetch_sub() which returns NULL if the given + item is not found. +*/ +cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep ); + + +/** + An iterator type for traversing object properties. + + Its values must be considered private, not to be touched by client + code. + + @see cson_object_iter_init() + @see cson_object_iter_next() +*/ +struct cson_object_iterator +{ + + /** @internal + The underlying object. + */ + cson_object const * obj; + /** @internal + Current position in the property list. + */ + unsigned int pos; +}; +typedef struct cson_object_iterator cson_object_iterator; + +/** + Empty-initialized cson_object_iterator object. +*/ +#define cson_object_iterator_empty_m {NULL/*obj*/,0/*pos*/} + +/** + Empty-initialized cson_object_iterator object. +*/ +extern const cson_object_iterator cson_object_iterator_empty; + +/** + Initializes the given iterator to point at the start of obj's + properties. Returns 0 on success or cson_rc.ArgError if !obj + or !iter. + + obj must outlive iter, or results are undefined. Results are also + undefined if obj is modified while the iterator is active. + + @see cson_object_iter_next() +*/ +int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter ); + +/** @struct cson_kvp + +This class represents a key/value pair and is used for storing +object properties. It is opaque to client code, and the public +API only uses this type for purposes of iterating over cson_object +properties using the cson_object_iterator interfaces. +*/ + +typedef struct cson_kvp cson_kvp; + +/** + Returns the next property from the given iterator's object, or NULL + if the end of the property list as been reached. + + Note that the order of object properties is undefined by the API, + and may change from version to version. + + The returned memory belongs to the underlying object and may be + invalidated by any changes to that object. + + Example usage: + + @code + cson_object_iterator it; + cson_object_iter_init( myObject, &it ); // only fails if either arg is 0 + cson_kvp * kvp; + cson_string const * key; + cson_value const * val; + while( (kvp = cson_object_iter_next(&it) ) ) + { + key = cson_kvp_key(kvp); + val = cson_kvp_value(kvp); + ... + } + @endcode + + There is no need to clean up an iterator, as it holds no dynamic resources. + + @see cson_kvp_key() + @see cson_kvp_value() +*/ +cson_kvp * cson_object_iter_next( cson_object_iterator * iter ); + + +/** + Returns the key associated with the given key/value pair, + or NULL if !kvp. The memory is owned by the object which contains + the key/value pair, and may be invalidated by any modifications + to that object. +*/ +cson_string const * cson_kvp_key( cson_kvp const * kvp ); + +/** + Returns the value associated with the given key/value pair, + or NULL if !kvp. The memory is owned by the object which contains + the key/value pair, and may be invalidated by any modifications + to that object. +*/ +cson_value * cson_kvp_value( cson_kvp const * kvp ); + +/** @typedef some unsigned int type cson_size_t + +*/ +typedef unsigned int cson_size_t; + +/** + A generic buffer class. + + They can be used like this: + + @code + cson_buffer b = cson_buffer_empty; + int rc = cson_buffer_reserve( &buf, 100 ); + if( 0 != rc ) { ... allocation error ... } + ... use buf.mem ... + ... then free it up ... + cson_buffer_reserve( &buf, 0 ); + @endcode + + To take over ownership of a buffer's memory: + + @code + void * mem = b.mem; + // mem is b.capacity bytes long, but only b.used + // bytes of it has been "used" by the API. + b = cson_buffer_empty; + @endcode + + The memory now belongs to the caller and must eventually be + free()d. +*/ +struct cson_buffer +{ + /** + The number of bytes allocated for this object. + Use cson_buffer_reserve() to change its value. + */ + cson_size_t capacity; + /** + The number of bytes "used" by this object. It is not needed for + all use cases, and management of this value (if needed) is up + to the client. The cson_buffer public API does not use this + member. The intention is that this can be used to track the + length of strings which are allocated via cson_buffer, since + they need an explicit length and/or null terminator. + */ + cson_size_t used; + + /** + This is a debugging/metric-counting value + intended to help certain malloc()-conscious + clients tweak their memory reservation sizes. + Each time cson_buffer_reserve() expands the + buffer, it increments this value by 1. + */ + cson_size_t timesExpanded; + + /** + The memory allocated for and owned by this buffer. + Use cson_buffer_reserve() to change its size or + free it. To take over ownership, do: + + @code + void * myptr = buf.mem; + buf = cson_buffer_empty; + @endcode + + (You might also need to store buf.used and buf.capacity, + depending on what you want to do with the memory.) + + When doing so, the memory must eventually be passed to free() + to deallocate it. + */ + unsigned char * mem; +}; +/** Convenience typedef. */ +typedef struct cson_buffer cson_buffer; + +/** An empty-initialized cson_buffer object. */ +#define cson_buffer_empty_m {0/*capacity*/,0/*used*/,0/*timesExpanded*/,NULL/*mem*/} +/** An empty-initialized cson_buffer object. */ +extern const cson_buffer cson_buffer_empty; + +/** + Uses cson_output() to append all JSON output to the given buffer + object. The semantics for the (v, opt) parameters, and the return + value, are as documented for cson_output(). buf must be a non-NULL + pointer to a properly initialized buffer (see example below). + + Ownership of buf is not changed by calling this. + + On success 0 is returned and the contents of buf.mem are guaranteed + to be NULL-terminated. On error the buffer might contain partial + contents, and it should not be used except to free its contents. + + On error non-zero is returned. Errors include: + + - Invalid arguments: cson_rc.ArgError + + - Buffer cannot be expanded (runs out of memory): cson_rc.AllocError + + Example usage: + + @code + cson_buffer buf = cson_buffer_empty; + // optional: cson_buffer_reserve(&buf, 1024 * 10); + int rc = cson_output_buffer( myValue, &buf, NULL ); + if( 0 != rc ) { + ... error! ... + } + else { + ... use buffer ... + puts((char const*)buf.mem); + } + // In both cases, we eventually need to clean up the buffer: + cson_buffer_reserve( &buf, 0 ); + // Or take over ownership of its memory: + { + char * mem = (char *)buf.mem; + buf = cson_buffer_empty; + ... + free(mem); + } + @endcode + + @see cson_output() + +*/ +int cson_output_buffer( cson_value const * v, cson_buffer * buf, + cson_output_opt const * opt ); + +/** + This works identically to cson_parse_string(), but takes a + cson_buffer object as its input. buf->used bytes of buf->mem are + assumed to be valid JSON input, but it need not be NUL-terminated + (we only read up to buf->used bytes). The value of buf->used is + assumed to be the "string length" of buf->mem, i.e. not including + the NUL terminator. + + Returns 0 on success, non-0 on error. + + See cson_parse() for the semantics of the tgt, opt, and err + parameters. +*/ +int cson_parse_buffer( cson_value ** tgt, cson_buffer const * buf, + cson_parse_opt const * opt, cson_parse_info * err ); + + +/** + Reserves the given amount of memory for the given buffer object. + + If n is 0 then buf->mem is freed and its state is set to + NULL/0 values. + + If buf->capacity is less than or equal to n then 0 is returned and + buf is not modified. + + If n is larger than buf->capacity then buf->mem is (re)allocated + and buf->capacity contains the new length. Newly-allocated bytes + are filled with zeroes. + + On success 0 is returned. On error non-0 is returned and buf is not + modified. + + buf->mem is owned by buf and must eventually be freed by passing an + n value of 0 to this function. + + buf->used is never modified by this function. +*/ +int cson_buffer_reserve( cson_buffer * buf, cson_size_t n ); + +/** + Fills all bytes of the given buffer with the given character. + Returns the number of bytes set (buf->capacity), or 0 if + !buf or buf has no memory allocated to it. +*/ +cson_size_t cson_buffer_fill( cson_buffer * buf, char c ); + +/** + Uses a cson_data_source_f() function to buffer input into a + cson_buffer. + + dest must be a non-NULL, initialized (though possibly empty) + cson_buffer object. Its contents, if any, will be overwritten by + this function, and any memory it holds might be re-used. + + The src function is called, and passed the state parameter, to + fetch the input. If it returns non-0, this function returns that + error code. src() is called, possibly repeatedly, until it reports + that there is no more data. + + Whether or not this function succeeds, dest still owns any memory + pointed to by dest->mem, and the client must eventually free it by + calling cson_buffer_reserve(dest,0). + + dest->mem might (and possibly will) be (re)allocated by this + function, so any pointers to it held from before this call might be + invalidated by this call. + + On error non-0 is returned and dest has almost certainly been + modified but its state must be considered incomplete. + + Errors include: + + - dest or src are NULL (cson_rc.ArgError) + + - Allocation error (cson_rc.AllocError) + + - src() returns an error code + + Whether or not the state parameter may be NULL depends on + the src implementation requirements. + + On success dest will contain the contents read from the input + source. dest->used will be the length of the read-in data, and + dest->mem will point to the memory. dest->mem is automatically + NUL-terminated if this function succeeds, but dest->used does not + count that terminator. On error the state of dest->mem must be + considered incomplete, and is not guaranteed to be NUL-terminated. + + Example usage: + + @code + cson_buffer buf = cson_buffer_empty; + int rc = cson_buffer_fill_from( &buf, + cson_data_source_FILE, + stdin ); + if( rc ) + { + fprintf(stderr,"Error %d (%s) while filling buffer.\n", + rc, cson_rc_string(rc)); + cson_buffer_reserve( &buf, 0 ); + return ...; + } + ... use the buf->mem ... + ... clean up the buffer ... + cson_buffer_reserve( &buf, 0 ); + @endcode + + To take over ownership of the buffer's memory, do: + + @code + void * mem = buf.mem; + buf = cson_buffer_empty; + @endcode + + In which case the memory must eventually be passed to free() to + free it. +*/ +int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state ); + + +/** + Increments the reference count for the given value. This is a + low-level operation and should not normally be used by client code + without understanding exactly what side-effects it introduces. + Mis-use can lead to premature destruction or cause a value instance + to never be properly destructed (i.e. a memory leak). + + This function is probably only useful for the following cases: + + - You want to hold a reference to a value which is itself contained + in one or more containers, and you need to be sure that your + reference outlives the container(s) and/or that you can free your + copy of the reference without invaliding any references to the same + value held in containers. + + - You want to implement "value sharing" behaviour without using an + object or array to contain the shared value. This can be used to + ensure the lifetime of the shared value instance. Each sharing + point adds a reference and simply passed the value to + cson_value_free() when they're done. The object will be kept alive + for other sharing points which added a reference. + + Normally any such value handles would be invalidated when the + parent container(s) is/are cleaned up, but this function can be + used to effectively delay the cleanup. + + This function, at its lowest level, increments the value's + reference count by 1. + + To decrement the reference count, pass the value to + cson_value_free(), after which the value must be considered, from + the perspective of that client code, to be destroyed (though it + will not be if there are still other live references to + it). cson_value_free() will not _actually_ destroy the value until + its reference count drops to 0. + + Returns 0 on success. The only error conditions are if v is NULL + (cson_rc.ArgError) or if the reference increment would overflow + (cson_rc.RangeError). In theory a client would get allocation + errors long before the reference count could overflow (assuming + those reference counts come from container insertions, as opposed + to via this function). + + Insider notes which clients really need to know: + + For shared/constant value instances, such as those returned by + cson_value_true() and cson_value_null(), this function has no side + effects - it does not actually modify the reference count because + (A) those instances are shared across all client code and (B) those + objects are static and never get cleaned up. However, that is an + implementation detail which client code should not rely on. In + other words, if you call cson_value_add_reference() 3 times using + the value returned by cson_value_true() (which is incidentally a + shared cson_value instance), you must eventually call + cson_value_free() 3 times to (semantically) remove those + references. However, internally the reference count for that + specific cson_value instance will not be modified and those + objects will never be freed (they're stack-allocated). + + It might be interesting to note that newly-created objects + have a reference count of 0 instead of 1. This is partly because + if the initial reference is counted then it makes ownership + problematic when inserting values into containers. e.g. consider the + following code: + + @code + // ACHTUNG: this code is hypothetical and does not reflect + // what actually happens! + cson_value * v = + cson_value_new_integer( 42 ); // v's refcount = 1 + cson_array_append( myArray, v ); // v's refcount = 2 + @endcode + + If that were the case, the client would be forced to free his own + reference after inserting it into the container (which is a bit + counter-intuitive as well as intrusive). It would look a bit like + the following and would have to be done after every create/insert + operation: + + @code + // ACHTUNG: this code is hypothetical and does not reflect + // what actually happens! + cson_array_append( myArray, v ); // v's refcount = 2 + cson_value_free( v ); // v's refcount = 1 + @endcode + + (As i said: it's counter-intuitive and intrusive.) + + Instead, values start with a refcount of 0 and it is only increased + when the value is added to an object/array container or when this + function is used to manually increment it. cson_value_free() treats + a refcount of 0 or 1 equivalently, destroying the value + instance. The only semantic difference between 0 and 1, for + purposes of cleaning up, is that a value with a non-0 refcount has + been had its refcount adjusted, whereas a 0 refcount indicates a + fresh, "unowned" reference. +*/ +int cson_value_add_reference( cson_value * v ); + +#if 0 +/** + DO NOT use this unless you know EXACTLY what you're doing. + It is only in the public API to work around a couple corner + cases involving extracting child elements and discarding + their parents. + + This function sets v's reference count to the given value. + It does not clean up the object if rc is 0. + + Returns 0 on success, non-0 on error. +*/ +int cson_value_refcount_set( cson_value * v, unsigned short rc ); +#endif + +/** + Deeply copies a JSON value, be it an object/array or a "plain" + value (e.g. number/string/boolean). If cv is not NULL then this + function makes a deep clone of it and returns that clone. Ownership + of the clone is transfered to the caller, who must eventually free + the value using cson_value_free() or add it to a container + object/array to transfer ownership to the container. The returned + object will be of the same logical type as orig. + + ACHTUNG: if orig contains any cyclic references at any depth level + this function will endlessly recurse. (Having _any_ cyclic + references violates this library's requirements.) + + Returns NULL if orig is NULL or if cloning fails. Assuming that + orig is in a valid state, the only "likely" error case is that an + allocation fails while constructing the clone. In other words, if + cloning fails due to something other than an allocation error then + either orig is in an invalid state or there is a bug. +*/ +cson_value * cson_value_clone( cson_value const * orig ); + +/* LICENSE + +This software's source code, including accompanying documentation and +demonstration applications, are licensed under the following +conditions... + +Certain files are imported from external projects and have their own +licensing terms. Namely, the JSON_parser.* files. See their files for +their official licenses, but the summary is "do what you want [with +them] but leave the license text and copyright in place." + +The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/]) +explicitly disclaims copyright in all jurisdictions which recognize +such a disclaimer. In such jurisdictions, this software is released +into the Public Domain. + +In jurisdictions which do not recognize Public Domain property +(e.g. Germany as of 2011), this software is Copyright (c) 2011 by +Stephan G. Beal, and is released under the terms of the MIT License +(see below). + +In jurisdictions which recognize Public Domain property, the user of +this software may choose to accept it either as 1) Public Domain, 2) +under the conditions of the MIT License (see below), or 3) under the +terms of dual Public Domain/MIT License conditions described here, as +they choose. + +The MIT License is about as close to Public Domain as a license can +get, and is described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +-- +Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/) + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +--END OF MIT LICENSE-- + +For purposes of the above license, the term "Software" includes +documentation and demonstration source code which accompanies +this software. ("Accompanies" = is contained in the Software's +primary public source code repository.) + +*/ + +#if defined(__cplusplus) +} /*extern "C"*/ +#endif + +#endif /* WANDERINGHORSE_NET_CSON_H_INCLUDED */ +/* end file include/wh/cson/cson.h */ +/* begin file include/wh/cson/cson_sqlite3.h */ +/** @file cson_sqlite3.h + +This file contains cson's public sqlite3-to-JSON API declarations +and API documentation. If CSON_ENABLE_SQLITE3 is not defined, +or is defined to 0, then including this file will have no side-effects +other than defining CSON_ENABLE_SQLITE3 (if it was not defined) to 0 +and defining a few include guard macros. i.e. if CSON_ENABLE_SQLITE3 +is not set to a true value then the API is not visible. + +This API requires that
%h
%s
", z, zRebuildMsg); - cgi_reply(); + if( g.json.isJsonMode ){ + json_err( 0, z, 1 ); + if( g.isHTTP ){ + rc = 0 /* avoid HTTP 500 */; + } }else{ - fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg); + if( g.xferPanic ){ + cgi_reset_content(); + @ error Database\serror:\s%F(z) + cgi_reply(); + } + if( g.cgiOutput ){ + g.cgiOutput = 0; + cgi_printf("%h
%s
", z, zRebuildMsg); + cgi_reply(); + }else{ + fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg); + } } + free(z); db_force_rollback(); - fossil_exit(1); + fossil_exit(rc); } static int nBegin = 0; /* Nesting depth of BEGIN */ static int doRollback = 0; /* True to force a rollback */ static int nCommitHook = 0; /* Number of commit hooks */ @@ -944,10 +961,11 @@ ** Verify that the repository schema is correct. If it is not correct, ** issue a fatal error and die. */ void db_verify_schema(void){ if( db_schema_is_outofdate() ){ + g.json.resultCode = FSL_JSON_E_DB_NEEDS_REBUILD; fossil_warning("incorrect repository schema version"); fossil_warning("your repository has schema version \"%s\" " "but this binary expects version \"%s\"", db_get("aux-schema",0), AUX_SCHEMA); fossil_fatal("run \"fossil rebuild\" to fix this problem"); ADDED src/json.c Index: src/json.c ================================================================== --- /dev/null +++ src/json.c @@ -0,0 +1,1997 @@ +/* +** Copyright (c) 2011 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the Simplified BSD License (also +** known as the "2-Clause License" or "FreeBSD License".) + +** This program is distributed in the hope that it will be useful, +** but without any warranty; without even the implied warranty of +** merchantability or fitness for a particular purpose. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +******************************************************************************* +** +** Code for the JSON API. +** +** For notes regarding the public JSON interface, please see: +** +** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit +** +** +** Notes for hackers... +** +** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or +** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then +** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f(). +** See the API docs for that typedef (below) for the semantics of the callbacks. +** +** +*/ +#include "config.h" +#include "VERSION.h" +#include "json.h" +#include
@ You entered an unknown user or an incorrect password.
@@ -329,26 +449,11 @@
** HASH/PROJECT/LOGIN
**
** where HASH is a random hex number, PROJECT is either project
** code prefix, and LOGIN is the user name.
*/
- char *zCookie;
- const char *zCookieName = login_cookie_name();
- const char *zExpire = db_get("cookie-expire","8766");
- int expires = atoi(zExpire)*3600;
- char *zCode = abbreviated_project_code(db_get("project-code",""));
- char *zHash;
-
- zHash = db_text(0, "SELECT hex(randomblob(25))");
- zCookie = mprintf("%s/%s/%s", zHash, zCode, zUsername);
- cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
- record_login_attempt(zUsername, zIpAddr, 1);
- db_multi_exec(
- "UPDATE user SET cookie=%Q, ipaddr=%Q, "
- " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
- zHash, zRemoteAddr, expires, uid
- );
+ login_set_user_cookie(zUsername, uid, NULL);
redirect_to_g();
}
}
style_header("Login/Logout");
@ %s(zErrMsg)
@@ -542,12 +647,16 @@
fossil_free(zOtherRepo);
return nXfer;
}
/*
-** Lookup the uid for a user with zLogin and zCookie and zRemoteAddr.
-** Return 0 if not found.
+** Lookup the uid for a non-built-in user with zLogin and zCookie and
+** zRemoteAddr. Return 0 if not found.
+**
+** Note that this only searches for logged-in entries with matching
+** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
+** entries.
*/
static int login_find_user(
const char *zLogin, /* User name */
const char *zCookie, /* Login cookie value */
const char *zRemoteAddr /* Abbreviated IP address for valid login */
@@ -569,16 +678,14 @@
);
return uid;
}
/*
-** This routine examines the login cookie to see if it exists and
-** and is valid. If the login cookie checks out, it then sets
-** global variables appropriately. Global variables set include
-** g.userUid and g.zLogin and of the g.perm.Read family of permission
-** booleans.
-**
+** This routine examines the login cookie to see if it exists and and
+** is valid. If the login cookie checks out, it then sets global
+** variables appropriately. Global variables set include g.userUid
+** and g.zLogin and the g.perm family of permission booleans.
*/
void login_check_credentials(void){
int uid = 0; /* User id */
const char *zCookie; /* Text of the login cookie */
const char *zIpAddr; /* Raw IP address of the requestor */
@@ -666,11 +773,11 @@
}
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
}
/* If no user found and the REMOTE_USER environment variable is set,
- ** the accept the value of REMOTE_USER as the user.
+ ** then accept the value of REMOTE_USER as the user.
*/
if( uid==0 ){
const char *zRemoteUser = P("REMOTE_USER");
if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -716,11 +823,11 @@
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
g.zLogin = 0;
}
/* Set the capabilities */
- login_set_capabilities(zCap, 0);
+ login_replace_capabilities(zCap, 0);
login_set_anon_nobody_capabilities();
}
/*
** Memory of settings
@@ -745,22 +852,25 @@
login_anon_once = 0;
}
}
/*
-** Flags passed into the 2nd argument of login_set_capabilities().
+** Flags passed into the 2nd argument of login_set/replace_capabilities().
*/
#if INTERFACE
#define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
#define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
#endif
/*
-** Set the global capability flags based on a capability string.
+** Adds all capability flags in zCap to g.perm.
*/
void login_set_capabilities(const char *zCap, unsigned flags){
int i;
+ if(NULL==zCap){
+ return;
+ }
for(i=0; zCap[i]; i++){
switch( zCap[i] ){
case 's': g.perm.Setup = 1; /* Fall thru into Admin */
case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -814,10 +924,18 @@
break;
}
}
}
}
+
+/*
+** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
+*/
+void login_replace_capabilities(const char *zCap, unsigned flags){
+ memset(&g.perm, 0, sizeof(g.perm));
+ return login_set_capabilities(zCap, flags);
+}
/*
** If the current login lacks any of the capabilities listed in
** the input, then return 0. If all capabilities are present, then
** return 1.
@@ -892,14 +1010,19 @@
/*
** 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);
+ 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
Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -23,13 +23,15 @@
#include
%h
", z); cgi_reply(); }else{ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z); fossil_puts(zOut, 1); } + free(z); db_force_rollback(); - fossil_exit(1); + fossil_exit(rc); } /* This routine works like fossil_fatal() except that if called ** recursively, the recursive call is a no-op. ** @@ -442,25 +546,32 @@ ** 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.cgiOutput ){ + 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("%h
", 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(1); + fossil_exit(rc); } /* Print a warning message */ void fossil_warning(const char *zFormat, ...){ @@ -467,17 +578,20 @@ char *z; va_list ap; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); - if( g.cgiOutput ){ + if(g.json.isJsonMode){ + json_warn( FSL_JSON_W_UNKNOWN, z ); + }else if( g.cgiOutput ){ cgi_printf("%h
", z); }else{ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z); fossil_puts(zOut, 1); free(zOut); } + free(z); } /* ** Malloc and free routines that cannot fail */ @@ -1013,13 +1127,17 @@ if( szFile<1024 ){ if( zNotFound ){ cgi_redirect(zNotFound); }else{ - @Page not found: %h(g.zPath)
+ if(g.json.isJsonMode){ + json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0); + }else{ + cgi_set_status(404,"Not Found"); + @Page not found: %h(g.zPath)
+ } }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){ - @The database schema on the server is out-of-date. Please ask - @ the administrator to run fossil rebuild.
+ if(g.json.isJsonMode){ + json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0); + }else{ + @The database schema on the server is out-of-date. Please ask + @ the administrator to run fossil rebuild.
+ } }else{ aWebpage[idx].xFunc(); } /* Return the result. Index: src/main.mk ================================================================== --- src/main.mk +++ src/main.mk @@ -47,10 +47,16 @@ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ + $(SRCDIR)/json.c \ + $(SRCDIR)/json_artifact.c \ + $(SRCDIR)/json_branch.c \ + $(SRCDIR)/json_login.c \ + $(SRCDIR)/json_timeline.c \ + $(SRCDIR)/json_wiki.c \ $(SRCDIR)/leaf.c \ $(SRCDIR)/login.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/md5.c \ @@ -131,10 +137,16 @@ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ + $(OBJDIR)/json_.c \ + $(OBJDIR)/json_artifact_.c \ + $(OBJDIR)/json_branch_.c \ + $(OBJDIR)/json_login_.c \ + $(OBJDIR)/json_timeline_.c \ + $(OBJDIR)/json_wiki_.c \ $(OBJDIR)/leaf_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/md5_.c \ @@ -215,10 +227,16 @@ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ + $(OBJDIR)/json.o \ + $(OBJDIR)/json_artifact.o \ + $(OBJDIR)/json_branch.o \ + $(OBJDIR)/json_login.o \ + $(OBJDIR)/json_timeline.o \ + $(OBJDIR)/json_wiki.o \ $(OBJDIR)/leaf.o \ $(OBJDIR)/login.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/md5.o \ @@ -301,11 +319,11 @@ # using -lsqlite3. SQLITE3_OBJ.1 = SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ. = $(SQLITE3_OBJ.0) -EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o +EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(OBJDIR)/cson_amalgamation.o $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build @@ -319,13 +337,14 @@ $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex $(OBJDIR)/mkindex $(TRANS_SRC) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h - $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h + $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h touch $(OBJDIR)/headers $(OBJDIR)/headers: Makefile +$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_login.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h Makefile: $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h @@ -589,10 +608,52 @@ $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c $(OBJDIR)/info.h: $(OBJDIR)/headers +$(OBJDIR)/json_.c: $(SRCDIR)/json.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/json.c >$(OBJDIR)/json_.c + +$(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c + +$(OBJDIR)/json.h: $(OBJDIR)/headers +$(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c + +$(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c + +$(OBJDIR)/json_artifact.h: $(OBJDIR)/headers +$(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c + +$(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c + +$(OBJDIR)/json_branch.h: $(OBJDIR)/headers +$(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c + +$(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c + +$(OBJDIR)/json_login.h: $(OBJDIR)/headers +$(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c + +$(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c + +$(OBJDIR)/json_timeline.h: $(OBJDIR)/headers +$(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c + +$(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c + +$(OBJDIR)/json_wiki.h: $(OBJDIR)/headers $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c @@ -909,5 +970,8 @@ $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o +$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c + $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o + Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -53,10 +53,16 @@ http http_socket http_transport import info + json + json_artifact + json_branch + json_login + json_timeline + json_wiki leaf login main manifest md5 @@ -202,11 +208,12 @@ EXTRAOBJ = \ $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ - $(OBJDIR)/th_lang.o + $(OBJDIR)/th_lang.o \ + $(OBJDIR)/cson_amalgamation.o $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build @@ -225,17 +232,19 @@ append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h" set extra_h($s) {} } append mhargs " \$(SRCDIR)/sqlite3.h" append mhargs " \$(SRCDIR)/th.h" +#append mhargs " \$(SRCDIR)/cson_amalgamation.h" append mhargs " \$(OBJDIR)/VERSION.h" writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@" writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" writeln "\t\$(OBJDIR)/makeheaders $mhargs" writeln "\ttouch \$(OBJDIR)/headers" writeln "\$(OBJDIR)/headers: Makefile" +writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h" writeln "Makefile:" set extra_h(main) \$(OBJDIR)/page_index.h foreach s [lsort $src] { writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate" @@ -264,10 +273,15 @@ writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" + +set opt {} +writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" +writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" + close $output_file # # End of the main.mk output ############################################################################## @@ -412,11 +426,12 @@ EXTRAOBJ = \ $(OBJDIR)/sqlite3.o \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ - $(OBJDIR)/th_lang.o + $(OBJDIR)/th_lang.o \ + $(OBJDIR)/cson_amalgamation.o $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o # This rule prevents make from using its default rules to try build @@ -466,10 +481,15 @@ writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c" set opt $SQLITE_OPTIONS writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" +set opt {} +writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" +writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h" + writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" set opt {-Dmain=sqlite3_shell} append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n" @@ -577,10 +597,13 @@ $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) -o$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) -o$@ -c $** + +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h + cp $@ $@ VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION +$** > $@ page_index.h: mkindex$E $(SRC) @@ -591,10 +614,17 @@ -del *.obj *_.c *.h *.map realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E +$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h + } foreach s [lsort $src] { writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h" writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n" writeln "${s}_.c : \$(SRCDIR)\\$s.c" @@ -603,11 +633,11 @@ writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E " foreach s [lsort $src] { writeln -nonewline "${s}_.c:$s.h " } -writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h" +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" writeln "\t@copy /Y nul: headers" close $output_file # # End of the win/Makefile.dmc output @@ -717,10 +747,12 @@ $(OX)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION $** > $@ +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h + cp $(SRCDIR)\cson_amalgamation.h $@ page_index.h: mkindex$E $(SRC) $** > $@ clean: @@ -729,10 +761,17 @@ -del headers linkopts realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E +$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h +$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h + } foreach s [lsort $src] { writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" writeln "${s}_.c : \$(SRCDIR)\\$s.c" @@ -741,11 +780,11 @@ writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E " foreach s [lsort $src] { writeln -nonewline "${s}_.c:$s.h " } -writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h" +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" writeln "\t@copy /Y nul: headers" close $output_file # Index: src/report.c ================================================================== --- src/report.c +++ src/report.c @@ -19,10 +19,11 @@ */ #include "config.h" #include