PDREP adaptor: localxml


This is the localxml PDREP adaptor for the wftk. It encapsulates procdef repository functionality, storing procdef versions as XML files in a local directory. The directory in question is determined by the config module using the PROCDEF_DIRECTORY named value.
 
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WINDOWS
#include <io.h>
#endif
#include <errno.h>
#include "xmlapi.h"
#include "../wftk_session.h"
#include "../wftk_internals.h"
The 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",
   "version",
   "load"
};

XML * PDREP_localxml_init (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_localxml_free (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_localxml_info (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_localxml_version (WFTK_ADAPTOR * ad, va_list args);
XML * PDREP_localxml_load (WFTK_ADAPTOR * ad, va_list args);

static WFTK_API_FUNC vtab[] = 
{
   PDREP_localxml_init,
   PDREP_localxml_free,
   PDREP_localxml_info,
   PDREP_localxml_version,
   PDREP_localxml_load
};

static struct wftk_adaptor_info _PDREP_localxml_info =
{
   5,
   names,
   vtab
};
Cool. So here's the incredibly complex function which returns a pointer to that:
 
struct wftk_adaptor_info * PDREP_localxml_get_info ()
{
   return & _PDREP_localxml_info;
}
Thus concludes the communication with the config module. Now on with the actual implementation of functionality.
 
XML * PDREP_localxml_init (WFTK_ADAPTOR * ad, va_list args) {
   struct stat statbuf;
   char directory[256];
   char * end;

   const char * parms = xml_attrval (ad->parms, "parms");
   if (!*parms) parms = wftk_config_get_value (ad->session, "pdrep.localxml.directory");

   /* Check for existence, return error if the directory doesn't exist or if it isn't a directory. */
   strcpy (directory, parms);
   end = directory + strlen (directory) - 1;
   if (*end == '\\') *end = '\0';
   if (*end == '/') *end = '\0';

   if (stat (directory, &statbuf) == -1) {
      xml_setf (ad->parms, "error", "Directory '%s' not found.", directory);
      return (XML *) 0;
   }
   if (!(statbuf.st_mode & S_IFDIR)) {
      xml_setf (ad->parms, "error", "Directory '%s' not directory.", directory);
      return (XML *) 0;
   }

   strcat (directory, "/");
   xml_set (ad->parms, "dir", directory);
   strcpy (directory, "localxml:");
   strcat (directory, xml_attrval (ad->parms, "dir"));
   xml_set (ad->parms, "spec", directory);
   return (XML *) 0;
}
That was exactly analogous to the same code in DSREP_localxml. Freeing is also identical.
 
XML * PDREP_localxml_free (WFTK_ADAPTOR * ad, va_list args) { return (XML *) 0; }
Next up is the info call, which builds and returns a little XML telling the caller about the adaptor. If the adaptor itself is NULL, then it just returns info about the installed adaptor handler; otherwise it's free to elaborate on the adaptor instance.
 
XML * PDREP_localxml_info (WFTK_ADAPTOR * ad, va_list args) {
   XML * info;

   info = xml_create ("info");
   xml_set (info, "type", "pdrep");
   xml_set (info, "name", "localxml");
   xml_set (info, "ver", "1.0.0");
   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);
}
The most crucial functionality of the procdef repository from the point of view of regular workflow is the ability to return a current version and retrieve actual versions. Later we'll extend this adaptor to deal with all the rest of the interaction with a procdef repository.

To find the current version, we require an ID.
 
XML * PDREP_localxml_version  (WFTK_ADAPTOR * ad, va_list args)
{
   char path[256];
   char * id = (char *) 0;
   FILE * file;
   XML * index;
   XML * value;

   xml_set (ad->parms, "error", "");
   if (args) id = va_arg (args, char *);
   if (!id) {
      xml_set (ad->parms, "error", "No ID given.");
      return (XML *) 0;
   }

   strcpy (path, xml_attrval (ad->parms, "dir"));
   strcat (path, id);
   strcat (path, "__versions.xml");
   file = fopen (path, "r");
   if (!file) {
      xml_setf (ad->parms, "error", "Procdef master file for %s not found.", id);
      return (XML *) 0;
   }

   index = xml_read (file);
   if (!index) {
      xml_setf (ad->parms, "error", "Procdef master file for %s corrupted.", id);
      return (XML *) 0;
   }

   value = xml_create ("value");
   xml_set (value, "value", xml_attrval (index, "ver"));

   xml_free (index);
   fclose (file);

   return value;
}
Loading a procdef is of course very easy. We just build the filename using the arguments, then open it and read it.
 
XML * PDREP_localxml_load (WFTK_ADAPTOR * ad, va_list args) {
   char path[256];
   char *id = (char *) 0;
   char *ver = (char *) 0;
   FILE *file;
   XML * ret;

   if (args) id = va_arg (args, char *);
   if (!id) {
      xml_set (ad->parms, "error", "No ID given.");
      return (XML *) 0;
   }
   ver = va_arg (args, char *);
   if (!ver) {
      xml_set (ad->parms, "error", "No version given.");
      return (XML *) 0;
   }

   strcpy (path, xml_attrval (ad->parms, "dir"));
   strcat (path, id);
   strcat (path, "_");
   strcat (path, ver);
   strcat (path, ".xml");

   file = fopen (path, "r");
   if (!file) {
      xml_setf (ad->parms, "error", "Couldn't open file '%s' for reading.", path);
      return (XML *) 0;
   }

   ret = xml_read (file);
   fclose (file);

   return ret;
}
Other functionality, to be copied from the PDM prototype, will include creation of new procdefs, checkout and checkin of versions, and so on. Those won't be used by the wtfk library, but by the PDM library which will come next.

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. This presentation was prepared with LPML. Try literate programming. You'll like it.