/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
#ifndef OPENISIS_H
/*
$Id: openisis.h,v 1.91 2003/06/30 09:50:45 kripke Exp $
public interface of the openisis lib.
usage samples of most calls are found in openisis.c
*/
#define OPENISIS_VERSION "0.9.0"
#ifndef __STDC__
# ifndef const
# define const
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* ************************************************************
record structure
*/
/** cooked version of an ISIS field.
* values are *NOT* terminated by a 0 byte.
*/
typedef struct OpenIsisField {
int tag;
const char *val;
int len;
} OpenIsisField;
/** cooked version of an ISIS Record.
*/
typedef struct OpenIsisRec {
int dbid;
int rowid;
int bytes; /* total avail bytes */
int used; /* total used bytes (originally BWB) */
int fields; /* avail number of fields (originally BWP) */
int base; /* byte offset of contents area (after fields) */
int len; /* used number of fields */
int state;
OpenIsisField field[1];
} OpenIsisRec;
/* compute base from fields (for both raw and cooked record) */
#define OPENISIS_BASESZ( nfields ) ((int)sizeof(int)*(8 + 3*(nfields)))
/* initialise record of f fields, n bytes */
#define OPENISIS_INITREC( r, n, f ) \
do { \
memset( r, 0, n ); \
(r)->bytes = (n); \
(r)->fields = (f); \
(r)->used = (r)->base = OPENISIS_BASESZ( (r)->fields ); \
} while (0)
/* init with std 1 field per 100 bytes */
#define OPENISIS_INITSTD( r, n ) OPENISIS_INITREC( r, n, (n)/100 )
/* init a buffer structure which has a member r */
#define OPENISIS_INITBUF( x ) OPENISIS_INITSTD( &x.r, sizeof(x) )
#define OPENISIS_CLRREC( r ) \
do { \
(r)->len = (r)->state = 0; \
(r)->used = (r)->base = OPENISIS_BASESZ( (r)->fields ); \
} while (0)
/* check some invariants (for both raw and cooked record) */
#define OPENISIS_RECOK( r ) \
( (r)->base <= (r)->used \
&& (r)->used <= (r)->bytes \
&& (r)->len <= (r)->fields \
&& (r)->base == OPENISIS_BASESZ( (r)->fields ) )
/** field description.
*/
enum { /* field types */
OPENISIS_FTX = 0, /* alphanum */
OPENISIS_FTA = 1, /* STRICTLY alpha */
OPENISIS_FTN = 2, /* numeric */
OPENISIS_FTP = 3, /* pattern */
OPENISIS_FTB = 4, /* bool */
OPENISIS_FTE = 5, /* enum */
OPENISIS_FTI = 8, /* ISO (unused) */
OPENISIS_FTT = 9, /* table (unused) */
OPENISIS_FTO = 13, /* structure: operator */
OPENISIS_FTR = 14, /* structure: record */
OPENISIS_FTS = 15, /* structure: sequence */
OPENISIS_FTV = 16, /* enum value */
OPENISIS_FTF = 16 /* subfield (unused) */
};
#define OPENISIS_FD_NAMELEN 32
typedef struct OpenIsisFd {
int id; /* tag */
char subf; /* subfield char or 0 */
char type; /* type */
char rep; /* !0 for repeated */
char slen; /* length of childs */
short len; /* max length or enum value */
char name[OPENISIS_FD_NAMELEN]; /* name, 0 terminated, max 30 */
char desc[OPENISIS_FD_NAMELEN]; /* 0 terminated description */
char *pat; /* 0 terminated pattern */
char *dflt; /* 0 terminated default value */
OpenIsisRec *info; /* additional application dependent attributes */
struct OpenIsisFd **subs; /* subfield childs */
} OpenIsisFd;
/** field description table.
*/
typedef struct {
int len;
OpenIsisFd *fd;
OpenIsisRec *rec; /* additional fields like formats, worksheets ... */
/* additional internal data like a hash might follow */
} OpenIsisFdt;
typedef struct { /* where a key has a hit */
unsigned mfn;
unsigned short tag;
unsigned short occ; /* hit is in occ'th occurence of field tag in row mfn */
unsigned short pos; /* ... as the pos'th word */
unsigned short dbn; /* for multi-db index; ignored by now */
} OpenIsisHit;
typedef struct {
unsigned char len;
unsigned char byt[23];
} OpenIsisVal;
typedef struct {
OpenIsisVal val;
unsigned char len;
unsigned char byt[255];
} OpenIsisKey;
/** Writex tags, see doc/Inverted.
Non-negative tags add (or delete) themselves.
Negative control indexing.
*/
enum {
OPENISIS_XCTL = -1, /* the "index" control field */
OPENISIS_XFST = -2, /* fst view line */
OPENISIS_XHIT = -3, /* dotted decimal hit plus val */
OPENISIS_XADD = -4, /* (prefix of) binary OpenIsisKey to add */
OPENISIS_XDEL = -5 /* (prefix of) binary OpenIsisKey to del */
};
/**
get occurence of field.
@param pos if given, the first occ starting at pos is searched
and pos is set to one after the found position (or after end).
may be used to loop all on an int var initialized to 0.
@return the field or 0
*/
extern OpenIsisField *openIsisRGet ( OpenIsisRec *r, int tag, int *pos );
extern OpenIsisField *openIsisROccurence ( OpenIsisRec *r, int tag, int occ );
/** similar to atoi/strtol, but
- string needs not be 0 terminated (unless l < 0)
- cares for hex 0x, but not octal 0.
*/
extern int openIsisA2i ( const char *p, int l );
/** similar to openIsisA2i but
- return number of parsed characters
- put result in *res
*/
extern int openIsisA2il ( const char *p, int l, int *res );
/** similar to openIsisA2i but
- return dflt if less than l characters of p has been successfully parsed
*/
extern int openIsisA2id ( const char *p, int l, int dflt );
/** print 0 terminated int.
p must have 12 bytes space
@return number of chars (up to 10 digits + minus sign)
*/
extern int openIsisI2a ( char *p, int i );
extern int openIsisU2a ( char *p, unsigned u );
/**
get field value as int as of A2i.
boolean values are recognized as 0 and 1.
if not found, return def.
*/
extern int openIsisRInt ( OpenIsisRec *r, int tag, int def, int *pos );
/**
* get field value as string from rec or default rec.
* note: def should be a non-legal value, since a def result in the
* first record causes a lookup in dflt.
*/
extern int openIsisRInt2 (OpenIsisRec *rec,
OpenIsisRec *dflt, int tag, int def);
/**
get field value as enum.
if not found, return def.
*/
extern int openIsisREnum ( OpenIsisFdt *fdt,
OpenIsisRec *r, int tag, int def, int *pos );
/**
get field value as string of length len (including 0-byte).
if not found, return 0.
*/
extern char* openIsisRString (OpenIsisRec *r,
int tag, int *pos, char *buf, int len);
/**
* get field value as string from rec or default rec.
*/
extern char* openIsisRString2 (OpenIsisRec *rec,
OpenIsisRec *dflt, int tag, char *buf, int len);
/** flatten (serialize) text record
to tag\tval lines ended by a blank line.
Text means the record is assumed to not contain
vertical tabs of any importance,
so newlines in field values are replaced with vertical tabs (^K).
buf must be of size rec->used,
wich has enough room for all values + 24bytes header
+ 12 bytes per field (ok for sign+10digits+tab).
@return # of bytes written
*/
extern int openIsisRSerialize ( char *buf, OpenIsisRec *rec );
/**
like Serialize, but newlines are turned into newline-tab.
buf must be of size 2*rec->used (unless you know you don't have newlines)
*/
extern int openIsisRSerializeBin ( char *buf, OpenIsisRec *rec );
/** alloc buffer with appropriate length, if necessary.
@param *len input: length of buf
@param *len output: length of serialized record written
to returned buffer
@return buffer, must be freed after usage if reallocated (i.e. != buf),
null on error
*/
extern char* openIsisRSerializeAlloc (OpenIsisRec *rec, char *buf, int *len);
/** flatten fully transparently,
replacing newlines with newline-tab.
extern int openIsisRBinSerialize ( char *buf, int len, OpenIsisRec *rec );
*/
/* ************************************************************
fdt
*/
/** get field description by id (tag).
*/
extern OpenIsisFd* openIsisFById ( const OpenIsisFdt *fdt, int id, int subf );
/** get field description by name.
*/
extern OpenIsisFd* openIsisFByName (const OpenIsisFdt *fdt, const char *name);
enum {
OPENISIS_NOENUM = 0x10000 /* enum NOENUM :) -- not a short */
};
/** lookup enum value for field id.
name must be exact match or unique prefix.
@return the enum value or NOENUM
*/
extern int openIsisFEnum ( OpenIsisFdt *fdt, int id, const char *name );
/** free fdt and fd structures.
@return 0
*/
extern OpenIsisFdt* openIsisFFree (OpenIsisFdt *fdt);
/** convert fdt to record.
@param fdt source
@param rec target where to append fd, may be null
@param embchld boolean indicating whether subfield childs should
be embedded in fd part of target rather than in fdt part
@return target or null if no memory available
*/
extern OpenIsisRec* openIsisFFdt2Rec (
const OpenIsisFdt *fdt, OpenIsisRec *rec, int embchld);
/** build fdt from rec.
@param rec source
@return fdt or null on error
*/
extern OpenIsisFdt* openIsisFRec2Fdt (OpenIsisRec *rec);
/* ************************************************************
session type and utilities
*/
typedef struct OpenIsisSes *OpenIsisSession;
extern void *openIsisMAlloc ( int size );
extern void openIsisMFree ( void *mem );
extern void *openIsisMDup ( const void *str, int size );
/**
convert string to html by replacing the four characters
<,>,& and ".
free the returned pointer after usage.
XML has a fifth standard entity apos,
but many HTML clients don't grok it.
*/
extern char *openIsisToHtml ( const char *str, int len );
extern int openIsisUtf8Chk ( void *mem, int len, int *tof );
/* ************************************************************
stream IO
*/
typedef struct OpenIsisIos *OpenIsisStream;
typedef int OpenIsisSFunc ( OpenIsisStream s, int op );
extern int openIsisSOpen ( const char *name, int flags, OpenIsisSFunc *type );
/**
error and loglevel codes one nibble each.
*/
enum { /* our very own errno */
OPENISIS_ERR_OK = 0, /* 0 is no error, also read(2)'s EINTR, EAGAIN */
/* errors logged at level VERBOSE */
OPENISIS_ERR_EOF = 0x10000, /* end of file */
/* errors logged at level ERROR */
OPENISIS_ERR_FAULT = 0x20000, /* NULL pointer or bad sized buffer given */
OPENISIS_ERR_INVAL = 0x30000, /* general invalid parameters, any EINVAL errno */
OPENISIS_ERR_BADF = 0x40000, /* bad file, also read(2)'s EINVAL, some of open(2) */
/* errors logged at level SYSERR */
OPENISIS_ERR_IO = 0x50000, /* real IO error, also write(2)'s ENOSPC, EPIPE */
OPENISIS_ERR_NOMEM = 0x60000, /* out of memory, also open(2)'s EMFILE, ENFILE */
OPENISIS_ERR_BUSY = 0x70000, /* object is busy */
/* errors logged at level FATAL */
OPENISIS_ERR_TRASH = 0x80000, /* database internal consistency */
OPENISIS_ERR_IDIOT = 0x90000 /* caught programming error */
};
typedef enum {
OPENISIS_LOG_OFF = 0, /* don't log anything */
OPENISIS_LOG_FATAL = 0x100000, /* fatal internal errors: we can't go on */
OPENISIS_LOG_SYSERR = 0x200000, /* problem with system ressources: bad file, no mem */
OPENISIS_LOG_IOERR = 0x300000, /* problem on IO */
OPENISIS_LOG_ERROR = 0x400000, /* unusable input, database or query */
OPENISIS_LOG_WARN = 0x500000, /* bad input */
OPENISIS_LOG_INFO = 0x600000, /* some major event like opening a db */
OPENISIS_LOG_VERBOSE = 0x700000, /* any event like reading a record */
OPENISIS_LOG_TRACE = 0x800000, /* database content (log_str) */
/* highest levels only with debug built */
OPENISIS_LOG_DEBUG = 0x900000, /* lots of processing details */
OPENISIS_LOG_ALL = 0xa00000 /* just everything, even built-in content */
} OpenIsisLogLevel;
enum {
OPENISIS_ERR_ERRORS = 10,
OPENISIS_ERR_MASK = 0xf0000, /* mask for filtering error code */
OPENISIS_ERR_SHIFT = 16, /* shift to make codes 0,1...ERR_ERRORS */
OPENISIS_LOG_NOCHANGE = -1, /* leave as is when initialising log */
OPENISIS_LOG_LEVELS = 11,
OPENISIS_LOG_MASK = 0xf00000, /* mask for filtering log levels */
OPENISIS_LOG_SHIFT = 20, /* shift to make codes 0,1...LOG_LEVELS */
/*
the following ids are provided just in case
you don't feel comfortable assuming that in,out,err are always 0,1,2 ;)
typically, you don't need any of these, 0 will work ...
*/
OPENISIS_SIN = 0, /* stream id of stdin */
OPENISIS_SOUT = 1, /* stdout */
OPENISIS_SERR = 2, /* stderr */
OPENISIS_SMASK = 0xffff, /* mask of stream id part of to */
OPENISIS_MSG_VA = 0x1000000 /* 1st parameter after fmt IS a va_list */
};
/**
printf to session's stream given by to.
The streams 0,1 and 2 of the default session are usually bound
to the system file descriptors 0,1 and 2 under unixes.
On windows, 2 is redirected to file oisiserr.txt.
0 and 1 are bound to CONIN$ and CONOUT$ for console apps,
to NULL and oisisout.txt for GUI apps.
For server sessions, 0 and 1 are connected to the client
and 2 is redirected to some per-session logging.
@param to
the lower bytes as given by SMASK specify the stream to use.
the higher bytes may specify loglevel and or error code.
if either loglevel or error code is given,
the other will be derived accordingly
and stream defaults to 2 (if stream id is 0).
otherwise, stream defaults to 1,
so you never can print to 0.
@return the negative error code
*/
extern int openIsisSMsg ( int to, const char *fmt, ... );
/**
get a string delimited by char delim (typically '\n') on stream number fd.
on success, ptr is set to the string
and it's length is returned, not including the delimiting char.
Note that there need not be a delimiting char near end of file.
If there is no string available, ptr is set to 0 (NULL).
If input might be available later, 0 is returned,
else some negative error code.
*/
extern int openIsisSGets ( int fd, char **ptr, char delim );
#define openIsisSReadln( p ) openIsisSGets( 0, p, '\n' )
typedef struct OpenIsisRecStream {
OpenIsisStream in; /* input stream to read from */
int flg; /* flags */
OpenIsisRec *buf; /* original buffer */
OpenIsisRec *rec; /* record read */
OpenIsisRec *dict; /* dictionary of field names */
} OpenIsisRecStream;
/*
stream in records in some free-style format suitable for
mails, properties, syspar ...
fields are delimited by CR or NL or CR-NL
within a field, tag stops at blank,TAB,= or :
after a TAB, val starts immediately
after a = or :, val starts at 1st character not a blank
after a blank, val starts at 1st char not one of the above separators
processing
- a field or record separator is recognized anywhere
- within each field, the tag is read up to the first separator
(any "special" char, all controls, whitespace and most punctuation).
upon reaching a separator, the tag is identified.
if it is empty, and the record has a field, now new field is created.
else, if the tag cannot be identified,
a tag of -1 is used and the tag string appended as value.
- the separator consists of any number of leading blanks,
some separator char, and, unless the sepchar is TAB, trailing blanks.
if the current field is not empty, the separator is appended as value.
- the rest of the field is appended as value
- a field with empty tag and value (blank line)
terminates the record in most modes
- a field with empty tag (continuation line)
has it's separator AND value appended to the previous field
- if either a tag or a separator extend over more than 4k,
the stream is considered over limit.
*/
enum {
OPENISIS_STOPONEMPTY = 0x10, /* stop at empty line */
OPENISIS_AUTOCLONE = 0x20, /* clone the record even if not needed */
OPENISIS_TRANSPARENT = 0x40, /* do not convert vtabs to newlines */
OPENISIS_MAIL = 0x100, /* process mail body */
OPENISIS_MBOX = 0x200, /* input stream is mbox format */
OPENISIS_DETACH = 0x400, /* detach body */
OPENISIS_CONVERT = 0x800 /* fix body parts */
};
extern int openIsisSGetr ( OpenIsisRecStream *stream );
/* ************************************************************
record utilities requiring a session
*/
/**
clone record or create empty record.
with room >= 0 and rec != NULL,
make sure that there is at least one field and room bytes available.
else, it works in shrink-to-fit mode: exactly the required bytes are used.
@param rec to extend, may be NULL
@param room number of required free bytes (<0 for shrink-to-fit)
@param discard wether the original record should be freed after extension
@return a rec with suitable room
*/
extern OpenIsisRec* openIsisRDup (
OpenIsisRec *r, int room, int discard );
/**
printf a new field of up to 1024 bytes at the end of record.
*/
extern OpenIsisRec* openIsisRMsg (
OpenIsisRec *r, int discard, int tag, const char *fmt, ... );
/**
check for free headspace in a record.
make sure that there is at least one field and room bytes available.
NOTE: this may change recp
@see openIsisRDup
*/
#define OPENISIS_RSPACE( recp, room, disc ) \
do { if ( ! (recp) \
|| (recp)->len >= (recp)->fields \
|| (recp)->bytes < (recp)->used + (room) \
) (recp) = openIsisRDup( (recp), (room), (disc) ); } while (0)
/**
add a field to a record.
a new field entry and n bytes space are provided.
if the pointer s is not NULL, n bytes are copied from it to the field.
NOTE: this may change recp
@see openIsisRDup
*/
#define OPENISIS_RADD( recp, ntag, s, n, disc ) \
do { \
int _ll = (n); \
OPENISIS_RSPACE( recp, _ll, disc ); \
if ( (recp) ) { \
OpenIsisField *_ff = (recp)->field + (recp)->len++; \
_ff->tag = (ntag); \
_ff->val = ((char*)recp)+(recp)->used; \
if ( ! s ) \
_ff->len = 0; \
else \
memcpy( (char*)_ff->val, s, _ff->len = _ll ); \
(recp)->used += _ff->len; \
} \
} while(0)
/**
append n bytes to the last field of a record.
NOTE: this may change recp
NOTE: this requires the last field to reside at the end of the buffer
@see openIsisRDup
*/
#define OPENISIS_RCAT( recp, s, n, disc ) \
do { \
OpenIsisField *_ff; \
int _ll = (n); \
assert ( (recp) ); \
assert ( (recp)->len ); \
if ( (recp)->bytes < (recp)->used + _ll ) \
(recp) = openIsisRDup( (recp), _ll, (disc) ); \
_ff = (recp)->field + (recp)->len - 1; \
memcpy( (char*)(_ff->val+_ff->len), s, _ll ); \
_ff->len += _ll; \
(recp)->used += _ll; \
} while(0)
/* like OPENISIS_RADD, but based on strlen */
#define OPENISIS_RADDS( recp, ntag, s, disc ) \
OPENISIS_RADD( recp, ntag, s, strlen(s), disc )
/* like OPENISIS_RADD, but based on field */
#define OPENISIS_RADDF( recp, f, disc ) \
OPENISIS_RADD( recp, (f)->tag, (f)->val, (f)->len, disc )
/* like OPENISIS_RCAT, but based on strlen */
#define OPENISIS_RCATS( recp, s, disc ) \
OPENISIS_RCAT( recp, s, strlen(s), disc )
/**
create a record from a field, i.e. split subfields.
if a rec buffer is given, it is filled up to it's rec->len and returned.
else an appropriately allocated rec is returned,
which must be freed after usage.
NOTE: USE WITH CARE, IT SOMEWHAT BREAKS THE GENERAL CONTRACT OF REC!
DO NOT TRY TO EXTEND THE RETURNED RECORD !!!
The rec does NOT have it's own data buffer,
but instead field values are pointers into the input field's value.
The returned rec will therefore become invalid,
if the inpur field is freed.
@param rec a rec buffer to fill or NULL
@param field some string to be split up at '^' characters
@return an OpenIsisRec or NULL, if splitting failed.
*/
extern OpenIsisRec *openIsisRSplitf (
OpenIsisRec *rec, const OpenIsisField* field );
enum { /* RSet flags */
OPENISIS_RARGC = 0xffff, /* argc mask */
OPENISIS_RFDT = 0x0010000, /* first vararg is the Fdt to use */
OPENISIS_RARGV= 0x0020000, /* next vararg is a char **argv */
OPENISIS_RDIS = 0x0040000, /* discard original record on change */
OPENISIS_RNOC = 0x0080000, /* do not recompact after CHG/DEL op */
OPENISIS_RIGN = 0x0100000, /* ignore unknown fields */
/* default op is to append fields at the end */
OPENISIS_RCHG = 0x1000000, /* change mode: overwrite previous field values */
OPENISIS_RDEL = 0x2000000, /* args is a list of tags to delete */
OPENISIS_RDFLT= 0x4000000, /* default mode: set field value only if not already present */
OPENISIS_ROP = 0xf000000 /* op mask */
};
/**
set fields in a record.
this is going to be the real field setting one-stop-shop.
This is called
EITHER with real varargs tag, val, tag, val ... 0, stopped by a 0 tag,
OR with an char **argv as vararg.
In most modes, the varargs are like tag, val, tag, val ...
Tags may be either numeric (int) or by field name.
Field names are translated to numerical ids by the Fdt,
which may be given explicitly or derived from the rec's dbid.
@param rec record to modify or 0 to create new one
@param mode bitwise OR of flags and the number of fields (2 byte)
if the number of fields is 0,
args must be zero terminated
@return the same record or a copy, depending on space needs
*/
extern OpenIsisRec *openIsisRSet ( OpenIsisRec *rec, int mode, ... );
/**
add serialized "text" to rec
supports the RDIS, STOPONEMPTY and TRANSPARENT flags
@return # of bytes read
*/
extern int openIsisRDeserialize (
OpenIsisRec **rec, const char *buf, int len, int flg );
/**
append tag with integer value to rec
*/
extern OpenIsisRec *openIsisRAddI (
OpenIsisRec *rec, int tag, int value, int discard);
enum {
OPENISIS_FMT_SHARP = 32, /* # */
OPENISIS_FMT_SLASH, /* / */
OPENISIS_FMT_PERCENT, /* % */
OPENISIS_FMT_OPEN, /* { */
OPENISIS_FMT_CLOSE, /* } */
OPENISIS_FMT_ESC, /* ! */
OPENISIS_FMT_B, /* bold */
OPENISIS_FMT_BOX, /* draw a box (0i) */
OPENISIS_FMT_BPICT, /* draw background pict (1sn) */
OPENISIS_FMT_C, /* column (1i) */
OPENISIS_FMT_CL, /* colour (1i) */
OPENISIS_FMT_COLS, /* colour table (s_) - emits a string of hex vals */
OPENISIS_FMT_FONTS, /* font table (s_) */
OPENISIS_FMT_FS, /* font size (1i) */
OPENISIS_FMT_F, /* font (1i) */
OPENISIS_FMT_I, /* italic */
OPENISIS_FMT_LINK, /* link (2s_) */
OPENISIS_FMT_LW, /* line width (1i) */
OPENISIS_FMT_M, /* indentation in twips(1in) */
OPENISIS_FMT_NC, /* new column (0i) */
OPENISIS_FMT_NEWLINE, /* set newline (1s) */
OPENISIS_FMT_NP, /* newpage (0i) */
OPENISIS_FMT_PICT, /* draw pict (1s) */
OPENISIS_FMT_QC, /* center */
OPENISIS_FMT_QJ, /* justify */
OPENISIS_FMT_TAB, /* tab (0i) */
OPENISIS_FMT_UL, /* underline */
OPENISIS_FMT_X /* blanks (1i) */
};
extern OpenIsisRec* openIsisRFmt (
OpenIsisRec *buf, const char *fmt, OpenIsisRec *r );
/* ************************************************************
db access
*/
/**
get highest rowid in db
@param dbid id returned by openIsisOpen
@return positive rowid on success, 0 if unknown, negative on error
*/
extern int openIsisDMaxId ( int dbid );
/**
read a cooked row.
free the returned pointer after usage.
@param db id returned by openIsisOpen
@param rowid the mfn
@return an OpenIsisRec or NULL, if row not available.
*/
extern OpenIsisRec *openIsisDRead ( int db, int rowid );
/**
for convenience: read and format in one.
*/
extern OpenIsisRec *openIsisDFmt (
OpenIsisRec *buf, const char *fmt, int db, int rowid );
/**
write a record and/or index entries.
If the rowid is 0, the record will be modified by assigning a new rowid.
@param db id returned by openIsisOpen
@param rec the OpenIsisRec to write
@param idx record containing index entries
@return 0 or an error code
*/
extern int openIsisDWritex ( int dbid, OpenIsisRec *rec,
OpenIsisRec *idx );
/**
write a record as with DWritex.
index entries should be derived automatically.
*/
extern int openIsisDWrite ( int dbid, OpenIsisRec *rec );
#define OPENISIS_DWRITE( rec ) openIsisDWrite( (rec)->dbid, rec )
/**
simple full scan search.
read the first row with rowid >= given rowid
and containing txt in subfield tag (any, if < 0).
free the returned pointer after usage.
@param db id returned by openIsisOpen
@param rowid the mfn
@param tag field in which to look for key, 0 for any
@return an OpenIsisRec or NULL, if row not available.
*/
extern OpenIsisRec *openIsisDScan (
int db, int rowid, int tag, const char *key );
/**
get a term for key w/ or w/o the postings.
prefix mode is determined by a trailing '$' in key.
for prefix mode, the last preset field in the record
gives a starting point, i.e. terms up to and including
it are ignored.
thus, if there are more matching terms than fit in the record,
the search can be continued by passing the same record.
@return an OpenIsisRec or NULL
*/
extern OpenIsisRec* openIsisDTerm (
OpenIsisRec *rec, int db, const char *key );
#ifndef OPENISIS_SETLEN
#define OPENISIS_SETLEN 1000
#endif
/**
record set type as returned from openIsisSearch.
first entry is the set length n, followed by rowids 1..n.
actual array length is thus n+1.
*/
typedef struct {
int len;
int id[OPENISIS_SETLEN];
} OpenIsisSet;
/* query mode */
enum {
/* search given string using index */
OPENISIS_QRY_KEYEQ, /* index scan key equal */
OPENISIS_QRY_KEYPF, /* index scan key prefix */
OPENISIS_QRY_KEYAT, /* index scan key auto (checks for '$') */
/* fulltext scan for given string */
OPENISIS_QRY_SCANE=64, /* fulltext scan for equal */
OPENISIS_QRY_SCANC, /* fulltext scan for contains */
/* parse string as query expression */
OPENISIS_QRY_SIMPLE=128, /* simple left-to-right binding */
OPENISIS_QRY_PROPER /* proper binding using precedence, () */
};
/**
query the db.
@param set set buffer to fill.
set->len must contain the actual buffer length (# of ids).
0 means default length OPENISIS_SETLEN.
@param db id returned by openIsisOpen
@param key keyword or query expression to search for
@param mode query mode, optionally |ed with a field tag<<16.
a field tag affects key and scan modes.
@param skip mfns < skip are ignored, useful to continue a query
@return the number of rowids in set (= set->len) on success, <0 on error.
*/
extern int openIsisDQuery ( OpenIsisSet *set, int db,
const char *key, int mode, int skip );
/**
callback for index loop
*/
typedef int OpenIsisDXCb ( void *me, OpenIsisKey *key, OpenIsisHit *hit );
typedef struct {
int flg;
void *me; /* commonly the session */
OpenIsisDXCb *cb;
OpenIsisKey key;
OpenIsisKey to;
} OpenIsisDXLoop;
enum { /* flags */
OPENISIS_IDXPF = 0, /* loop where from is prefix of key */
OPENISIS_IDXEQ = 1, /* loop from == key */
OPENISIS_IDXUPTO = 2, /* loop from <= key < to */
OPENISIS_IDXINCL = 3, /* loop from <= key <= to */
OPENISIS_IDXMODE = 0x03, /* mask for match mode */
OPENISIS_IDXTRAD = 0x10000 /* use traditional index */
};
/**
*/
extern int openIsisDXLoop ( int dbid, OpenIsisDXLoop *l );
/* ************************************************************
system control -- session 0 only
*/
enum {
/* system (schema 0) parameters */
OPENISIS_SPATH = 5, /* db dir */
OPENISIS_SLOGF = 700, /* logfile */
OPENISIS_SLOGV = 701, /* verbosity */
/* schema parameters */
OPENISIS_SC_NAME = 710, /* schema n name */
OPENISIS_SC_HOST = 711, /* schema n host */
OPENISIS_SC_PORT = 712, /* schema n port */
OPENISIS_SC_DFLTDB = 721, /* schema default db */
/* database parameters */
OPENISIS_DNAME = 800,
OPENISIS_DTYPE = 801,
OPENISIS_DRO = 802,
OPENISIS_DPATH = 803,
OPENISIS_DDUMP = 804,
OPENISIS_DENC = 810, /* encoding */
OPENISIS_DFDT = 811, /* path to serialized fdt */
OPENISIS_DFMT = 850, /* worksheets */
OPENISIS_DPFT = 851, /* printformats */
OPENISIS_DFST = 852, /* field selections */
/* fdt entries */
OPENISIS_FDID = 860, /* tag */
OPENISIS_FDSUB = 861, /* subfield char */
OPENISIS_FDTYPE = 862, /* field type */
OPENISIS_FDREP = 863, /* repeatable flag */
OPENISIS_FDNUMC = 864,
OPENISIS_FDLEN = 865, /* length: 0 variable, >0 maxlen, <0 fixed len */
OPENISIS_FDNAME = 866, /* field name */
OPENISIS_FDDESC = 867, /* field description */
OPENISIS_FDPAT = 868, /* pattern */
OPENISIS_FDDFLT = 869, /* default value */
OPENISIS_FDINFO = 870, /* application specific addons */
OPENISIS_FDCHLD = 871,
OPENISIS_FDT_LEN = 880, /* fdt: number of fd's */
OPENISIS_FDT_FD = 881, /* fdt: embedded fd */
OPENISIS_FDT_REC = 882, /* fdt: application specific addons */
/* communication parameters */
OPENISIS_COM_SID = 900, /* session id */
OPENISIS_COM_SER = 901, /* serial no of request */
OPENISIS_COM_DBN = 902, /* db name */
OPENISIS_COM_TMS = 903, /* server timestamp */
OPENISIS_COM_ROW = 904, /* rowid */
OPENISIS_COM_CFG = 907, /* embedded cfg */
OPENISIS_COM_REC = 908, /* embedded data record */
/* communication parameters: request */
OPENISIS_RQS_TYPE = 920, /* request type */
OPENISIS_RQS_FLG = 921, /* flags */
OPENISIS_RQS_QMOD = 922, /* mode (query) */
OPENISIS_RQS_SKIP = 923, /* skip (query) */
OPENISIS_RQS_SIZE = 924, /* output size (query) */
OPENISIS_RQS_KEY = 925, /* key value (query) */
OPENISIS_RQS_IDX = 926, /* index record (insert, update) */
/** communication parameters: response */
OPENISIS_RSP_DBID = 940, /* local db id */
OPENISIS_RSP_ERR = 941, /* error code */
OPENISIS_RSP_MSG = 942, /* error message */
OPENISIS_RSP_NUMT = 943, /* total number of records (query, read) */
OPENISIS_RSP_NUMR = 944, /* number in this response (query, read) */
OPENISIS_RSP_CERR = 945 /* client side error */
};
extern const OpenIsisFdt *openIsisFdtSyspar; /* schema 0 */
extern const OpenIsisFdt *openIsisFdtScheme; /* schema n */
extern const OpenIsisFdt *openIsisFdtDbpar; /* db cfg */
extern const OpenIsisFdt *openIsisFdtFd; /* fd rec */
extern const OpenIsisFdt *openIsisFdtFdt; /* fdt rec */
extern const OpenIsisFdt *openIsisFdtRqs; /* request rec */
extern const OpenIsisFdt *openIsisFdtRsp; /* response rec */
/** request types. */
enum {
OPENISIS_RQST_OPEN = 1, /* open db */
OPENISIS_RQST_CLOS = 3, /* close db */
OPENISIS_RQST_MNT = 4, /* mount db */
OPENISIS_RQST_LSDB = 6, /* list available dbs */
OPENISIS_RQST_MROW = 10, /* get maxrowid of db */
OPENISIS_RQST_QRY = 11, /* exec query on db */
OPENISIS_RQST_READ = 12, /* get rec for rowid */
OPENISIS_RQST_INS = 20, /* insert rec */
OPENISIS_RQST_UPD = 21, /* update rec */
OPENISIS_RQST_DEL = 22, /* delete row */
OPENISIS_RQST_EVAL = 30 /* command evaluation */
};
/** request flags. */
enum {
OPENISIS_RQSF_MNT = 0x0001, /* mount db */
OPENISIS_RQSF_QRR = 0x0002 /* fetch records on query */
};
/** max length of key in query request. */
#define OPENISIS_QRY_KEYLEN 1024
/** max length of error messages. */
#define OPENISIS_ERRMSGLEN 1024
enum {
OPENISIS_RELE = 0x0000000, /* release, unlock */
OPENISIS_LOCK = 0x1000000,
OPENISIS_WAKE = 0x2000000, /* broadcast signal, notifyall */
OPENISIS_WAIT = 0x3000000,
OPENISIS_COND = 0x0ffffff /* condition mask */
};
/** a function to provide a global lock in a multi threaded process.
@param lock coded action.
the high byte is one of release, lock, wake and wait.
the lower bytes indicate a condition for wake and wait.
the value may be hashed to a small set of conditions,
typically eight or one (ignore).
@return 0 on success
sample with a single condition:
int myOpenIsisLockFunc ( int lock )
{
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
switch ( OPENISIS_WAIT & lock ) {
case OPENISIS_RELE: return pthread_mutex_unlock( &mut );
case OPENISIS_LOCK: return pthread_mutex_lock( &mut );
case OPENISIS_WAKE: return pthread_cond_broadcast( &cond );
case OPENISIS_WAIT: return pthread_cond_wait( &cond, &mut );
}
return -1;
}
*/
typedef int OpenIsisCLockFunc ( int lock );
/** initialize system with some common parameters and a lock func.
@param lock a function to provide a global lock in a multi threaded process.
*/
extern int openIsisCInit ( int argc, const char **argv,
OpenIsisCLockFunc lock );
/**
set logging level and device.
@param level the logging level
may be given numerical or as ascii char in -ofsewivtda for
nochange(-1), off (0), fatal (1), syserr, error, warn, info, verbose,
trace, debug, all(9)
default is set to error
@param filename specifies, if not NULL, a file to append log to
*/
extern void openIsisCLog ( int level, const char *filename );
/** get the default session, possibly adding args.
only one (session master) thread in a process may call this.
@param args initialisation properties (cloned to prop)
*/
extern OpenIsisSession openIsisCOpen ( OpenIsisRec *args );
/** get a new session.
only one (session master) thread in a process may call this.
@param args initialisation properties (cloned to prop)
*/
extern OpenIsisSession openIsisCSession ( OpenIsisRec *args );
/* ************************************************************
schema
*/
#define OPENISIS_SC_NAMELEN 32 /* max length of schema identification */
#define OPENISIS_DB_NAMELEN 32 /* max length of db id relative to schema */
#define OPENISIS_SCIDMSK 0x0ff00 /* schema part of id */
#define OPENISIS_DBIDMSK 0x000ff /* db part of id */
typedef struct {
char name[OPENISIS_DB_NAMELEN];
int dbid;
int mntcnt; /* number of sessions currently using this */
int tms; /* last (re)opening timestamp */
OpenIsisRec *cfg;
OpenIsisFdt *fdt;
} OpenIsisDb;
typedef struct {
char name[OPENISIS_SC_NAMELEN];
int scid;
int mntcnt;
OpenIsisRec *cfg;
int ndbs;
OpenIsisDb **dbs;
} OpenIsisSchema;
/* ************************************************************
client side
*/
typedef struct OpenIsisStb *OpenIsisStub;
/** function that sends request record rqs to remote address of stb
*/
typedef int OpenIsisStbRqsProc (OpenIsisStub stb, OpenIsisRec *rqs);
/** callback for freeing external resources when a stub is deleted.
@param client_data client_data registered in openIsisNOpen
*/
typedef void OpenIsisStbDelProc (OpenIsisStub stb, void *client_data);
/** deprecated
*/
typedef int OpenIsisRspCb (void *client_data,
OpenIsisStub stub, OpenIsisRec *response, OpenIsisDb *db);
typedef struct {
/* function for request sending.
if 0, defaults to internal blocking sender
*/
int (*sendproc) (OpenIsisStub stub, OpenIsisRec *rqs);
/* default response callback */
OpenIsisRspCb *dfltcb;
/* delete callback: cleanup client data cbd that is no longer
used. if 0==cbd, session is going to be deleted.
*/
void (*delcb) (void *cld, OpenIsisStub session, void *cbd);
/* client data for dfltcb */
void *dfltcld;
/* client data for delcb */
void *delcld;
} OpenIsisStubCbData;
extern OpenIsisStub openisis_stub0;
/** create stub for local schema.
*/
extern OpenIsisStub openIsisNInit (
int argc, const char **argv, OpenIsisStubCbData *dta
);
/** destroy local schema.
*/
extern void openIsisNDeinit ();
/** create a new stub.
*/
extern OpenIsisStub openIsisNOpen (
const char *name, int argc, const char **argv, OpenIsisStubCbData *dta
);
/** destroy a stub.
*/
extern void openIsisNClose (OpenIsisStub stub);
/** get schema from stub.
*/
extern OpenIsisSchema* openIsisNSchema (OpenIsisStub stub);
/** send request.
SID,SER,TMS are set in this call.
Note: rqs (or the internal copy) will be freed at the next call
to openIsisNClean or openIsisNSend.
@param stub communication partner
@param rqs request parameter
@param actcb optional async callback for this request
@param actcld optional client data for actcb
@param rdup make a internal copy of rqs
@return 0 on success
*/
extern int openIsisNSend (
OpenIsisStub stub, OpenIsisRec *rqs,
OpenIsisRspCb *actcb, void *actcld, int rdup
);
/** get stub's response record.
@param dbid optional db id of embedded result records
*/
extern OpenIsisRec *openIsisNRecv (OpenIsisStub stub, OpenIsisDb **db);
/** clean request, response, session used in last openIsisNSend call.
*/
extern void openIsisNClean (OpenIsisStub stub);
/** util: unwrap embbeded response records.
*/
extern int openIsisNGetResult (OpenIsisStub stub,
int **rowids, OpenIsisRec ***recs, OpenIsisDb **db, int *total);
/** Notification of a successfully received response record.
Interface for applications that handle asynchronous communications
with a remote server.
@param rsp address of received response record
*/
extern void openIsisNNotifyResponse (OpenIsisStub stub, OpenIsisRec **rsp);
/** Error notification when server communication breaks down.
Interface for applications that handle asynchronous communications
with a remote server.
@param errcode reason of failure
*/
extern void openIsisNNotifyError (OpenIsisStub stub, int errcode);
/** get communication channel from stub.
*/
extern struct CliChnl* openIsisNGetChannel (OpenIsisStub stub);
/** get db by name.
*/
extern OpenIsisDb *openIsisNDbByName (OpenIsisStub stub, const char *dbname);
/** get db by id.
*/
extern OpenIsisDb *openIsisNDbById (int dbid);
/* ************************************************************
db control
*/
/**
open an isis database.
The following options are supported as text-style parameters:
<ul>
<li>db <name><br>
basename of database. same as param dbname.
</li>
<li>dbpath <path><br>
path to database. usefull when opening secondary indexes
or other files whose names are not based on the db basename.
</li>
<li>v <level><br>
set verbosity level
</li>
</ul>
@param dbname identification name of database.
the dbpath arg, if given, will be prepended, and the ISIS file
extensions appended to build the actual filename.
dbname may be NULL, if argv includes a db arg.
@param argv array of names and values.
May be NULL.
argv contains parameter names, which may be prefixed
by a dash '-'. See the syspar and dbpar fdt above for
supported parameter names.
Unrecognized parameter names are ignored.
@param argc length of argv.
You may supply -1 to indicate that argv is NULL terminated.
@param syspar optional syspar record
@param dbpar optional dbpar record, a clone will be saved in the
db header cfg rec
@param fdt optional fdt of this db, the reference will be saved
in the db header fdt, so fdt must *NOT* be freed after call
@return db structure or 0 on error
*/
extern OpenIsisDb* openIsisCDOpen ( const char *dbname,
OpenIsisRec *dbpar, OpenIsisRec *syspar, OpenIsisFdt *fdt );
/**
open by vector
@return some non-negative dbid on success,
some negative errorcode on error.
Errorcodes include -ENOENT, -ENOMEM, -EACCESS and others
returned by file access functions.
*/
extern int openIsisCDOpenv ( const char *dbname, const char **argv, int argc );
enum {
OPENISIS_CHK_ONLY = 0x01, /* check only, do not fix */
OPENISIS_CHK_XRF = 0x02, /* check the xrf */
OPENISIS_CHK_FIX = 0xfe, /* check all and fix */
OPENISIS_CHK_ALL = 0xff /* check all, no fix */
};
/** check and optionally fix the database.
*/
extern int openIsisCDCheck ( int dbid, int flags );
/**
close a database.
@param dbid id returned by openIsisOpen
@return 0 on success, some negative errorcode else.
*/
extern int openIsisCDClose ( int dbid );
/* ************************************************************
low level index access
DO NOT USE unless you know what you're doing !!!!!!!!!
*/
typedef struct OpenIsisIdx *OpenIsisIndex;
/**
get the db's index for loading.
the index may not be access until the loading session is finished.
@param mode lowest byte give percent free for index
*/
extern OpenIsisIndex openIsisCXOpen ( int dbid, int mode );
/**
load a sorted series of keys and hits into index.
@param key may be NULL to append to previous key.
@param hit a value of NULL terminates the loading session
*/
extern int openIsisCXAdd ( OpenIsisIndex idx,
OpenIsisKey *key, OpenIsisHit *hit );
#define openIsisCXClose( idx ) openIsisCXAdd( idx, 0, 0 )
#ifdef _REENTRANT
extern int openisis_threaded;
#endif
/* ************************************************************
single session and legacy mode pre 0.9
*/
/** raw version of an ISIS field.
* off gives an offset from the start of the record taken as char*.
* values are *NOT* terminated by a 0 byte.
*/
typedef struct {
/*
although isis-1 dbs have only 2 bytes for field tags (numbers),
we use ints with the lower 3 bytes for the tag.
The highest byte may have special usage in some context.
*/
int tag;
int off;
int len;
} OpenIsisFld;
/** raw version of an ISIS Record. rarely used.
*/
typedef struct {
int dbid;
int rowid;
int bytes; /* total avail bytes */
int used; /* total used bytes (originally BWB) */
int fields; /* avail number of fields (originally BWP) */
int base; /* byte offset of contents area (after fields) */
int len; /* used number of fields */
int state;
OpenIsisFld field[1];
} OpenIsisRaw;
/** read raw. */
extern OpenIsisRaw *openIsisDRaw ( int db, int rowid );
#ifndef OPENISIS_NOPRE09
/* utilities */
#define openIsis2Html( str, len ) \
openIsisToHtml ( str, len )
#define openIsisValidUTF8( mem, len, tof ) \
openIsisUtf8Chk ( mem, len, tof )
/* record tools */
#define openIsisClone( r, ro, d ) \
openIsisRDup( r, ro, d )
/* GNUC only, and waaarns ...
#define openIsisPrintf( r, d, t, f, a... ) \
openIsisRMsg( r, d, t, f, ## a )
*/
#define OPENISIS_CHKFREE( recp, room, disc ) \
OPENISIS_RSPACE( recp, room, disc )
#define OPENISIS_ADD( recp, ntag, s, n, disc ) \
OPENISIS_RADD( recp, ntag, s, n, disc )
#define OPENISIS_CAT( recp, s, n, disc ) \
OPENISIS_RCAT( recp, s, n, disc )
#define OPENISIS_SADD( recp, ntag, s, disc ) \
OPENISIS_RADDS( recp, ntag, s, disc )
#define OPENISIS_SCAT( recp, s, disc ) \
OPENISIS_RCATS( recp, s, disc )
#define openIsisReadField( rec, f ) \
openIsisRSplitf( rec, f )
#define openIsisFmt( buf, fmt, r ) \
openIsisRFmt( buf, fmt, r )
#define openIsisReadStream( stream ) \
openIsisSGetr( stream )
/* database */
#define openIsisMaxRowid( db ) \
openIsisDMaxId( db )
#define openIsisReadRow( db, rowid ) \
openIsisDRead( db, rowid )
#define openIsisReadRaw( db, rowid ) \
openIsisDRaw( db, rowid )
#define openIsisRead( buf, fmt, db, rowid ) \
openIsisDFmt( buf, fmt, db, rowid )
#define openIsisWrite( db, r ) \
openIsisDWrite( db, r )
#define openIsisWritex( db, r, i ) \
openIsisDWritex( db, r, i )
#define openIsisScan( db, rowid, tag, key ) \
openIsisDScan( db, rowid, tag, key )
#define openIsisTerm( rec, db, key ) \
openIsisDTerm( rec, db, key )
#define openIsisQuery( set, db, key, mode, skip ) \
openIsisDQuery( set, db, key, mode, skip )
/* database index */
#define OpenIsisIdxCb OpenIsisDXCb
#define OpenIsisIdxLoop OpenIsisDXLoop
#define openIsisIdxLoop( dbis, cb ) \
openIsisDXLoop( dbis, cb )
/* system control (main session only) */
#define openIsisLog openIsisCLog
#define OpenIsisLockFunc OpenIsisCLockFunc
#define openIsisInit( argc, argv, lock ) \
openIsisCInit( argc, argv, lock )
#define openIsisSesGet( id, args ) \
(id) ? openIsisCSession( args ) : openIsisCOpen( args )
/* database control (main session only) */
#define openIsisOpen openIsisCDOpenv
#define openIsisCheck openIsisCDCheck
#define openIsisClose openIsisCDClose
/* database index control (main session only) */
#define openIsisIdxOpen openIsisCXOpen
#define openIsisIdxAdd openIsisCXAdd
#define openIsisIdxDone( idx ) openIsisCXClose( idx )
#endif /* OPENISIS_NOPRE09 */
#ifdef __cplusplus
}
#endif /* extern "C" */
#define OPENISIS_H
#endif /* OPENISIS_H */