#include <stdio.h> #include <stdarg.h> #include <string.h> #include <time.h> #include <stdlib.h> #ifdef WINDOWS #include <windows.h> #endif #include <mysql.h> #include "xmlapi.h" #include "xmlobj.h" #include "../wftk.h" #include "../wftk_internals.h" XML * wftk_sql_build_select (XML * list, char * key, WFTK_SQL_FORCE_MAPPING force); XML * wftk_sql_build_insert (XML * list, XML * object, WFTK_SQL_FORCE_MAPPING force); XML * wftk_sql_build_update (XML * list, XML * object, WFTK_SQL_FORCE_MAPPING force); XML * wftk_sql_build_delete (XML * list, char * key, WFTK_SQL_FORCE_MAPPING force); static void _LIST_mysql_force_mapping (XML * field); |
#ifdef WINDOWS
#define _LIST_mysql_strlwr(x) strlwr(x)
#else
void _LIST_mysql_strlwr (char * str) {
if (!str) return;
while (*str) {
if (*str >= 'A' && *str <= 'Z') {
*str += 'a' - 'A';
}
str++;
}
}
#endif
|
adaptor_info structure is used to pass adaptor info (duh) back to the
config module when it's building an adaptor instance. Here's what it contains:
static char *names[] =
{
"init",
"free",
"info",
"create",
"destroy",
"add",
"update",
"delete",
"get",
"query",
"first",
"next",
"rewind",
"prev",
"last",
"attach_open",
"attach_write",
"attach_close",
"attach_cancel",
"retrieve_open",
"retrieve_read",
"retrieve_close"
};
XML * LIST_mysql_init (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_free (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_info (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_create (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_destroy (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_add (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_update (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_delete (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_get (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_query (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_first (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_next (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_rewind (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_prev (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_last (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_attach_open (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_attach_write (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_attach_close (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_attach_cancel (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_retrieve_open (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_retrieve_read (WFTK_ADAPTOR * ad, va_list args);
XML * LIST_mysql_retrieve_close (WFTK_ADAPTOR * ad, va_list args);
static WFTK_API_FUNC vtab[] =
{
LIST_mysql_init,
LIST_mysql_free,
LIST_mysql_info,
LIST_mysql_create,
LIST_mysql_destroy,
LIST_mysql_add,
LIST_mysql_update,
LIST_mysql_delete,
LIST_mysql_get,
LIST_mysql_query,
LIST_mysql_first,
LIST_mysql_next,
LIST_mysql_rewind,
LIST_mysql_prev,
LIST_mysql_last,
LIST_mysql_attach_open,
LIST_mysql_attach_write,
LIST_mysql_attach_close,
LIST_mysql_attach_cancel,
LIST_mysql_retrieve_open,
LIST_mysql_retrieve_read,
LIST_mysql_retrieve_close
};
static struct wftk_adaptor_info _LIST_mysql_info =
{
22,
names,
vtab
};
|
struct wftk_adaptor_info * LIST_mysql_get_info ()
{
return & _LIST_mysql_info;
}
|
struct _LIST_mysql_handles {
MYSQL * mysql;
int ok;
};
void LIST_mysql_cleanup (struct _LIST_mysql_handles *handles)
{
if (handles) {
mysql_close (handles->mysql);
free (handles);
}
}
XML * LIST_mysql_init (WFTK_ADAPTOR * ad, va_list args)
{
const char * parms;
char * mark;
XML * repository = (XML *) ad->session;
XML * conn;
struct _LIST_mysql_handles *handles;
int rslt;
char buf[1024];
parms = xml_attrval (ad->parms, "parm");
if (!*parms) {
xml_set (ad->parms, "error", "No MySQL datasource specified");
return NULL;
}
/* Find or create connection. */
xml_setf (repository, "scratch", "mysql:%s", parms);
conn = xml_search (repository, "connection", "storage", xml_attrval (repository, "scratch"));
if (!conn) {
conn = xml_create ("connection");
xml_set (conn, "storage", xml_attrval (repository, "scratch"));
xml_set (conn, "database", parms); /* Default, if no predefined connection: parms name local database. */
xml_append (repository, conn);
}
/* Is connection already made? If not, connect. */
handles = (struct _LIST_mysql_handles *) xml_getbin (conn);
if (!handles) {
handles = (struct _LIST_mysql_handles *) malloc (sizeof (struct _LIST_mysql_handles));
if (!handles) {
xml_set (ad->parms, "error", "Unable to allocate binary stash.");
return NULL;
}
handles->mysql = NULL;
handles->ok = 0;
}
ad->bindata = (void *) handles;
if (!handles->ok) {
handles->mysql = mysql_init (NULL);
if (!(handles->mysql)) {
xml_set (ad->parms, "error", "Unable to allocate MySQL environment handle.");
free ((void *) handles);
xml_setbin (conn, NULL, NULL);
ad->bindata = 0;
return (XML *) 0;
}
if (!mysql_real_connect (handles->mysql,
*xml_attrval (conn, "host") ? xml_attrval (conn, "host") : "localhost",
*xml_attrval (conn, "user") ? xml_attrval (conn, "user") : NULL,
*xml_attrval (conn, "password") ? xml_attrval (conn, "password") : NULL,
*xml_attrval (conn, "database") ? xml_attrval (conn, "database") : NULL,
*xml_attrval (conn, "port") ? atoi (xml_attrval (conn, "port")): MYSQL_PORT,
NULL, CLIENT_FOUND_ROWS)) {
xml_setf (ad->parms, "error", "Unable to connect to database specified: %s", mysql_error (handles->mysql));
mysql_close (handles->mysql);
free ((void *) handles);
xml_setbin (conn, NULL, NULL);
ad->bindata = 0;
return (XML *) 0;
}
handles->ok = 1;
xml_setbin (conn, handles, (XML_SETBIN_FREE_FN *)LIST_mysql_cleanup);
}
return (XML *) 0;
}
XML * LIST_mysql_free (WFTK_ADAPTOR * ad, va_list args) { return (XML *) 0; }
|
XML * LIST_mysql_info (WFTK_ADAPTOR * ad, va_list args) {
XML * info;
info = xml_create ("info");
xml_set (info, "type", "list");
xml_set (info, "name", "mysql");
xml_set (info, "ver", "1.0.0");
xml_setnum (info, "mysql_ver", MYSQL_VERSION_ID);
xml_set (info, "compiled", __TIME__ " " __DATE__);
xml_set (info, "author", "Michael Roberts");
xml_set (info, "contact", "wftk@vivtek.com");
xml_set (info, "extra_functions", "0");
return (info);
}
|
static void _LIST_mysql_builderrmsg (XML * ap, MYSQL * mysql)
{
xml_setf (ap, "_error", "%s[%d] %s", xml_attrval (ap, "error"), mysql_errno (mysql), mysql_error (mysql));
xml_set (ap, "error", xml_attrval (ap, "_error"));
xml_set (ap, "_error", "");
}
static void sql_escaped_append (XML * s, const char * q, const char * v)
{
char * mark;
if (!v) return;
while (mark = strchr (v, '\'')) {
xml_attrncat (s, q, v, mark - v);
xml_attrcat (s, q, "'");
v = mark + 1;
}
xml_attrcat (s, q, v);
}
|
static XML * _LIST_mysql_add (WFTK_ADAPTOR * ad, XML * list, XML * object)
{
struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata;
XML * rest_xml = NULL;
if (!handles) {
xml_set (ad->parms, "error", "Database connection not initialized.");
return 0;
}
if (!handles->ok) {
xml_set (ad->parms, "error", "Database connection not initialized.");
return 0;
}
xml_set (list, "_now", "NOW()");
xml_set (list, "_blob", "NULL");
xml_set (list, "_keygen", "");
xml_set (list, "_keycol", "");
rest_xml = wftk_sql_build_insert (list, object, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping);
/* Now execute the query we just built. */
(ad->log) (ad->session, ad, 5, xml_attrval (list, "_insert"));
if (mysql_query (handles->mysql, xml_attrval (list, "_insert"))) {
xml_setf (ad->parms, "error", "SQL error from [%s] ", xml_attrval (list, "_insert"));
_LIST_mysql_builderrmsg (ad->parms, handles->mysql);
} else {
if (*xml_attrval (list, "_keycol")) {
xml_setnum (list, "_newkey", (int)mysql_insert_id(handles->mysql));
xmlobj_set (object, NULL, xml_attrval (list, "_keycol"), xml_attrval (list, "_newkey"));
xml_unset (list, "_newkey");
}
}
/* TODO: handle links. */
/* TODO: BLOB/rest_xml stuff. */
xml_free (rest_xml);
return NULL;
}
XML * LIST_mysql_add (WFTK_ADAPTOR * ad, va_list args) {
XML * list = NULL;
XML * obj = NULL;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
obj = va_arg (args, XML *);
if (!obj) {
xml_set (ad->parms, "error", "No object given.");
return (XML *) 0;
}
return _LIST_mysql_add (ad, list, obj);
}
|
static void _LIST_mysql_force_mapping (XML * field)
{
const char * id;
if (*xml_attrval (field, "alias")) return;
/* TODO: encode reserved words. */
if (!strncmp (xml_attrval (field, "storage"), "record:", 7)) {
xml_set (field, "alias", xml_attrval (field, "storage") + 7);
}
if (!*xml_attrval (field, "alias")) xml_set (field, "alias", xml_attrval (field, "id"));
}
|
static XML * _LIST_mysql_update (WFTK_ADAPTOR * ad, XML * list, XML * object)
{
struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata;
XML * rest_xml;
if (!handles) {
xml_set (ad->parms, "error", "Database connection not initialized.");
return 0;
}
if (!handles->ok) {
xml_set (ad->parms, "error", "Database connection not initialized.");
return 0;
}
xml_set (list, "_now", "NOW()");
xml_set (list, "_blob", "NULL");
xml_set (list, "_keygen", "");
rest_xml = wftk_sql_build_update (list, object, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping);
/* Now execute the query we just built. */
(ad->log) (ad->session, ad, 5, xml_attrval (list, "_update"));
if (mysql_query (handles->mysql, xml_attrval (list, "_update"))) {
xml_setf (ad->parms, "error", "SQL error from [%s] ", xml_attrval (list, "_update"));
_LIST_mysql_builderrmsg (ad->parms, handles->mysql);
} else {
if (!xml_is (object, "diff")) { /* Note: we only allow an insertion for a complete record, not a diff. */
if (mysql_affected_rows (handles->mysql) == 0) {
xml_free (rest_xml);
return _LIST_mysql_add (ad, list, object);
}
}
}
xml_free (rest_xml);
return 0;
}
XML * LIST_mysql_update (WFTK_ADAPTOR * ad, va_list args) {
XML * list;
XML * obj;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
obj = va_arg (args, XML *);
if (!obj) {
xml_set (ad->parms, "error", "No object given.");
return (XML *) 0;
}
return _LIST_mysql_update (ad, list, obj);
}
static XML * _LIST_mysql_delete (WFTK_ADAPTOR * ad, XML * list, char * key)
{
struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata;
if (!handles) {
xml_set (ad->parms, "error", "Database connection not initialized.");
return 0;
}
if (!handles->ok) {
xml_set (ad->parms, "error", "Database connection not initialized.");
return 0;
}
wftk_sql_build_delete (list, key, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping);
/* Now execute the query we just built. */
(ad->log) (ad->session, ad, 5, xml_attrval (list, "_delete"));
if (mysql_query (handles->mysql, xml_attrval (list, "_delete"))) {
xml_setf (ad->parms, "error", "SQL error from [%s] ", xml_attrval (list, "_delete"));
_LIST_mysql_builderrmsg (ad->parms, handles->mysql);
}
return 0;
}
XML * LIST_mysql_delete (WFTK_ADAPTOR * ad, va_list args) {
XML * list;
char * key;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
key = va_arg (args, char *);
if (!key) {
xml_set (ad->parms, "error", "No key given.");
return (XML *) 0;
}
return _LIST_mysql_delete (ad, list, key);
}
|
static void _LIST_mysql_select (WFTK_ADAPTOR * ad, XML * list, char * key) {
struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata;
XML * scratch;
XML * field;
char * value;
XML * mark;
MYSQL_RES * result;
if (!handles) return;
if (!handles->ok) return;
wftk_sql_build_select (list, key, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping);
/* Execute the query. */
(ad->log) (ad->session, ad, 5, xml_attrval (list, "_select"));
if (mysql_query (handles->mysql, xml_attrval (list, "_select"))) {
xml_set (ad->parms, "error", "SQL error selecting data ");
xml_set (list, "_status", "error");
_LIST_mysql_builderrmsg (ad->parms, handles->mysql);
} else {
result = mysql_store_result (handles->mysql);
xml_set (list, "_status", "ok");
xml_setnum (list, "_numcols", mysql_field_count (handles->mysql));
xml_setbin (list, result, (XML_SETBIN_FREE_FN *)mysql_free_result);
}
}
|
static XML * _LIST_mysql_retrieve (WFTK_ADAPTOR * ad, XML * list, int save_row) {
struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata;
int numcols;
int col;
long value_len;
MYSQL_RES * result;
MYSQL_ROW row;
MYSQL_FIELD * fields;
XML * ret;
char column[256];
char value[1024];
XML * field;
if (!handles) return (XML *) 0;
if (!handles->ok) return (XML *) 0;
result = xml_getbin (list);
if (!result) return (XML *) 0;
/* Retrieve a row; if none, free the statement and return NULL. */
row = mysql_fetch_row (result);
if (!row) {
xml_set (list, "_status", "complete");
mysql_free_result (result);
xml_setbin (list, NULL, NULL);
return (XML *) 0;
}
fields = mysql_fetch_fields (result);
numcols = xml_attrvalnum (list, "_numcols");
ret = xml_create (*xml_attrval (list, "element") ? xml_attrval (list, "element") : "record");
if (save_row) xml_append (list, ret);
for (col=0; col < numcols; col++) {
strcpy (column, fields[col].name);
_LIST_mysql_strlwr (column);
field = xml_search (list, "field", "id", column);
if (field) {
xmlobj_set (ret, list, xml_attrval (field, "id"), row[col]);
}
}
xml_set_nodup (ret, "id", xmlobj_getkey (ret, list));
return ret;
}
|
XML * LIST_mysql_query (WFTK_ADAPTOR * ad, va_list args)
{
XML * list;
int count = 0;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
_LIST_mysql_select (ad, list, NULL); /* NULL = no specific key. */
/* Retrieve all results. */
while (_LIST_mysql_retrieve (ad, list, 1)) { /* 1 = save row in list. */
count ++;
}
xml_setnum (list, "count", count);
return list;
}
|
XML * LIST_mysql_first (WFTK_ADAPTOR * ad, va_list args) {
XML * list;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
_LIST_mysql_select (ad, list, NULL);
return _LIST_mysql_retrieve (ad, list, 0);
}
XML * LIST_mysql_next (WFTK_ADAPTOR * ad, va_list args) {
XML * list;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
return _LIST_mysql_retrieve (ad, list, 0);
}
XML * LIST_mysql_rewind (WFTK_ADAPTOR * ad, va_list args) {
xml_set (ad->parms, "error", "'rewind' command not supported under mysql");
}
XML * LIST_mysql_prev (WFTK_ADAPTOR * ad, va_list args)
{
xml_set (ad->parms, "error", "'prev' command not supported under mysql");
}
XML * LIST_mysql_last (WFTK_ADAPTOR * ad, va_list args)
{
xml_set (ad->parms, "error", "'last' command not supported under mysql");
}
|
XML * LIST_mysql_get (WFTK_ADAPTOR * ad, va_list args)
{
XML * list;
char * key;
XML * copy;
XML * ret;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
key = va_arg (args, char *);
copy = xml_copy (list);
_LIST_mysql_select (ad, copy, key);
ret = _LIST_mysql_retrieve (ad, copy, 0);
xml_free (copy);
return ret;
}
|
XML * LIST_mysql_create (WFTK_ADAPTOR * ad, va_list args)
{
return NULL;
}
XML * LIST_mysql_destroy (WFTK_ADAPTOR * ad, va_list args)
{
return NULL;
}
|
XML * LIST_mysql_attach_open (WFTK_ADAPTOR * ad, va_list args) {
XML * list;
char * key;
char * field;
char * filename;
XML * mark;
XML * ret;
FILE * file;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list descriptor given.");
return (XML *) 0;
}
key = va_arg (args, char *);
field = va_arg (args, char *);
filename = va_arg (args, char *);
/* If we're not given a fieldname, then we'll just scan the list definition to find the first "document"-type field. */
if (!field) {
mark = xml_search (list, "field", "type", "document");
if (!mark) {
xml_set (ad->parms, "error", "No attachment field given and no default exists.");
return NULL;
}
field = (char *) xml_attrval (mark, "id");
}
ret = xml_create ("attachment-handle");
xml_set (ret, "dir", xml_attrval (ad->parms, "basedir"));
if (!*xml_attrval (ad->parms, "subdir")) xml_set (ad->parms, "subdir", xml_attrval (list, "id"));
if (strcmp (xml_attrval (ad->parms, "subdir"), ".")) {
xml_attrcat (ret, "dir", xml_attrval (ad->parms, "subdir"));
xml_attrcat (ret, "dir", "/");
}
xml_setf (ret, "adaptor", "mysql:%s", xml_attrval (ret, "dir"));
/* If we're supplied with a filename, then that file can't already exist in our controlled
directory. */
if (filename && *filename) {
xml_setf (ret, "location", filename);
xml_setf (ret, "file", "%s%s", xml_attrval (ret, "dir"), filename);
xml_set (ret, "tempfile", xml_attrval (ret, "file"));
} else {
xml_setf (ret, "location", "_att_%s_%s.dat", key, field);
xml_setf (ret, "file", "%s%s", xml_attrval (ret, "dir"), xml_attrval (ret, "location"));
xml_setf (ret, "tempfile", "%s_newatt_%s_%s.dat", xml_attrval (ret, "dir"), key, field);
}
file = fopen (xml_attrval (ret, "file"), "w");
if (!file) {
xml_setf (ad->parms, "error", "Unable to open file %s for writing.", xml_attrval (ret, "file"));
xml_free (ret);
return NULL;
}
xml_setbin (ret, file, (XML_SETBIN_FREE_FN *)fclose);
xml_set (ret, "content-type", "text/plain");
return (ret);
}
|
XML * LIST_mysql_attach_write (WFTK_ADAPTOR * ad, va_list args) {
void * buffer;
int size, number;
XML * handle;
if (!args) {
xml_set (ad->parms, "error", "No arguments given.");
return NULL;
}
buffer = va_arg (args, void *);
size = va_arg (args, int);
number = va_arg (args, int);
handle = va_arg (args, XML *);
xml_setnum (handle, "last-write", fwrite (buffer, size, number, xml_getbin(handle)));
return NULL;
}
XML * LIST_mysql_attach_cancel (WFTK_ADAPTOR * ad, va_list args) {
XML * handle;
if (!args) {
xml_set (ad->parms, "error", "No arguments given.");
return NULL;
}
handle = va_arg (args, XML *);
fclose (xml_getbin (handle));
unlink (xml_attrval (handle, "tempfile"));
return NULL;
}
XML * LIST_mysql_attach_close (WFTK_ADAPTOR * ad, va_list args) {
XML * handle;
if (!args) {
xml_set (ad->parms, "error", "No arguments given.");
return NULL;
}
handle = va_arg (args, XML *);
fclose (xml_getbin (handle));
rename (xml_attrval (handle, "tempfile"), xml_attrval (handle, "file"));
return NULL;
}
|
XML * LIST_mysql_retrieve_open (WFTK_ADAPTOR * ad, va_list args) {
XML * list = NULL;
XML * fld;
char * key;
char * field;
XML * mark;
XML * ret;
FILE * file;
WFTK_ADAPTOR * ad2;
if (args) list = va_arg (args, XML *);
if (!list) {
xml_set (ad->parms, "error", "No list given.");
return (XML *) 0;
}
key = va_arg (args, char *);
fld = va_arg (args, XML *);
ret = xml_create ("attachment-handle");
xml_set (ret, "dir", xml_attrval (ad->parms, "basedir"));
if (!*xml_attrval (ad->parms, "subdir")) xml_set (ad->parms, "subdir", xml_attrval (list, "id"));
if (strcmp (xml_attrval (ad->parms, "subdir"), ".")) {
xml_attrcat (ret, "dir", xml_attrval (ad->parms, "subdir"));
xml_attrcat (ret, "dir", "/");
}
xml_setf (ret, "adaptor", "mysql:%s", xml_attrval (ret, "dir"));
if (fld) {
xml_set (ret, "location", xml_attrval (fld, "location"));
}
if (!*xml_attrval (ret, "location")) xml_setf (ret, "location", "_att_%s_%s.dat", key, xml_attrval (fld, "id"));
xml_setf (ret, "file", "%s%s", xml_attrval (ret, "dir"), xml_attrval (ret, "location"));
file = fopen (xml_attrval (ret, "file"), "r");
if (!file) {
xml_setf (ad->parms, "error", "Unable to open file %s for reading.", xml_attrval (ret, "location"));
xml_free (ret);
return NULL;
}
xml_setbin (ret, file, (XML_SETBIN_FREE_FN *)fclose);
xml_set (ret, "content-type", "text/plain");
return (ret);
}
XML * LIST_mysql_retrieve_read (WFTK_ADAPTOR * ad, va_list args) {
void * buffer;
int size, number;
XML * handle;
if (!args) {
xml_set (ad->parms, "error", "No arguments given.");
return NULL;
}
buffer = va_arg (args, void *);
size = va_arg (args, int);
number = va_arg (args, int);
handle = va_arg (args, XML *);
xml_setnum (handle, "last-read", fread (buffer, size, number, xml_getbin(handle)));
return NULL;
}
XML * LIST_mysql_retrieve_close (WFTK_ADAPTOR * ad, va_list args) {
XML * handle;
if (!args) {
xml_set (ad->parms, "error", "No arguments given.");
return NULL;
}
handle = va_arg (args, XML *);
fclose (xml_getbin (handle));
return NULL;
}
|
| This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2002-2004, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. This presentation was prepared with LPML. Try literate programming. You'll like it. |