Previous: wftk command-line utility ] [ Top:  ] [ Next: Dealing with processes ]

Boy, this thing is almost too simple. Well, once I reflect the entire wftk library in it, it won't be. But what I'm interested in is a program which I invoke like this:
wftk_util create . thingle
and it creates a datasheet 'thingle' for me. Then I continue to call it:
wftk_util define . thingle . thingle_procdef
wftk_util require . thingle
wftk_util complete . thingle
wftk_util complete . thingle 14
and so on. Good way to test new datasheet repositories, too.

March 19, 2002: The wftk_session management module which I've just introduced requires the calling application to pass it a lookup function for any statically linked adaptors (otherwise it will do what it can to find dynamically linked ones.) So the very beginning of this code is new in that respect. Up until now, this was compiled into the wftk library. Not very appropriate, really.
#include "wftk.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

WFTK_ADAPTOR_INFO_FN DSREP_localxml_get_info;
WFTK_ADAPTOR_INFO_FN PDREP_localxml_get_info;
WFTK_ADAPTOR_INFO_FN USER_localxml_get_info;
WFTK_ADAPTOR_INFO_FN PERMS_localxml_get_info;
struct wftk_adaptor_info * lookup_static_modules (int adaptor_class, int name_length, const char * adaptor_descriptor)

   switch (adaptor_class) {
      case DSREP:
         if (!name_length || (name_length == 8 && !strncmp ("localxml", adaptor_descriptor, 8))) {
            return DSREP_localxml_get_info();
      case DATASTORE:
         if (name_length == 4 && (!strncmp ("role", adaptor_descriptor, 4))) {
            return DATASTORE_role_get_info();
         if (name_length == 9 && (!strncmp ("currecord", adaptor_descriptor, 9))) {
            return DATASTORE_currecord_get_info();
      case DATATYPE:
         if (name_length == 6 && (!strncmp ("option", adaptor_descriptor, 6))) {
            return DATATYPE_option_get_info();
      case PDREP:
         if (!name_length || (name_length == 8 && !strncmp ("localxml", adaptor_descriptor, 8))) {
            return PDREP_localxml_get_info();
      case USER:
         if (!name_length || (name_length == 8 && !strncmp ("localxml", adaptor_descriptor, 8))) {
            return USER_localxml_get_info();
      case PERMS:
         /* Perms are different -- the caller gets no choice.
            Otherwise I figure we're in trouble, security-wise.
            Probably are anyway. */
         return PERMS_localxml_get_info();
      case TASKINDEX:
         if (name_length == 6 && !strncmp ("stdout", adaptor_descriptor, 6)) {
            return TASKINDEX_stdout_get_info();
      case NOTIFY:
      case ACTION:
         if (name_length == 6 && !strncmp ("system", adaptor_descriptor, 6)) {
            return ACTION_system_get_info();
         if (!name_length || (name_length == 4 && !strncmp ("wftk", adaptor_descriptor, 4))) {
            return ACTION_wftk_get_info();
      case DEBUG_MSG:
         return NULL;
   return NULL;

#define PERIOD_TO_NULL(x) (strcmp(x, ".") ? x : (char *)0)

main (int argc, char * argv[])
   char config_path[512];
   char * chmark;
   FILE * config_file;
   FILE * file;
   int len;
   int  complain_if_no_file = 0;
   int argp = 1; /* We'll use this to skip flags like -c for location of the config file. */
   #define argsleft (argc - argp)
   XML * datasheet = (XML *) 0;
   XML * task = (XML *) 0;
   XML * list = (XML *) 0;
   XML * session = NULL;
   XML * mark;
   XML * action = (XML *) 0;
   char * value;

   if (argc < 2) {
      printf ("usage: wftk [command] -- type wftk help for more information.\n");
      exit (1);

   session = wftk_session_alloc();
   wftk_session_setlookup (session, lookup_static_modules);

   while (*argv[argp] == '-') {
      if (!strcmp (argv[argp], "-c")) {
         strcpy (config_path, argv[argp++]);
         complain_if_no_file = 1;
      } else if (!strcmp (argv[argp], "-u")) {
         wftk_session_setuser (session, argv[argp++]);

   if (!complain_if_no_file) {
      strcpy (config_path, argv[0]);
      chmark = strrchr (config_path, '\\');
      if (chmark) chmark[1] = '\0';
      strcat (config_path, "config.xml");

   config_file = fopen (config_path, "r");
   if (!config_file && complain_if_no_file) {
      printf ("wftk: can't open config file '%s'\n", config_path);
      exit (1);
   if (config_file) {
      mark = xml_read_error (config_file);
      if (!strcmp (mark->name, "xml-error")) {
         printf ("wftk: error reading config file '%s'; '%s' in line %s\n", config_path, xml_attrval (mark, "message"), xml_attrval (mark, "line"));
         exit (1);
      } else wftk_session_configure (session, mark);
      fclose (config_file);

   if (!strcmp (argv[argp-1], "help")) {
      See The list of commands in printf format

   } else if (!strcmp (argv[argp-1], "create")) {
      See create: Creating a new datasheet
   } else if (!strcmp (argv[argp-1], "delete")) {
      See delete: Deleting an existing process
   } else if (!strcmp (argv[argp-1], "define")) {
      See define: Attach a procdef
   } else if (!strcmp (argv[argp-1], "start")) {
      See start: Start a procdef
   } else if (!strcmp (argv[argp-1], "show")) {
      See show: Displaying process information
   } else if (!strcmp (argv[argp-1], "adhoc")) {
      See adhoc: Running ad-hoc workflow

   } else if (!strcmp (argv[argp-1], "task")) {
      See task: Show a task, including data requirements
   } else if (!strcmp (argv[argp-1], "tasks")) {
      See tasks: List tasks
   } else if (!strcmp (argv[argp-1], "todo")) {
      See todo: List indexed tasks
   } else if (!strcmp (argv[argp-1], "complete")) {
      See complete: Complete a task (or start the process)
   } else if (!strcmp (argv[argp-1], "reject")) {
      See reject: Reject a task
   } else if (!strcmp (argv[argp-1], "newtask")) {
      See newtask: Create an ad-hoc task
   } else if (!strcmp (argv[argp-1], "rescind")) {
      See rescind: Delete an ad-hoc task
   } else if (!strcmp (argv[argp-1], "assign")) {
      See assign: Attach a user to a task

   } else if (!strcmp (argv[argp-1], "ask")) {
      See ask: Make a request
   } else if (!strcmp (argv[argp-1], "requests")) {
      See requests: List requests
   } else if (!strcmp (argv[argp-1], "request")) {
      See request: Show a request
   } else if (!strcmp (argv[argp-1], "accept")) {
      See accept: Accept a request
   } else if (!strcmp (argv[argp-1], "decline")) {
      See decline: Decline a request
   } else if (!strcmp (argv[argp-1], "forget")) {
      See forget: Rescind a request

   } else if (!strcmp (argv[argp-1], "set")) {
      See set: setting named values
   } else if (!strcmp (argv[argp-1], "get")) {
      See get: getting named values
   } else if (!strcmp (argv[argp-1], "values")) {
      See values: listing named values
   } else if (!strcmp (argv[argp-1], "html")) {
      See html: getting HTML for a named value
   } else if (!strcmp (argv[argp-1], "htmlblank")) {
      See htmlblank: getting a blank field

   } else if (!strcmp (argv[argp-1], "log")) {
      See log: get or write to enactment history

   } else if (!strcmp (argv[argp-1], "roles")) {
      See roles: list roles in a datasheet
   } else if (!strcmp (argv[argp-1], "role")) {
      See role: display or assign a role

   } else if (!strcmp (argv[argp-1], "users")) {
      See users: list users in a datasheet
   } else if (!strcmp (argv[argp-1], "user")) {
      See user: display user or set attribute

   } else if (!strcmp (argv[argp-1], "action")) {
      See action: Performing actions with possible deferment

   } else {
      printf ("Unknown command %s.\n", argv[argp-1]);

   xml_free (session);
   if (task) xml_free (task);
   if (list) xml_free (list);
   if (action) xml_free (action);
A little note on the way I'm scanning the args of the program might be in order. I have a variable argp which is the offset of the "current argument." That allows me to consume flags at the outset of processing, without losing track of where the actual arguments are. Normally you'd think you would then just read each arg and increment with argp++, but unfortunately MSVC++ 5.0 has a nasty little bug: if you have two separate arguments to a function, and they're both the same variable incremented, it "optimizes" by incrementing after the function call is built. So you duplicate the first item. Yeah. Real optimal. You'll notice that I therefore increment explicitly after each function call which requires more than one of the command-line arguments (if they're not in macros, anyway). Good goin', Bill.

The list of commands in printf format
printf ("WFTK command-line interface v1.0 2001/02/18\n");
if (config_file) {
   printf ("Using configuration file in %s\n\n", config_path);
} else {
   printf ("Using precompiled configuration.\n\n");
printf (" -c  : specify alternate configuration file\n");
printf (" -u  : specify username for session (not authenticated)\n");
printf ("\n");

printf ("BASIC INFO:\n-----------\n");
printf (" help     -- this list\n");
printf (" info     -- (not implemented) library version and installation information\n");

printf ("\nPROCESSES:\n----------\n");
printf (" create   -- create a new process/datasheet\n");
printf (" delete   -- delete an existing process/datasheet\n");
printf (" define   -- associate a procdef with a process\n");
printf (" start    -- start a procdef on a process\n");
printf (" show     -- show the state of a process\n");
printf (" adhoc    -- attach and run ad-hoc workflow\n");

printf ("\nTASKS:\n------\n");
printf (" task     -- show a task (explicit or potential)\n");
printf (" tasks    -- list tasks\n");
printf (" todo     -- list active indexed tasks\n");
printf (" complete -- start process or complete a task\n");
printf (" reject   -- reject a task\n");
printf (" newtask  -- create a new ad-hoc task\n");
printf (" rescind  -- delete an ad-hoc task\n");
printf (" assign   -- assign a user to a task\n");

printf ("\nREQUESTS:\n---------\n");
printf (" ask      -- make a request\n");
printf (" requests -- list requests\n");
printf (" request  -- show a request\n");
printf (" accept   -- accept a request\n");
printf (" decline  -- decline a request\n");
printf (" forget   -- rescind a request\n");

printf ("\nVALUES:\n-------\n");
printf (" set       -- set a named value\n");
printf (" get       -- get a named value\n");
printf (" values    -- list the values in a datasheet\n");
printf (" html      -- show the HTML form field for a value\n");
printf (" htmlblank -- show a blank HTML form field for a value\n");

printf ("\nENACTMENT:\n----------\n");
printf (" log      -- show enactment history or write a log entry to the history\n");

printf ("\nROLES:\n------\n");
printf (" roles    -- list roles of process\n");
printf (" role     -- show or assign a role\n");

printf ("\nUSERS:\n------\n");
printf (" users    -- list involved users of process\n");
printf (" user     -- show a user or assign a user attribute\n");

printf ("\nACTIONS:\n--------\n");
printf (" action   -- perform an action with full permission/deferment protection\n");
Previous: wftk command-line utility ] [ Top:  ] [ Next: Dealing with processes ]

This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license.