[Copyright status: this file is in the public domain.] In Xlib, there is a concept of resource databases. This is not, strictly, part of X; the protocol spec has nothing of the sort. But it is a useful facility. liblx has something analogous. There is an opaque type, LX_DB, which represents a database of resource settings, and a handful of supporting types and calls. LX_DBVAL represents a value returned from a lookup; it has pointer (const char *s) and length (int l) members. As a convenience for the common case of string values, .s[.l] is always '\0' in non-error cases. Errors, such as looking for an etry that isn't there, are represented with .s set to nil and .l set to -1. .s is consted because the returned strings are always owned by the database code and must never be written by the application. The interface is built around strings of `labels', short strings representing references to things with meaning to a particular application or set of applications. Resources can be named by `class' or `name', as well, allowing multiple things to have a common resource because they are all in the same class (an example might be user-interface buttons, which might want to all use the same font). For example, an SGF file displayer might look up the foreground colour for its `quit' button by looking for name .sgf.buttons.quit.foreground class .Displayer.Button.Quit.Foreground, which (assuming the presence of other buttons) would allow a resource for .sgf.Button*Foreground to specify a foreground colour for all buttons, possibly with another resource like .sgf.button.quit.foreground to specify the foreground colour for that particular button without affecting any others. As the above examples imply, resource names normally begin with lowercase letters and resource classes with initial caps, but nothing enforces this; it is purely convention. These labels are represented as `quark's in the interface, using a type LX_QUARK (the name was inspired by Xlib's analog); an LX_QUARK is an integral type which serves as an interned form of the labels. There is also the notion of `loose' and `tight' binding, which is present in the above example in the form of . versus * separating labels. In .sgf.button.quit.foreground, all the bindings are tight, whereas in .sgf.Button*Foreground the binding between Button and Foreground is loose. (If the initial binding is . - tight - then it can be, and usually is, omitted, so these would typically be written, eg, sgf.Button*Foreground, skipping the leading dot.) There are two special LX_QUARK values: LX_ERRQUARK, which represents errors such as attempts to get the quark for a string which contains characters not permitted in database labels, and LX_NOQUARK, which is unused by the library at this writing but is intended to represent non-erroneous absence of a quark. LX_BINDING is a type for a binding; it has values LX_BIND_TIGHT and LX_BIND_LOOSE. LX_BOUNDQUARK represents a quark and its preceding binding; it is a struct containing b, an LX_BINDING, and q, an LX_QUARK. In alternating binding-quark lists, such as the above, the binding in an LX_BOUNDQUARK is the one that textually comes before the string form of the quark. For example, "sgf.Button*Foreground", or more fully ".sgf.Button*Foreground", would be represented as "tight, quark for `sgf', tight, quark for `Button', loose, quark for `Foreground'"; this would be three LX_BOUNDQUARKs, containing, in order, , , and . There are also two types related to iteration over databases. LX_DB_ITER is an opaque type representing the state of an iteration over a particular database, and LX_DB_ITERSTAT is the status of a single step of such an iteration. See lx_db_iter_start, lx_db_iter_next, and lx_db_iter_done, below, for more. The resource database facilities are mostly independent of any particular connection. Most of these calls have nothing to do with LX_CONNs. One important note is that there is no guarantee that, even for exactly the same sequence of calls by exactly the same program, the same LX_QUARK values will be returned for different runs. Except for LX_NOQUARK and LX_ERRQUARK, it is a bug to try to use quark values in a process other than the one they were created in. This includes using them in the child after a fork (or fork-like call such as vfork). The relevant calls: lx_new_quark ------------ LX_QUARK lx_new_quark(void) This creates and returns a new quark that does not correspond to any resource-manager string. (For those coming from an Xlib background, this is liblx's analog of XrmUniqueQuark.) lx_quark_for_string ------------------- LX_QUARK lx_quark_for_string(const char *str, int len) This looks up the quark for the string pointed to by str, length len (except that, if len is -1, strlen(str) is used instead). If such a quark already exists, it is returned; otherwise, if the string is a valid quark string, creates and returns a quark for it. Otherwise, returns LX_ERRQUARK. (Any string is a valid quark string, provided it contains only ASCII letters, digits, dashes, and underscores. Using pointer-and-length instead of NUL-termination is usually so the string can include NULs. Here, that is not relevant; in this case the reason is that many uses want to look up a quark for a substring of a larger string and would rather not copy it just to arrange for a NUL terminator.) The argument string never needs to remain valid after lx_quark_for_string returns. lx_rm_bq_string --------------- int lx_rm_bq_string(const char *s, int l, LX_BOUNDQUARK *bqv, int bqn) This converts a resource database string to an LX_BOUNDQUARK list. The string is pointer-and-length (s and l); if l is -1, strlen(s) is used instead. The return value is the number of components found in the string. On error, such as a syntax error in the string, returns -1; in this case, it is undefined which, if any, of the LX_BOUNDQUARKs are written to, nor what values are written to any that are. The LX_BOUNDQUARKs are written through bqv, which has length bqn. If there are more than bqn components in the string, the pairs after the first bqn of them are not written anywhere (and, indeed, it is undefined which, if any, of the non-stored quarks are actually created), but the return value is still correct. This can be detected by comparing the return value against the bqn value passed in and can be used to, for example, lazily allocate destination arrays. The argument string never needs to remain valid after lx_rm_bq_string returns. lx_rm_q_string -------------- int lx_rm_q_string(const char *s, int l, LX_QUARK *qv, int qn) This is just like lx_rm_bq_string except that (a) it requires that all bindings be tight (. rather than *) and it doesn't return the LX_BINDINGs (since they'd be constant). Overflow, memory lifetime, and l==-1 convenience are all as for lx_rm_bq_string. lx_db_new --------- LX_DB *lx_db_new(void) Creates and returns a new database containing no entries. lx_db_done ---------- void lx_db_done(LX_DB *db) Destroys the the database db points to, freeing all memory used by it. db may also be nil, in which case this does nothing. lx_db_putq ---------- int lx_db_putq(LX_DB *db, const LX_BOUNDQUARK *bq, int bqn, const void *val, int vallen, unsigned int flags) This is the most fundamental (and usually fastest, and hardest to use) routine for altering databases. In all cases, bq and bqn describe the lookup key to be manipulated. The return value is usually 0 if it did nothing, 1 if it modified the database, or -1 in error cases; some flags can modify this (see below). As a convenience when using NUL-terminated value strings, if vallen is -1 and anything actually uses the value, strlen(val) is used as the length. bqn<1 and vallen<-1 are always errors. Undefined bits set in flags is always an error. Valid flag bits are: LX_DB_PUTQ_DELETE LX_DB_PUTQ_PROBE LX_DB_PUTQ_NOTIFX LX_DB_PUTQ_NOTIFNX If flags includes LX_DB_PUTQ_DELETE, it is an error if there are any other bits set in flags, and any entry in the database for bq/bqn is removed. val and vallen are ignored (except that vallen<0 is still an error). Otherwise, if flags includes LX_DB_PUTQ_PROBE, it is an error if there are any other bits set in flags. Otherwise, the database is never changed, and the return value is 1 if there is an entry for bq/bqn or 0 if not. val and vallen are ignored (except that vallen<0 is still an error). Otherwise, an entry is potentially inserted or modified. If flags includes LX_DB_PUTQ_NOTIFX and the database contains any entry for bq/bqn, nothing happens (and 0 is returned). If flags includes LX_DB_PUTQ_NOTIFNX and the database does not contain any entry for bq/bqn, nothing happens (and 0 is returned). (These are "not if exists" and "not if doesn't exist", respectively. They can be used to support "change iff it exists" and "insert iff it doesn't already exist".) If both are specified, the logically consistent thing happens: nothing is done regardless. If neither is specified, the operation below always happens. (This is why they are phrased negatively: so the combination of both makes sense.) Otherwise, an entry is created or replaced. If the database contains no entry for bq/bqn, one is created with val/vallen as its value; otherwise, the existing entry has its value replaced with val/vallen. (Replacing the value of an existing entry is considered a change, even if the new value is identical to the old.) The memory pointed to by bq and val does not need to remain valid after lx_db_putq returns. lx_db_getq ---------- LX_DBVAL lx_db_getq(LX_DB *db, const LX_BOUNDQUARK *bq, int bqn) This looks up bq/bqn in the database, returning the value found as an LX_DBVAL. If there is no such entry, returns . This looks up only the exact bq/bqn given. As implied by its taking one vector of LX_BOUNDQUARKs instead of two vectors of LX_QUARKs, it is not what you want for normal database queries; for those, see lx_db_query. On success, the memory pointed to by the returned LX_DBVAL remains valid until the relevant entry is removed or has its value changed (or the database is destroyed). The memory pointed to by bq does not need to remain valid after lx_db_getq returns. lx_db_query ----------- LX_DBVAL lx_db_query(LX_DB *db, const LX_QUARK *nqv, const LX_QUARK *cqv, int qn) This performs a query, looking in db for an entry matching name nqv/qn, class cqv/qn. The most specific value (in the sense described below) found is returned, or if no match is found. Note there is only one length value, which describes both quark vectors. Return memory lifetime is as for lx_db_getq(). The memory pointed to by nqv and cqv does not need to remain valid after lx_db_query returns. lx_db_from_string ----------------- LX_DB *lx_db_from_string(const char *s, int l, unsigned int flags, ...) Creates a database from a string, specified as pointer-and-length. As a convenience, if l is -1, strlen(s) is used as the length. This breaks s at newlines, processing lines according to the ResourceLine definition below. There does not need to be a final trailing newline. flags specifies various flags, some of which, when set, involve additional arguments. When multiple flags involving arguments are given, the additional arguments appear in the arglist in the same order as the flags are listed here: LX_DB_IGNORE_ERR No additional args. Normally, errors cause lx_db_from_string to return nil. With this flag, they cause any erroneous lines to be ignored; the returned database is formed from the non-erroneous lines (if any). LX_DB_ERR_COUNT Additional args: one int *. A count of errors encountered is written through the int *. (If LX_DB_IGNORE_ERR is not also set, this number will be at most one, since in that case processing stops at the first error encounterd.) Undefined flag bits set in flags are always an error, provoking a nil return. If the string includes no lines with data on them (ie, no non-blank, non-comment, and if LX_DB_IGNORE_ERR non-erroneous lines), the resulting database will be empty. If the data specifies the same lookup key multiple times, it is specifically not defined which value gets used. The argument string does not need to remain valid after lx_db_from_string returns. lx_db_from_file --------------- LX_DB *lx_db_from_file(const char *p, unsigned int flags, ...) Creates a database from the contents of a file, specified as a pathname (p). Unlike many calls taking strings, this is a NUL-terminated string, not a pointer-and-length. (The reason for this is that most cases probably already have the pathname NUL-terminated; even if not, there would be no saving making a copy, because working with the pathname requires a NUL-terminated string anyway.) This is semantically equivalent to lx_db_from_string on the file's contents, with flags and all later args simply passed along, though it is not necessarily implemented that way. The pathname string does not need to remain valid after lx_db_from_file returns. lx_db_from_callback ------------------- LX_DB *lx_db_from_callback(char (*cb)(int, void *), int len, void *cbarg, unsigned int flags, ...); Creates a database from data accessed via a callback (which thus can be stored anywhere or potentially even computed at run-time and thus not actually stored). It is just like lx_db_from_string except that the characters are obtained by calling (*cb)() instead of reading from a string (and there is no special case for len==-1). To obtain the character at index x, (*cb)(x,cbarg) is called. Note that there is no promise that calls to (*cb)() will always use sequentially increasing index values. While that will be the common case (it is reasonable to optimize for it, for example), nothing more is promised; (*cb)() must be prepared to be called with any index from 0 through len-1 at any point in lx_db_from_callback()'s operation. lx_db_merge ----------- void lx_db_merge(LX_DB *tdb, LX_DB *fdb) Merges two databases. Merges all entries from fdb ("from database") into tdb ("to database"), with fdb's entries winning in case of any conflicts. After this operation, fdb is left valid but empty, in a state equivalent to having just been returned by lx_db_new. fdb may also be nil, in which case this does nothing (tdb must still be valid, though the implementation does not necessarily catch all cases of invalid tdb pointers). lx_db_map --------- int lx_db_map(LX_DB *db, int (*cb)(void *, const LX_BOUNDQUARK *bqv, int nbq, const LX_DBVAL *val), void *cbarg) Iterates over all entries in a database, callback style. This calls cb for each entry in db (at least potentially; it may skip some - see the following). If any callback returns a negative value, lx_db_map returns that same value immediately, without processing further or making any more callbacks. If that never happens, lx_db_map returns the sum of the values returned by all the callbacks. (If the database has no entries, lx_db_map returns 0 without calling the callback at all.) Each call to the callback gets cbarg as its first argument (this pointer is opaque to the resource manager code; it is used only for passing to the callback). The second and third arguments describe the key, as an LX_BOUNDQUARK vector and length. The final arg is the associated value, as an LX_DBVAL. As the const qualifiers imply, the callback must not write to any of these; furthermore, with one exception, the pointed-to memory is not promised to remain valid past the time the callback returns. That exception is that the value data, the bytes val->s points to, remain valid until the relevant database entry is removed or has its value changed, or the database is destroyed. If the callback modifies the database and returns a negative value, the modification remains and lx_db_map returns per the above spec. If the callback modifies the database and returns a non-negative value, what happens is undefined. Nothing is promised about what order database entries are processed in. lx_db_iter_start, lx_db_iter_next, lx_db_iter_done ---------------- --------------- --------------- LX_DB_ITER *lx_db_iter_start(LX_DB *db) LX_DB_ITERSTAT lx_db_iter_next(LX_DB_ITER *i, LX_BOUNDQUARK *bq, int *bqn, LX_DBVAL *val) void lx_db_iter_done(LX_DB_ITER *i) Iterates over all entries in a database, call-driven style. lx_db_iter_start starts an iteration over db's entries. LX_DB_ITER is an opaque type encapsulating the iterator state. Call lx_db_iter_next to get the next entry, lx_db_iter_done when done. (There is no requirement that the iteration finish before calling lx_db_iter_done.) lx_db_iter_next returns the next entry in the database. bqn is a value-result parameter. When nonnegative on entry, it points to the number of LX_BOUNDQUARKs bq points to; in some cases, it is written through (see below). If the iteration has reached the end of the available entries, it returns LX_DB_ITER_DONE and does nothing else (ignoring bq, bqn, and val). Otherwise, if *bqn is negative on entry, it ignores bq and val, advances the iteration, and returns LX_DB_ITER_SKIP. Otherwise, if the next entry overflows bq/bqn, lx_db_iter_next sets *bqn to the number of LX_BOUNDQUARKs needed to handle the entry, does not store anything through bq or val, does not advance the iteration, and returns LX_DB_ITER_OVERFLOW. Otherwise (the usual case), it writes the quarks and bindings through bq, the number of them through bqn, the value through val (or .s=nil,.l=-1 if there is no value for that node), advances the iteration, and returns LX_DB_ITER_NORMAL. In the LX_DB_ITER_NORMAL case, the memory pointed to by val->s, when non-nil, remains valid until the relevant entry is removed from the database or has its value changed, or the database is destroyed. lx_db_iter_done shuts down the iteration, disposing of the LX_DB_ITER, which must not be used further. There is no requirement that the iteration finish before calling lx_db_iter_done. If a database is modified or destroyed while an LX_DB_ITER is in active on it (ie, returned by lx_db_iter_start but not yet passed to lx_db_iter_done), then, after the modification or destruction, the result of calling lx_db_iter_next on that LX_DB_ITER is undefined, but it is safe to call lx_db_iter_done on it. The order in which the entries in a database are visited is specifically not promised. ---- We attempt to be as compatible with Xlib as our API differences permit. In particular, we (try to) support the same resource syntax in files and strings (eg RESOURCE_MANAGER property values), and in query strings. The Xlib doc gives this description of a resource line (BNF syntax slightly fixed up): ResourceLine = Comment | ResourceSpec Comment = "!" string | ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace value ResourceName = [Binding] ComponentName ( Binding ComponentName ) * Binding = "." | "*" WhiteSpace = ( " " | "\t" ) * ComponentName = ( "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" ) * value = string string = ( ) * Query strings must use a Binding of ., never *. Xlib actually implements something a bit more liberal: Binding = ( "." | "*" ) + which carries the semantics of * if any * is given or . if not. (I don't know why this was done; I conjecture it's to cater to the human tendency to, for example, write "app.*.font" to refer to the font resource at any level under app, what could equivalently be written "app*font".) We do likewise. . is so-called `tight' binding; * is `loose'. The difference is that loose binding can match any sequence of zero or more names; a resource spec "foo*foreground" can match a query for "foo.foreground", but it can also match "foo.widget.foreground" or "foo.commands.gutters.foreground". Matching returns the most specific match's value. The rules for this are (again, quoting from the Xlib doc): 1. The attribute of the name and class must match. For example, queries for: xterm.scrollbar.background(name) XTerm.Scrollbar.Background(class) will not match the following database entry: xterm.scrollbar:on 2. Database entries with name or class prefixed by a period (.) are more specific than those prefixed by an asterisk (*). For example, the entry xterm.geometry is more specific than the entry xterm*geometry. 3. Names are more specific than classes. For example, the entry ``*scrollbar.background'' is more specific than the entry ``*Scrollbar.Background''. 4. Specifying a name or class is more specific than omitting either. For example, the entry ``Scrollbar*Background'' is more specific than the entry ``*Background''. 5. Left components are more specific than right components. For example, ``*vt100*background'' is more specific than the entry ``*scrollbar*background'' for the query ``.vt100.scrollbar.background''. 6. If neither a period (.) nor an asterisk (*) is specified at the beginning, a period (.) is implicit. For example, ``xterm.background'' is identical to ``.xterm.background''. For compatibility's sake, we implement the same rules.