WFTK_EXPORT int repos_create (XML * repository, const char * list) { return 0; } WFTK_EXPORT int repos_drop (XML * repository, const char * list) { return 0; } |
WFTK_EXPORT XML * repos_defn (XML * repository, const char * list_id) { XML * list; XML * defn; int later_check = 0; FILE * file; list = xml_locf (repository, ".list[%s]", list_id); if (!list) { /* System-defined lists, first list. */ if (!strcmp (list_id, "_sessions")) { list = xml_parse (" |
WFTK_EXPORT XML * repos_list_choices (XML * repository, const char *list_id, XML * obj, const char *field) { XML * list; XML * fld; WFTK_ADAPTOR * ad; XML * mark; XML * state; XML * choices = NULL; XML * choice; XML * holder; XML * val; const char * adaptor; char * adaptor_buf; char * label; XML * query; list = repos_defn (repository, list_id); fld = xml_locf (list, ".field[%s]", field); if (!strcmp (xml_attrval (fld, "type"), "select")) { mark = xml_firstelem (fld); while (mark) { if (xml_is (mark, "option")) { if (!choices) choices = xml_create ("select"); xml_append_pretty (choices, xml_copy (mark)); } mark = xml_nextelem (mark); } return (choices); } /* State? */ if (!strcmp (field, "_state") || !strcmp (xml_attrval (fld, "type"), "state")) { if (!obj) return NULL; choices = xml_create ("select"); xml_set (choices, "name", field); xml_set_nodup (choices, "state", xmlobj_get (obj, list, field)); if (*xml_attrval (choices, "state")) { state = xml_search (list, "state", "id", xml_attrval (choices, "state")); } else { state = xml_search (list, "state", NULL, NULL); if (state) xml_set (choices, "state", xml_attrval (state, "id")); } mark = NULL; if (state) mark = xml_search (state, "transition", NULL, NULL); if (!mark) { /* No transitions (or no such state): all states are fair game. */ mark = xml_search (list, "state", NULL, NULL); while (mark) { if (xml_is (mark, "state")) { holder = xml_create ("option"); xml_set (holder, "value", xml_attrval (mark, "id")); if (!strcmp (xml_attrval (mark, "id"), xml_attrval (choices, "state"))) { xml_set (holder, "selected", "yes"); } xml_append (holder, xml_createtext (*xml_attrval (mark, "label") ? xml_attrval (mark, "label") : xml_attrval (mark, "id"))); xml_append (choices, holder); } mark = xml_nextelem (mark); } } else { /* Current state has restricted transition list. */ holder = xml_create ("option"); xml_set (holder, "value", xml_attrval (choices, "state")); xml_set (holder, "selected", "yes"); xml_append (holder, xml_createtext (*xml_attrval (state, "label") ? xml_attrval (state, "label") : xml_attrval (state, "id"))); xml_append (choices, holder); while (mark) { if (xml_is (mark, "transition")) { state = xml_search (list, "state", "id", xml_attrval (mark, "to")); if (strcmp (xml_attrval (state, "id"), xml_attrval (choices, "state"))) { holder = xml_create ("option"); xml_set (holder, "value", xml_attrval (mark, "to")); xml_append (holder, xml_createtext (*xml_attrval (state, "label") ? xml_attrval (state, "label") : xml_attrval (state, "id"))); xml_append (choices, holder); } } mark = xml_nextelem (mark); } } xml_unset (choices, "state"); return (choices); } /* Permissible actions? */ if (!strcmp (field, "_action")) { /* TODO */ } adaptor = strchr (field, ':') ? field : fld ? (*xml_attrval (fld, "choices") ? xml_attrval (fld, "choices") : xml_attrval (fld, "storage") ) : ""; if (*adaptor && strcmp (adaptor, "record")) { /* Ask datastore adaptor */ if (!strncmp (adaptor, "list:", 5)) { adaptor_buf = strdup (adaptor + 5); label = strchr (adaptor_buf, ';'); if (label) { *label = '\0'; label++; } query = xml_create ("list"); xml_set (query, "id", adaptor_buf); repos_list (repository, query); choices = xml_create ("select"); mark = xml_firstelem (query); while (mark) { if (!xml_is (mark, "field") && !xml_is(mark, "where") && !xml_is (mark, "link")) { choice = xml_create ("option"); xml_set (choice, "value", xml_attrval (mark, "id")); if (label) { xml_append (choice, xml_createtext_nodup (xmlobj_format (mark, NULL, label))); } else { xml_append (choice, xml_createtext (xml_attrval (mark, "id"))); } xml_append_pretty (choices, choice); } mark = xml_nextelem (mark); } xml_free (query); free (adaptor_buf); return (choices); } ad = wftk_get_adaptor (repository, DATASTORE, adaptor); if (!ad) return NULL; choices = wftk_call_adaptor (ad, "choices", field, obj, list); wftk_free_adaptor (repository, ad); return (choices); } if (!fld) return NULL; if (*xml_attrval (fld, "link")) { /* TODO: implement linked fields. */ return NULL; } /* Are options supplied in the field spec? */ mark = xml_firstelem (fld); while (mark) { if (xml_is (mark, "option")) { if (!choices) choices = xml_create ("select"); xml_append_pretty (choices, xml_copy (mark)); } mark = xml_nextelem (mark); } return choices; } |
WFTK_EXPORT XML * repos_form (XML * repository, const char * list_id, const char * key, const char * mode) { XML * list; if (!repository) return NULL; list = repos_defn (repository, list_id); if (!list) return NULL; return repos_form_direct (repository, list, key, mode); } WFTK_EXPORT XML * repos_form_object (XML * repository, const char * list_id, XML * object, const char * mode) { XML * list; if (!repository) return NULL; list = repos_defn (repository, list_id); if (!list) return NULL; return repos_form_object_direct (repository, list, object, mode); } WFTK_EXPORT XML * repos_form_direct (XML * repository, XML * list, const char * key, const char * mode) { XML * object; /* Retrieve the object. */ if (key) { object = repos_get (repository, xml_attrval (list, "id"), key); if (!object) return NULL; } else { object = NULL; } return repos_form_object_direct (repository, list, object, mode); } void _repos_default_field_layout (XML * layout, XML * field, const char * mode); WFTK_EXPORT XML * repos_form_object_direct (XML * repository, XML * list, XML * object, const char * mode) { XML * layout = NULL; XML * field; XML * state; XML * parent_list; XML * defn_field; XML * actions; XML * tr; XML * td; XML * mark; char * view; WFTK_ADAPTOR * ad; if (!mode) mode = "new"; /* Ask the list how we should lay the current object out, assuming there is one. */ /* TODO: this is where we implement form construction from the system definition (or form lookup, or whatever). */ /* 2004-02-14 - one method: _views (see repos_view_find) */ view = repos_view_find (repository, list, object, mode); if (view) { repos_log (repository, 4, 0, NULL, "repmgr", "building _views-derived form in mode %s for object in class %s", mode, xml_attrval (list, "id")); layout = xml_createtext_nodup (repos_view_express (repository, list, object, view)); free (view); return (layout); } /* Otherwise we build our default. */ repos_log (repository, 4, 0, NULL, "repmgr", "building default form in mode %s for object in class %s", mode, xml_attrval (list, "id")); /* The default for tasks, of course, differs from the regular run of the mill object, as field information comes from the parent object and its list rather than (mostly) from the _tasks list. */ if (!strcmp (xml_attrval (list, "id"), "_tasks") || !strcmp (xml_attrval (list, "id"), "_todo")) { list = repos_defn (repository, "_tasks"); layout = xml_create ("table"); repos_log (repository, 4, 0, NULL, "repmgr", "task's parent list is %s", xml_attrval (object, "list")); parent_list = repos_defn (repository, xml_attrval (object, "list")); /* For tasks, we first scan the object itself for fields, and display them using labeling info from the parent object's list. */ field = xml_firstelem (object); while (field) { if (xml_is (field, "field")) { defn_field = xml_search (parent_list, "field", "id", xml_attrval (field, "id")); if (strcmp (xml_attrval (defn_field, "special"), "rest_xml")) { _repos_default_field_layout (layout, defn_field ? defn_field : field, mode); } } field = xml_nextelem (field); } /* We then scan the task defn for all fields with a display-mode attribute, and add them to the form. */ field = xml_firstelem (list); while (field) { if (xml_is (field, "field") && *xml_attrval (field, "display-mode")) { _repos_default_field_layout (layout, field, (strcmp (mode, "edit") && strcmp (mode, "new")) ? mode : xml_attrval (field, "display-mode")); } field = xml_nextelem (field); } } /* If not a task, we build a simple layout. */ if (!layout) { layout = xml_create ("table"); field = xml_create ("input"); xml_set (field, "name", "_control.list"); xml_set (field, "type", "hidden"); xml_set (field, "value", xml_attrval (list, "id")); xml_append_pretty (layout, field); if (object) { field = xml_create ("input"); xml_set (field, "name", "_control.key"); xml_set (field, "type", "hidden"); xml_set_nodup (field, "value", xmlobj_getkey (object, list)); xml_append_pretty (layout, field); } field = xml_firstelem (list ? ((!strcmp (xml_attrval (list, "format"), "object") && object) ? object : list) : object); while (field) { if (xml_is (field, "field") && strcmp (xml_attrval (field, "special"), "rest_xml")) { /* rest_xml fields are invisible. */ _repos_default_field_layout (layout, field, mode); } /* TODO: how about lists and links? */ field = xml_nextelem (field); } /* Add standard actions. */ if ((!object || !strcmp (mode, "new") || !strcmp (mode, "edit")) && strcmp (xml_attrval (list, "defaultview"), "nobuttons")) { actions = repos_action_list_object_direct (repository, list, object, mode); tr = xml_create ("tr"); td = xml_create ("td"); xml_append_pretty (layout, tr); xml_append_pretty (tr, td); xml_set (td, "colspan", "2"); tr = xml_create ("center"); xml_append_pretty (td, tr); for (mark = xml_firstelem (actions); mark; mark = xml_nextelem (mark)) { field = xml_create ("input"); xml_setf (field, "name", "_action.%s", xml_attrval (mark, "id")); xml_set (field, "type", "submit"); xml_set (field, "value", xml_attrval (mark, "label")); xml_append (tr, field); xml_append (tr, xml_create ("nbsp")); xml_append (tr, xml_create ("nbsp")); } } } return repos_format_object (repository, list, object, layout); } |
void _repos_default_field_layout (XML * layout, XML * field, const char * mode) { XML * row; XML * col; XML * val; row = xml_create ("tr"); col = xml_create ("td"); xml_append (row, col); xml_append (col, xml_createtext (*xml_attrval (field, "label") ? xml_attrval (field, "label") : xml_attrval (field, "id"))); if (strcmp (xml_attrval (field, "type"), "text") && strcmp (xml_attrval (field, "type"), "wiki")) { col = xml_create ("td"); xml_append (row, col); val = xml_create ("template:value"); xml_set (val, "name", xml_attrval (field, "id")); xml_set (val, "storage", xml_attrval (field, "storage")); xml_set (val, "mode", mode); xml_append (col, val); } else { xml_set (col, "colspan", "2"); xml_append_pretty (layout, row); row = xml_create ("tr"); col = xml_create ("td"); xml_append (row, col); xml_set (col, "colspan", "2"); val = xml_create ("template:value"); xml_set (val, "name", xml_attrval (field, "id")); xml_set (val, "storage", xml_attrval (field, "storage")); xml_set (val, "mode", mode); xml_append (col, val); } xml_append_pretty (layout, row); } |
WFTK_EXPORT XML * repos_form_object_field (XML * repository, XML * list, XML * object, const char * field_name) { XML * field; XML * spec; XML * choices; XML * mark; XML * state; WFTK_ADAPTOR * ad; XML * val = NULL; char * value = NULL; const char * t; const char * f; spec = xml_locf (list ? list : object, ".field[%s]", field_name); if (object) value = xmlobj_get (object, list, field_name); /* Select drop-down if repos_list_choices can come up with a list of value choices. */ /* Note: this includes state selection fields. */ if (strchr (xml_attrval (spec, "storage"), ':') || !strcmp (xml_attrval (spec, "type"), "select")) { choices = repos_list_choices (repository, xml_attrval (list, "id"), object, strchr (xml_attrval (spec, "storage"), ':') ? xml_attrval (spec, "storage") : field_name); if (choices) { val = xml_create ("select"); xml_set (val, "name", field_name); if (*xml_attrval (spec, "size")) xml_set (val, "size", xml_attrval (spec, "size")); if (*xml_attrval (spec, "class")) xml_set (val, "class", xml_attrval (spec, "class")); if (*xml_attrval (spec, "style")) xml_set (val, "style", xml_attrval (spec, "style")); mark = xml_firstelem (choices); while (mark) { if (value && !strcmp (value, xml_attrval (mark, "value"))) xml_set (mark, "selected", "yes"); xml_append_pretty (val, xml_copy (mark)); mark = xml_nextelem (mark); } } xml_free (choices); if (value) free (value); return (val); } /* Next, normal text fields. */ if (!*xml_attrval (spec, "type") || !strcmp (xml_attrval (spec, "type"), "string") || !strcmp (xml_attrval (spec, "type"), "numeric")) { val = xml_create ("input"); xml_set (val, "name", field_name); if (*xml_attrval (spec, "size")) xml_set (val, "size", xml_attrval (spec, "size")); if (*xml_attrval (spec, "class")) xml_set (val, "class", xml_attrval (spec, "class")); if (*xml_attrval (spec, "style")) xml_set (val, "style", xml_attrval (spec, "style")); if (value) xml_set_nodup (val, "value", xmlobj_get (object, list, field_name)); return (val); } /* Attachment fields = upload fields. TODO: something sensible if an attachment is already present. */ if (!strcmp (xml_attrval (spec, "type"), "document")) { val = xml_create ("input"); xml_set (val, "name", xml_attrval (spec, "id")); xml_set (val, "type", "file"); if (*xml_attrval (spec, "size")) xml_set (val, "size", xml_attrval (spec, "size")); if (*xml_attrval (spec, "class")) xml_set (val, "class", xml_attrval (spec, "class")); if (*xml_attrval (spec, "style")) xml_set (val, "style", xml_attrval (spec, "style")); if (value) free (value); return (val); } /* Text/Wiki fields, that is, textareas. */ if (!strcmp (xml_attrval (spec, "type"), "text") || !strcmp (xml_attrval (spec, "type"), "wiki")) { val = xml_create ("textarea"); xml_set (val, "name", field_name); if (*xml_attrval (spec, "rows")) xml_set (val, "rows", xml_attrval (spec, "rows")); if (*xml_attrval (spec, "cols")) xml_set (val, "cols", xml_attrval (spec, "cols")); if (*xml_attrval (spec, "class")) xml_set (val, "cols", xml_attrval (spec, "class")); if (*xml_attrval (spec, "style")) xml_set (val, "style", xml_attrval (spec, "style")); if (value) xml_append (val, xml_createtext_nodup (value)); return (val); } /* State fields that didn't get handled by repos_list_choices */ if (!strcmp (xml_attrval (spec, "type"), "state")) { val = xml_create ("div"); xml_set (val, "name", field_name); if (*xml_attrval (spec, "class")) xml_set (val, "class", xml_attrval (spec, "class")); if (*xml_attrval (spec, "style")) xml_set (val, "style", xml_attrval (spec, "style")); state = xml_search (list, "state", NULL, NULL); if (state) { if (*xml_attrval (state, "label")) { xml_append (val, xml_createtextf ("(%s)", xml_attrval (state, "label"))); } else { xml_append (val, xml_createtextf ("(%s)", xml_attrval (state, "id"))); } } else { xml_append (val, xml_createtext ("(system will supply)")); } if (value) free (value); return (val); } /* System-determined fields (treated as simple text) */ if (!strcmp (xml_attrval (spec, "type"), "system")) { val = xml_create ("div"); xml_set (val, "name", field_name); if (*xml_attrval (spec, "class")) xml_set (val, "cols", xml_attrval (spec, "class")); if (*xml_attrval (spec, "style")) xml_set (val, "style", xml_attrval (spec, "style")); if (value) xml_append (val, xml_createtext_nodup (xmlobj_get (object, list, field_name))); else xml_append (val, xml_createtext ("(system will supply)")); return (val); } /* Boolean fields (new 2004/03/04) */ if (!strcmp (xml_attrval (spec, "type"), "bool") || !strncmp (xml_attrval (spec, "type"), "bool:", 5)) { if (!strncmp (xml_attrval (spec, "type"), "bool:", 5)) { f = xml_attrval (spec, "type") + 5; t = strchr (f, ':'); if (t) { t++; xml_set (spec, "t", t); t--; xml_set (spec, "f", ""); xml_attrncat (spec, "f", f, t-f); } else { if (!*xml_attrval (spec, "t")) xml_set (spec, "t", "1"); xml_set (spec, "f", f); } xml_set (spec, "type", "bool"); } if (!*xml_attrval (spec, "t")) xml_set (spec, "t", "1"); /* TODO: should there be a one-time preparation phase? */ val = xml_create ("div"); mark = xml_create ("input"); xml_append_pretty (val, mark); xml_set (mark, "name", field_name); xml_set (mark, "type", "hidden"); xml_set (mark, "value", xml_attrval (spec, "f")); mark = xml_create ("input"); xml_append_pretty (val, mark); xml_set (mark, "name", field_name); xml_set (mark, "type", "checkbox"); if (*xml_attrval (spec, "class")) xml_set (mark, "class", xml_attrval (spec, "class")); if (*xml_attrval (spec, "style")) xml_set (mark, "style", xml_attrval (spec, "style")); xml_set (mark, "value", xml_attrval (spec, "t")); if (value) { if (*value && strcmp (value, xml_attrval (spec, "f"))) xml_set (mark, "checked", "yes"); free (value); } return (val); } /* Last chance: let the adaptor deal with it. */ ad = wftk_get_adaptor (repository, DATATYPE, xml_attrval (spec, "type")); if (ad) { val = wftk_call_adaptor (ad, value ? "html" : "htmlblank", object, spec); wftk_free_adaptor (repository, ad); if (value) free (value); return (val); } if (value) free (value); return NULL; } WFTK_EXPORT XML * repos_format_object (XML * repository, XML * list, XML * object, XML * layout) { XML * row; XML * col; XML * val; XML * spec; XML * field; XML * state; XML * mark; XML * holder; XML * choices; char * value; WFTK_ADAPTOR * ad; /* Express the object using the layout we just retrieved or built. */ field = xml_firstelem (layout); while (field) { if (xml_is (field, "template:value")) { spec = xml_locf (list ? list : object, ".field[%s]", xml_attrval (field, "name")); val = NULL; if (!object || !strcmp (xml_attrval (field, "mode"), "new")) { val = repos_form_object_field (repository, list, NULL, xml_attrval (field, "name")); } else if (!strcmp (xml_attrval (field, "mode"), "edit")) { val = repos_form_object_field (repository, list, object, xml_attrval (field, "name")); } else { if (*xml_attrval (field, "storage")) { choices = repos_list_choices (repository, xml_attrval (list, "id"), object, xml_attrval (field, "storage")); } else { choices = NULL; } value = xmlobj_get (object, list, xml_attrval (field, "name")); if (choices) { for (mark = xml_firstelem (choices); mark; mark = xml_nextelem (mark)) { if (!strcmp (value, xml_attrval (mark, "value"))) { val = xml_createtext_nodup (xml_stringcontenthtml (mark)); break; } } } if (!val) { val = xml_createtext_nodup (value); } else { free (value); } } if (!val) val = xml_createtext (""); xml_replace (field, val); field = val; } /* TODO: presumably we'll have some sort of list things too. */ if (xml_firstelem (field)) { field = xml_firstelem (field); } else do { if (xml_nextelem (field)) { field = xml_nextelem (field); break; } else { field = xml_parent (field); if (!field) break; } } while (1); } /* Return the result. */ return (layout); } |
WFTK_EXPORT char * repos_view_find (XML * repository, XML * list, XML * object, const char * mode) { char * viewkey; char * viewkey_expressed; char * key; XML * view_object = object; char * ret; if (!list) return NULL; if (!view_object) view_object = list; /* Get viewkey -- TODO: implement said rule engine interface here, when we have a rule engine. */ viewkey = strdup (xml_attrval (list, "viewkey")); if (!*viewkey) { free (viewkey); return NULL; /* TODO: make sure this behavior makes sense in the bigger picture. When called from repos_form, it does. */ } /* Do the flat-template thing, with extra fields. */ /*xml_set (view_object, "_user", TODO: get current user from repository); */ /* _state is already set for object, right? TODO: test this. */ xml_set (view_object, "_mode", mode); viewkey_expressed = xmlobj_format (view_object, list, viewkey); free (viewkey); key = xml_string_format ("%s_%s", xml_attrval (list, "id"), viewkey_expressed); free (viewkey_expressed); xml_unset (view_object, "_mode"); /* Clean up pseudofield. */ /* Use the key just built to index _views. */ repos_log (repository, 5, 0, NULL, "repmgr", "repos_view_find: looking for %s in _views", key); view_object = repos_get (repository, "_views", key); free (key); if (!view_object) return NULL; ret = xmlobj_get (view_object, NULL, "view"); xml_free (view_object); return (ret); } |
WFTK_EXPORT char * repos_view_express (XML * repository, XML * list, XML * object, const char * view) { XML * scratch = xml_create ("s"); char * mark; int len; char * val; XML * actions; XML * xmark; char * viewkey_expressed; char * key; XML * view_object; char * other_view; xml_set (scratch, "s", ""); mark = strstr (view, "[##"); while (mark) { xml_attrncat (scratch, "s", view, mark - view); view = mark + 3; xml_set (scratch, "name", ""); mark = strstr (view, "##]"); if (mark) len = mark - view; else len = strlen (view); xml_attrncat (scratch, "name", view, len); /* Evaluate the tag in "name" */ val = NULL; if (!strncmp (xml_attrval (scratch, "name"), "input ", 6)) { xmark = repos_form_object_field (repository, list, object, xml_attrval (scratch, "name") + 6); /* This is some cool magic. */ val = xml_stringhtml (xmark); xml_free (xmark); } else if (!strncmp (xml_attrval (scratch, "name"), "include ", 8)) { mark = (char *) xml_attrval (scratch, "name") + 8; while (*mark == ' ') mark++; viewkey_expressed = xmlobj_format (object, list, mark); key = xml_string_format ("%s_%s", xml_attrval (list, "id"), viewkey_expressed); free (viewkey_expressed); repos_log (repository, 6, 0, NULL, "repmgr", "repos_view_find: inserting %s from _views", key); view_object = repos_get (repository, "_views", key); free (key); if (view_object) { other_view = xmlobj_get (view_object, NULL, "view"); xml_free (view_object); val = repos_view_express (repository, list, object, other_view); } else val = NULL; } else if (!strncmp (xml_attrval (scratch, "name"), "repeat ", 7)) { /* TODO: do. */ } else if (!strncmp (xml_attrval (scratch, "name"), "if ", 3)) { /* TODO: do. */ } else if (!strncmp (xml_attrval (scratch, "name"), "actions ", 8)) { actions = repos_action_list_object_direct (repository, list, object, xml_attrval (scratch, "name") + 8); for (xmark = xml_firstelem (actions); xmark; xmark = xml_nextelem (xmark)) { xml_attrcat (scratch, "s", "<input type=\"submit\" name=\"_action."); xml_attrcat (scratch, "s", xml_attrval (xmark, "id")); xml_attrcat (scratch, "s", "\" value=\""); xml_attrcat (scratch, "s", xml_attrval (xmark, "label")); xml_attrcat (scratch, "s", "\"> "); } } else if (!strncmp (xml_attrval (scratch, "name"), "control ", 8)) { xml_attrcat (scratch, "s", "<input type=\"hidden\" name=\"_control.list\" value=\""); xml_attrcat (scratch, "s", xml_attrval (list, "id")); xml_attrcat (scratch, "s", "\">\n"); xml_attrcat (scratch, "s", "<input type=\"hidden\" name=\"_control.key\" value=\""); xml_attrcat (scratch, "s", repos_getkey (repository, xml_attrval (list, "id"), object)); xml_attrcat (scratch, "s", "\">\n"); xml_attrcat (scratch, "s", "<input type=\"hidden\" name=\"_control.mode\" value=\""); xml_attrcat (scratch, "s", xml_attrval (scratch, "name") + 8); xml_attrcat (scratch, "s", "\">\n"); } else { if (object) val = xmlobj_get (object, list, xml_attrval (scratch, "name")); } if (val) { xml_attrcat (scratch, "s", val); free (val); } view += len; if (*view) view += 3; /* Skip the ending ##] */ mark = strstr (view, "[##"); } xml_attrcat (scratch, "s", view); mark = strdup (xml_attrval (scratch, "s")); xml_free (scratch); return (mark); } |
WFTK_EXPORT int repos_define (XML * repository, const char * list, XML * defn) { printf ("define\n"); return 0; } |
This code and documentation are released under the terms of the GNU license. They are copyright (c) 2001-2005, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. |