NAME
ort-c-header
—
generate ort C API
SYNOPSIS
ort-c-header |
[-jJsv ] [-g
guard] [-N
db] [config...] |
DESCRIPTION
The ort-c-header
utility accepts
ort(5)
config files, defaulting to standard input, and
generates a C API. These declarations is usually defined by the output of
ort-c-source(1). Its
arguments are as follows:
-j
- Output JSON export function declarations.
-J
- Output JSON import function declarations.
-s
- Enable safe types, where natural field types (e.g.,
int
) are embedded in typed structures to prevent mis-assignment. -v
- Output Data validation function and variable declarations.
-g
guard- Preprocessor guard for header re-inclusion. Defaults to DB_H. This is not sanity-checked.
-N
bd- Disable production of output, which may be b and/or d. The b flag suppresses the foundational Data structures declarations, while d suppresses the Database input. This flag is used when creating multiple header files.
By default, the Database input and Data structures declarations are output.
Data structures
The basis of the C API is a series of enumerations, structures, and preprocessor macros that describe the data. Unless as otherwise noted, all fields except for string literal data (e.g., comments) are printed in lowercase. To include output files for data structures, specify:
#include <sys/queue.h> /* iff the config has lists */ #include <stdint.h>
Output begins with the definition of roles. This only happens if roles are specified in config, such as:
roles { role user; };
Each of these are produced as an enumeration (always called enum ort_role), with the extra ROLE_none and ROLE_default produced for the empty and initial roles, respectively. See the Database input section for details on role management.
enum ort_role { ROLE_user, ROLE_none, ROLE_default };
Next, an enumeration is generated for each
enum
with the name as given. The enumeration is then
given a series of values for each item in the enumeration object.
enum foo { item bar 0; item baz 1; };
Output variables are prefixed with the capitalised enumeration name, followed by an underscore, followed by the item name.
enum foo { FOO_bar = 0, FOO_baz = 1 };
Bit-fields produce similar code, except that each bit-field value is represented by the bit index and mask.
bits foo { item baz 1; };
This produces an enumeration that describes the index of the bit prefixed with "BITI", the underscore, followed by the uppercase name; and the mask when the respective bit is set prefixed with "BITF", the underscore, and the uppercase name.
enum foo { BITI_FOO_baz = 1, BITF_FOO_baz = (1U << 1) };
Structures are the foundational data structure of the API. They
are named as defined in the configuration and consist of variables mapped to
field
entries along with bookkeeping fields: if a
variable may be null
, an extra
int variable prefixed with "has_" is defined
as well; or if it is a blob
, an extra
size_t variable suffixed with "_sz" is
defined.
struct company { field name text; field foo enum foo null; field id int rowid; };
By default, text variables are NUL-terminated char * strings, blobs are void *, integers are int64_t, reals are double, epoch values are time_t, enumerations are as named, and nested structures (foreign key references) are defined as structures of the given type.
struct company { char *name; enum foo foo; int has_foo; company_id id; };
Integer and epoch fields use defined types with the name of the structure, an underscore, then the field name. If safe typing is enabled, these are implemented as structures with a single integer field whose name is not fixed. Otherwise, the type is an alias for int64_t or time_t, as appropriate.
typedef int64_t company_id; # typedef struct { int64_t val } company_id;
Integers may be set, get, or assigned by using the following macros or inline functions. These should be used when safe types are enabled and are also produced in non-safe mode for easier transition. Callers should not assume they are implemented as either macros or functions, however, as this may change.
ORT_company_id
(int64_t x)- Return a company_id value initialised to x.
ORT_GET_company_id
(const struct company *p)- Return the int64_t value from the id field within p.
ORT_GETV_company_id
(const company_id p)- Like
ORT_GET_company_id
(), but getting the int64_t value directly from the field. ORT_SET_company_id
(struct company *p, int64_t val)- Set the id field within p to val.
ORT_SETV_company_id
(company_id *p, int64_t val)- Like
ORT_SET_company_id
(), but setting val directly to the field p.
If the structure consists of a list
operation, a TAILQ_ENTRY(3) named _entries is produced in
its output. If roles are defined, each structure has a variable
priv_store of an opaque pointer type
struct ort_store. This is used to keep track of the
role in which the query function was invoked.
Database input
Input functions define how the structures described in
Data structures are pulled from
the database. These functions invoke sqlbox(3) for handling the database itself, so the
-l
sqlbox library is required
for linking. Depending on your operating system, you may need the following
inclusion before including the header file.
#include <stdint.h> /* int64_t... */
There are two functions,
db_open
()
and
db_close
(),
that open and close the database, respectively. There are also several
transaction functions and, if applicable, a function for managing roles.
- void
db_close
(struct ort *p) - Closes a database opened by
db_open
(). PassingNULL
is a noop. - struct ort
*db_open
(const char *file) - Like
db_open_logging
(), but withNULL
values for all parameters except file. - struct ort
*db_open_logging
(const char *file, void (*log)(const char *, void *), void (*log_short)(const char *, ...), void *log_arg) - Open a database named file and return a pointer.
If log is not set, it will be invoked within the child or parent process on database errors with the error message first, then log_arg as the second parameter. It must not have any side effects. If log_short is set and log is
NULL
, it is used and is compatible with the warn(3) family of functions.Returns
NULL
on failure to allocate, open, or configure the database. - void
db_logging_data
(struct ort *p, const void *arg, size_t sz) - Set the opaque pointer log_arg as otherwise passed
to
db_open_logging
(). The binary data in arg of byte size sz are passed to the child process. - void
db_trans_commit
(struct ort *p, size_t id) - Commit a transaction opened by
db_trans_open
() with identifier id. If an error occurs, subsequent database access will cause the system to exit(3). - void
db_trans_open
(struct ort *p, size_t id, int mode) - Open a transaction with a unique identifier id. The identifier prevents recursive transactions and allows for identifying open transactions on error. If mode is zero, the transaction locks the database on first access with shared locks (no writes allowed, reads allowed) on queries and unshared locks (single writer, reads allowed) on modification. If >0, the transaction immediately enters unshared lock mode. If <0, the transaction locks exclusively, preventing all other access. Using >0 is the preferred way of creating database transactions. If an error occurs, subsequent database access will cause the system to exit(3).
- void
db_trans_rollback
(struct ort *p, size_t id) - Roll-back a transaction opened by
db_trans_open
() with identifier id. If an error occurs, subsequent database access will cause the system to exit(3). - void
db_role
(struct ort *ctx, enum ort_role newrole) - If roles are enabled, move from the current role to
newrole. If the role is the same as the current
role, this does nothing. Roles may only transition to ancestor roles, not
descendant roles or siblings, or any other non-ancestor roles. The only
exception is when leaving
ROLE_default
or enteringROLE_none
. This does not return failure: on role violation, it invokes abort(2). - enum ort_role
db_role_current
(struct ort *ctx) - If roles are enabled, get the currently-assigned role. If
db_role
() hasn't yet been called, this will beROLE_default
. - enum ort_role
db_role_stored
(struct ort_store *ctx) - If roles are enabled, get the role assigned to an object at the time of its creation.
Each structure has a number of operations for operating on the
Data structures. These are all
stipulated as count
, delete
,
insert
, iterate
,
list
, search
, and
update
statements in the configuration. Let
"foo" be the name of the exemplar structure.
- void
db_foo_delete_xxxx
(struct ort *p, ARGS) - Run the named
delete
function "xxxx". The ARGS passed to this function are the fields that constrain which rows are deleted. Parameters are only specified for operations for binary-operator constraints, i.e., those not checking for null status. - void
db_foo_delete_by_yy_op
(struct ort *p, ARGS) - Like
db_foo_delete_xxxx
(), but using an un-nameddelete
statement constrained by "yy" with operation "op". - void
db_foo_free
(struct foo *p) - Frees a pointer returned by a unique search function. If passed
NULL
, this is a noop. - void
db_foo_freeq
(struct foo_q *p) - Frees a queue (and its members) created by a listing function. This function is produced only if there are listing statements on a given structure.
- struct foo
*db_foo_get_xxxx
(struct ort *p, ARGS) - The
search
statement named "xxxx". The function accepts variables for all binary-operator fields to check (i.e., all except for those checking for null). - struct foo
*db_foo_get_by_xxxx_op1_yy_zz_op2
(struct ort *p, ARGS) - Like
db_foo_get_xxxx
(), but for (possibly-nested) structures. In the given example, "xxxx" is a field in the given structure with operation "op1" and "yy_zz" means a field "zz" in the nested structure "yy" with operation "op2". - int64_t
db_foo_insert
(struct ort *p, ARGS) - Insert a row and return its identifier or -1 on constraint failure. This
accepts all native fields ARGS as parameters
excluding
rowid
, which is automatically set by the database. If any fields are specified asnull
, they are passed into this functions as pointers. The null values must then be specified asNULL
pointers. This function is only generated if theinsert
statement is specified for the given structure. - void
db_foo_iterate
(struct ort *p, foo_cb cb, void *arg, ARGS) - Like
db_foo_iterate_xxxx
() but iterating over all rows. - void
db_foo_iterate_xxxx
(struct ort *p, foo_cb cb, void *arg, ARGS) - Like
db_foo_get_xxxx
(), but invoking a function callback cb passed arg within the active query for each retrieved result. - void
db_foo_iterate_by_xxxx_op1_yy_zz_op2
(struct ort *p, foo_cb cb, void *arg, ARGS) - Like
db_foo_get_by_xxxx_op1_yy_zz_op2
(), but invoking a function callback for each retrieved result. - uint64_t
db_foo_count
(struct ort *p) - Like
db_foo_count_xxxx
() but returning a count of all rows. - uint64_t
db_foo_count_xxxx
(struct ort *p, ARGS) - Like
db_foo_get_xxxx
(), but returning a count of all rows returned. - uint64_t
db_foo_count_by_xxxx_op1_yy_zz_op2
(struct ort *p, ARGS) - Like
db_foo_get_by_xxxx_op1_yy_zz_op2
(), but returning a count of all rows returned. - struct foo_q
*db_foo_list
(struct ort *p) - Like
db_foo_list_xxxx
() but allocating and filling a queue of all rows. - struct foo_q
*db_foo_list_xxxx
(struct ort *p, ARGS) - Like
db_foo_get_xxxx
(), but producing a queue of responses. - struct foo_q
*db_foo_list_by_xxxx_op1_yy_zz_op2
(struct ort *p, ARGS) - Like
db_foo_get_by_xxxx_op1_yy_zz_op2
(), but producing a queue of responses. - int
db_foo_update_xxxx
(struct ort *p, ARGS) - Run the named update function "xxxx". The update functions are
specified with
update
statements. The parameters passed to this function are first the fields to modify, then the fields that constrain which rows are updated. If any modified fields are specified asnull
, they are passed into this functions as pointers. Any null values must then be specified asNULL
pointers. Update fields are only specified for operations for binary-operator constraints, i.e., those not checking for null status. Returns non-zero on success, zero on constraint failure. - int
db_foo_update_xx_mod_by_yy_op
(struct ort *p, ARGS) - Like
db_foo_update_xxxx
(), but using an un-named update statement modifying "xx" with modifier "mod" constrained by "yy" with operation "op". Either or both modifiers and constraints may be empty. If modifiers are empty, all fields are modified by setting. If constraints are empty, they and the preceding "by" are omitted.
JSON export
These functions invoke kcgijson(3) to manage output formats. The header files for both kcgijson(3) and kcgi(3) must be stipulated.
#include <sys/types.h> /* kcgi(3) */ #include <stdarg.h> /* kcgi(3) */ #include <stdint.h> /* kcgi(3) */ #include <kcgi.h> #include <kcgijson.h>
All structure and field names are expressed in lowercase.
- void
json_foo_array
(struct kjsonreq *r, const struct foo_q *q) - Print the list q of structures as a key-value pair
where the key is the structure name and the value is an array consisting
of
json_foo_data
() objects. This is only produced if the structure haslist
queries stipulated. - void
json_foo_data
(struct kjsonreq *r, const struct foo *p) - Enumerate only the fields of the structure p in JSON
dictionary format. The key is the field name and the value is a string for
text types, decimal number for reals, integer for integers, and
base64-encoded string for blobs. If a field is null, it is serialised as a
null value. Fields marked
noexport
are not included in the enumeration, nor are passwords. - void
json_foo_iterate
(const struct foo *p, void *arg) - Print a “blank” object consisting only of the structure data
(see
json_foo_data
()) within JSON object braces. The calling convention (passing a void * as the struct kjsonreq) makes for easy integration with iterate functions. This is only produced if the structure hasiterate
queries stipulated. - void
json_foo_obj
(struct kjsonreq *r, const struct foo *p) - Print the entire structure p as a key-value pair
where the key is the structure name and the value is an object consisting
of
json_foo_data
().
JSON import
Utility functions for parsing buffers into objects defined in a ort(5) configuration. Unlike the functions in JSON export, these do not require any additional headers. The following use “foo” as an example structure.
- int
jsmn_init
(jsmn_parser *p) - Initialise a parser. This must be called each time before
jsmn_parse
() is invoked. - int
jsmn_parse
(jsmn_parse *p, const char *buf, size_t sz, jsmntok_t *toks, unsigned int toksz) - Parse a buffer buf of length
sz into a series of tokens
toks of length toksz. Returns
less than zero on failure or the number of tokens parsed. If invoked with
a
NULL
value for toks, tokens are parsed but not filled in. This is the usual practise for parsing an unknown set of objects: a set of tokens may be allocated using the non-negative return value. - int
jsmn_eq
(const char *buf, const jsmntok_t *t, const char *s) - Test whether the current token t referencing buffer buf is a string equal (case sensitive) to s.
- int
jsmn_foo
(struct foo *p, const char *buf, const jsmntok_t *t, size_t toksz) - Parse an object starting at token t referencing
buffer buf with toksz
remaining tokens into p. Returns less than zero on
allocation failure, zero on parse error (malformed fields), or the number
of tokens parsed. The input structure should be zeroed prior to calling.
Regardless the return value, the resulting pointer should be passed to
jsmn_foo_free
(). - int
jsmn_foo_array
(struct foo **p, size_t *sz, const char *buf, const jsmntok_t *t, size_t toksz) - Like
jsmn_foo
(), but allocating and filling an array of structures. The array must be freed withjsmn_foo_free_array
() regardless the return value. - void
jsmn_foo_free_array
(struct foo *p, size_t sz) - Free an array of structures, recursively clearing all nested data. Does
nothing if p is
NULL
. - void
jsmn_foo_clear
(struct foo *p) - Recursively clears all nested data, not touching the pointer. Does nothing
if p is
NULL
.
The parser writes the parse tree tokens into a linear array in infix order. Each node is either an object (consisting of string key and value pairs), an array, a primitive, or a string. To drive the parser, initialise a parse, parse the input (usually twice: once to get the number of tokens, the second to fill in tokens), recursively descend into the token stream. The descent should occur for all objects and arrays.
Data validation
These functions invoke kcgi(3) to perform basic type validation. The following are required for including the produced functions.
#include <sys/types.h> /* kcgi(3) */ #include <stdarg.h> /* kcgi(3) */ #include <stdint.h> /* kcgi(3) */ #include <kcgi.h>
A full validation array is given for all fields, although these need not be used by the calling application. Given the same structure "foo", the following are generated:
- int
valid_foo_xxxx
(struct kpair *p) - Validate the field "xxxx" in the structure. This should be used in place of raw validation functions such as kvalid_int(3). The validation function will at least validate the type. If limitation clauses are given to a field, those will also be emitted within this function. Note: structs are not validated.
- enum valid_keys
- An enumeration of all fields that accept validators. The enumeration
entries are
VALID_FOO_XXXX
, where "XXXX" is the field name. The last enumeration value is alwaysVALID__MAX
. - const struct kvalid valid_keys[VALID__MAX]
- A validation array for khttp_parse(3). This uses the
valid_foo_xxxx
() functions as described above and names corresponding HTML form entries as "foo-xxxx", where again, "xxxx" is the field name.
EXIT STATUS
The ort-c-header
utility exits 0 on
success, and >0 if an error occurs.
EXAMPLES
In the simplest case, put all C sources and headers (for validation, database routines, and JSON output) into one pair of files. Let foo.ort be the configuration file.
% ort-c-header -jv foo.ort > db.h % ort-c-source -jv foo.ort > db.c
Breaking up into two header and source files: one for basic database functions, the other for JSON output.
% ort-c-header foo.ort > db.h % ort-c-header -g JSON_H -j -Nbd foo.ort > json.h % ort-c-source -h db.h > db.c % ort-c-source -j -Nb -Ibj -h db.h,json.h > json.c
In this more complicated snippet, the
json.h file is created without structure or database
information using -N
, -then
json.c needs to include both database and JSON
headers (in name, -h
, and in the headers those
stipulated in source, -I
) also while inhibiting
database routine creation with -N
.
SEE ALSO
ort-c-manpage(1), ort-c-source(1), kcgi(3), kcgijson(3), sqlbox(3), ort(5)