Index: /tags/1.0/Makefile
===================================================================
--- /tags/1.0/Makefile	(revision 5)
+++ /tags/1.0/Makefile	(revision 5)
@@ -0,0 +1,24 @@
+CXX = g++
+CXXFLAGS = -g -O3 -Wall
+
+OBJS = disparm.o disparmmain.o token.o shellenv.o clocks.o
+SRCS = $(OBJS:.o=.cpp)
+
+all: disparmcmd
+
+disparmcmd: $(OBJS)
+	$(CXX) -o $@ $(CXXFLAGS) $(OBJS)
+
+%.o: 	%.cpp
+	$(CXX) $(CXXFLAGS) -c -o $@ $<
+
+clean:
+	rm -f disparmcmd $(OBJS) core .depend
+
+include .depend
+
+dep .depend:
+	$(CXX) $(CXXFLAGS) -M $(SRCS) > .depend
+
+test:
+	echo $(SRCS)
Index: /tags/1.0/TODO
===================================================================
--- /tags/1.0/TODO	(revision 5)
+++ /tags/1.0/TODO	(revision 5)
@@ -0,0 +1,3 @@
+disparm -s -p niet-bestaande-pool geeft onzin getallen
+'waiting for lock' niet meer geven, maakt gebruikers 
+  zenuwachtig
Index: /tags/1.0/clocks.cpp
===================================================================
--- /tags/1.0/clocks.cpp	(revision 5)
+++ /tags/1.0/clocks.cpp	(revision 5)
@@ -0,0 +1,55 @@
+#include <sys/times.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include "clocks.h"
+#include <unistd.h>
+double cputime() /* aix, xlf */
+{ struct tms b;
+  clock_t r;
+  times( &b);
+  r = b.tms_utime + b.tms_stime;
+  return ( (double) r/(double) sysconf(_SC_CLK_TCK));
+}
+double CPUTIME() /* cray  */
+{ 
+  return ( cputime());
+}
+double cputime_() /* g77, gcc */
+{ 
+  return ( cputime());
+}
+
+double wallclock() 
+{ 
+	struct timeval toot;
+	double r;
+
+	gettimeofday(&toot,0);
+	r=toot.tv_sec+0.000001*(double)toot.tv_usec;
+	return(r);
+}
+double WALLCLOCK()
+{
+  return (wallclock());
+}
+double wallclock_()
+{
+  return wallclock();
+}
+		
+void fortransleep(int *i)
+  {
+    sleep(*i);
+  }
+ 
+void FORTRANSLEEP(int *i)
+  {
+    sleep(*i);
+  }
+
+void fortransleep_(int *i)
+  {
+    sleep(*i);
+  }
+
Index: /tags/1.0/clocks.h
===================================================================
--- /tags/1.0/clocks.h	(revision 5)
+++ /tags/1.0/clocks.h	(revision 5)
@@ -0,0 +1,8 @@
+#ifdef __cplusplus
+extern "C"  {
+#endif
+double wallclock(void);
+double cputime(void);
+#ifdef __cplusplus
+}
+#endif
Index: /tags/1.0/disparm.1
===================================================================
--- /tags/1.0/disparm.1	(revision 5)
+++ /tags/1.0/disparm.1	(revision 5)
@@ -0,0 +1,146 @@
+.TH DISPARM 1 
+.SH NAME
+disparm \- alias using disparmcmd to call the program disparmcmd
+.SH SYNOPSIS
+.B disparm
+\fB\ -h,--help  \fP  
+.P 8
+.B disparm
+\fB\ -c,--create  \fP [\fB OPTIONS\fP ] [\fB -i,--input INPUTFILE \fB]
+.P 8
+.B disparm
+\fB\ -n,--next  \fP [\fB OPTIONS\fP ] [\fB -m,--max MAX \fB]
+.P 8
+.B disparm
+\fB\ -r,--ready  \fP [\fB OPTIONS\fP] [\fB -k, --key KEY \fP ]
+.P 8
+.B disparm
+\fB\ -R,--REMOVE  \fP [\fB OPTIONS\fP] [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -s,--stats [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -u,--unhandled [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -D,--debug [\fB -p, --pool POOL \fP ]
+.SH DESCRIPTION
+disparm is an alias using disparmcmd, an utility to distribute 
+strings (parameters) in a pool, in an orderly way.
+In general, the
+strings are used as command parameters, see Examples.
+.SH COMMANDS
+.TP 4
+.B -h,--help
+Prints usage information.
+.TP 4
+.B -c,--create [ -i,--input INPUTFILE ] [ -p,--pool POOL ]
+Creates new pool and fills it with lines from INPUTFILE, default
+from stdin. INPUTFILENAME '-' stands for stdin.
+See OPTIONS for a description of POOL.
+The environment
+variable 'DISPARM_RC' is set, see ENVIRONMENT.
+.TP 4
+.B -n,--next [ -m,--max MAX ] [ -p,--pool POOL ]
+Gets the next string.
+The environment variables DISPARM_RC, DISPARM_VALUE, DISPARM_COMMITTED and DISPARM_KEY are set,
+see ENVIRONMENT.
+The same string in the pool will be
+produced MAX times, the default is 1. By setting this to a higher
+value, the same string will be produced more times. This can be useful
+for dealing with crashed jobs.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -r,--ready [ -k,--key KEY ] [-p,--pool POOL ]
+Marks the token with key KEY as ready. If not specified on the commandline, the
+value of environment variable DISPARM_KEY is used.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -R,--REMOVE --p,--pool POOL ]
+Removes the pool.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -s,--stats
+Prints to standard error:
+
+.br 
+total number of strings in pool
+.br 
+number of strings delivered
+.br
+number of strings marked as ready
+.br
+maximum one string has been delivered
+.TP 4
+.B -u,--unhandled
+Prints to standard error the strings that are committed, but not marked
+as ready.
+.TP 4
+.B -D,--debug
+Prints to standard error some debug information.
+.SH OPTIONS
+All options accept the following general parameters:
+.TP 4
+.B -p,--pool POOL
+POOL is a unique name of the file that contains the pool. 
+Default: pool.disparm in the current directory.
+.TP 4
+.B -d,--lockdir LOCKDIR
+LOCKDIR is a directory to hold the lockfile (implemented as a hard
+link to the pool file). All lockfiles have different names,
+so it is permitted to use the same directory for all uses of disparm. 
+Default: the directory where the pool resides. 
+
+.SH ENVIRONMENT
+The following environment variables can be set by disparm:
+.P
+.B DISPARM_RC
+if the value is OK, than no errors were found
+.P
+.B DISPARM_KEY
+contains the keyvalue of the token
+.P
+.B DISPARM_LINENO
+contains the line number of the string in the file from which
+the pool file has been created
+.P
+.B DISPARM_COMMITTED
+contains the number of times the string has been committed before
+.P
+.B DISPARM_VALUE
+contains the token
+.SH EXAMPLES
+.TP 4
+
+Create a pool with the firat 10 lines from the man page of sed:
+
+ man sed | head > parmfile
+ disparm -c -p demo1 -i parmfile
+
+Get a line:
+
+ disparm -n -p demo1
+
+The environment variable DISPARM_VALUE contains now one of the
+first ten lines of the man page of sed.
+
+You can use this in a command like this:
+
+ eval "myparser $DISPARM_VALUE"
+
+The token can be marked as ready by:
+
+ disparm -r -p demo1
+
+Finally, the pool can be completely removed by:
+
+ disparm -R -p demo1
+
+.SH "SEE ALSO"
+sara-get-mem-size(1), sara-get-num-cores(1), disparmcmd(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.0/disparm.cpp
===================================================================
--- /tags/1.0/disparm.cpp	(revision 5)
+++ /tags/1.0/disparm.cpp	(revision 5)
@@ -0,0 +1,822 @@
+#include <iostream>
+#include <fstream>
+#include <libgen.h>
+#include <cstdlib>
+#include <string.h>
+
+#include "disparm.h"
+#include "tofromstring.h"
+#include "token.h"
+#include "shellenv.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+
+const std::string disparm::keyname       = "DISPARM_KEY";
+const std::string disparm::committedname = "DISPARM_COMMITTED";
+const std::string disparm::valuename     = "DISPARM_VALUE";
+const std::string disparm::rcname        = "DISPARM_RC";
+const std::string disparm::linename      = "DISPARM_LINENO";
+
+const std::string disparm::defpath_pool   = "pool.disparm";
+const std::string disparm::definputfilename = "-";
+
+//
+//constructor disparm
+//
+disparm::disparm()
+{
+  char defpoolname_c[this->defpath_pool.length()+1];        // default path_pool
+  strcpy(defpoolname_c,this->defpath_pool.c_str());         
+  //
+  // A note about basename and dirname: they need as argument a char*
+  // NOT a const char*. Both functions may modify the argument,
+  // so, in general, do not use the same argument more than
+  // once.
+  //
+  char defpath_lockdir_c[this->defpath_lockdir.length()+1];  // default directory for the lockfile
+  strcpy(defpath_lockdir_c,this->defpath_lockdir.c_str());
+  this->setpath_pool(this->defpath_pool);
+  this->defpath_lockdir = std::string(dirname(defpoolname_c));
+  this->setpath_link("");
+  this->setpath_lock("");     // the name of the lock folder
+  this->setbaselinkfilename("");
+  this->setinputfilename(this->definputfilename);  // default input file name
+  this->setpath_lockdir(this->defpath_lockdir);      // default lock directory
+  this->setmaxiter(1);                             // default maximum times a string can be given out
+  this->pmarker="pp";                              // start of record with pointer to next string in pool
+  this->pmarkerlen = this->pmarker.length();
+  this->lockname="lock.disparm";
+
+  std::string shell = getenv("SHELL");
+  if (shell.find("csh") != std::string::npos) 
+    this->shelltype = SHELL_CSH;
+  else
+    this->shelltype = SHELL_SH;
+
+  //
+  // following items are returned on stdout as in:
+  // export DISPARM_KEY=1234
+  // See also above
+  //
+  this->key       = -1;     // key: pointer to next item in pool
+  this->status    = '0';
+  this->committed = 0;      // the number of times the string is given out
+  this->rc        = "OK";   // return code
+  this->value     = "";     // the string to give out
+  this->linenr    = 0;      // the line number in the pool
+
+  this->debug     = 0;
+}
+
+void disparm::removelock()
+{
+  rmdir(this->path_lock.c_str());
+  unlink(this->path_link.c_str());
+}
+
+void disparm::setpath_pool(std::string pool)
+{
+  this->path_pool = pool;
+}
+
+void disparm::setpath_link(std::string path_link)
+{
+  this->path_link = path_link;
+}
+
+void disparm::setpath_lock(std::string path_lock)
+{
+  this->path_lock = path_lock;
+}
+
+void disparm::setbaselinkfilename(std::string baselinkfilename)
+{
+  this->baselinkfilename = baselinkfilename;
+}
+
+int disparm::setpath_lockdir(std::string lockdir)
+{
+  this->path_lockdir = lockdir;
+  int rc = mkdir(path_lockdir.c_str(),0777);
+  if (rc ==0)
+    return 0;
+  else
+  {
+    if (errno == EEXIST)
+    {
+      struct stat buf;
+      stat(path_lockdir.c_str(),&buf);
+      if (S_ISDIR(buf.st_mode))
+	return 0;
+    }
+    else
+      return -1;
+  }
+  return 0;  // never reached
+}
+
+//
+// we try to generate a name for a linkfile that is unique.
+// In the name is also the creation time.
+// We also create a suitable path_lockdir here. 
+//
+void disparm::createlinkfilename()
+{
+  if (this->debug)
+    this->debugger("Before createlinkfilename:");
+  char hostname[1025];
+  gethostname(hostname,1024);
+  char poolname_c[this->path_pool.length()+1];
+  strcpy(poolname_c,this->path_pool.c_str());
+  std::string poolnamebase = basename(poolname_c);
+  std::string base = this->path_lockdir + "/" + poolnamebase + ".link.";
+  this->setbaselinkfilename(base);
+  this->setpath_link(   base
+                         + to_string(time(0))
+                         + "." 
+                         + hostname 
+                         + "." 
+                         + to_string(getpid()));
+  this->setpath_lock(this->path_lockdir + "/" + this->lockname);
+  if (this->debug)
+    this->debugger("After createlinkfilename:");
+}
+
+void disparm::setinputfilename(std::string inputfilename)
+{
+  this->inputfilename = inputfilename;
+}
+
+void disparm::setkey(std::streampos key)
+{
+  this->key = key;
+}
+
+std::streampos disparm::getkey(void)
+{
+  return this->key;
+}
+
+void disparm::setmaxiter(int maxiter)
+{
+  this->maxiter = maxiter;
+}
+
+//
+// Create poolfile from input file
+//
+// On return, s wil contain the output of createrc
+//
+int disparm::create(std::string &s)
+{
+  if (this->debug)
+    this->debugger("Before create:");
+  std::ifstream inp;
+  std::istream *pinp;
+  s = "";
+  //
+  // open inputfile, take care that stdin is also allowed
+  //
+  if (this->inputfilename == "-")
+    pinp = &std::cin;
+  else
+  {
+    inp.open(this->inputfilename.c_str());
+    if (!inp.is_open())
+    {
+      std::cerr<<"Cannot open '"<<inputfilename<<"'"<<std::endl;
+      this->rc = "FAIL";
+      s = this->createrc();
+      return 1;
+    }
+    pinp = &inp;
+  }
+  //
+  // open poolfile
+  //
+  std::string line;
+  std::fstream out;
+  out.open(this->path_pool.c_str(),std::fstream::in | std::fstream::out
+                               |  std::fstream::trunc);
+
+  //
+  // fill poolfile
+  //
+  token tok;
+  tok.setfile(out);
+  std::streampos p;
+  unsigned int linenr = 0;
+  while (getline(*pinp,line))
+  {
+    tok.setlinenr(++linenr);
+    tok.setcommitted(0);
+    tok.clearstatus();
+    tok.setvalue(line);
+    tok.writetoken(p);
+  }
+  tok.writelocpointer(0);
+
+  //
+  // close input file, only if it is not stdin
+  //
+  if (pinp == &inp)
+    inp.close();
+  out.close();
+
+  this->key = 0;
+  s = createrc();
+  if (this->debug)
+    this->debugger("After create:");
+
+  return 0;
+}
+
+//
+// get next string from pool
+//
+int disparm::next(std::string& s)
+{
+  if (this->debug)
+    this->debugger("Before next:");
+  int rc = this->realnext();
+  s = this->createrc();
+  if (this->debug)
+    this->debugger("After next:");
+  return rc;
+}
+
+//
+// create string to be eval'd by the shell
+//
+std::string disparm::createrc(void)
+{
+  std::string s = "";
+  s = s + shellenv(to_string(this->key),      this->keyname,      this->shelltype);
+  s = s + shellenv(to_string(this->committed),this->committedname,this->shelltype);
+  s = s + shellenv(to_string(this->value),    this->valuename,    this->shelltype);
+  s = s + shellenv(to_string(this->rc),       this->rcname,       this->shelltype);
+  s = s + shellenv(to_string(this->linenr),   this->linename,     this->shelltype);
+  return s;
+}
+
+// 
+// get the next string from the pool
+//
+int disparm::realnext()
+{
+  std::fstream file;
+  bool good = 1;
+
+  this->key       = 0;
+  this->status    = '0';
+  this->committed = 0;
+  this->rc        = "OK";
+  this->linenr    = 0;
+  this->value     = "";
+
+  //
+  // get lock, return if that fails
+  //
+  if (this->getlock()!=0)
+  {
+    this->rc = "FAIL";
+    return 1;
+  }
+
+  //
+  // read next string from pool
+  // take care of ready records, maximum iterations
+  // allowed and some kinds of mishaps
+  //
+  file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out);
+  if (file.is_open())
+  {
+    token       tok;
+    std::string line;
+    tok.setfile(file);
+    std::streampos locpointer, posused, posnext;
+
+    //
+    // get pointer to next string
+    //
+    locpointer = tok.readlocpointer();
+    if (locpointer < 0)
+    {
+      std::cerr << "Corrupt parameterfile: cannot find locpointer\n" << std::endl;
+      this->rc = "FAIL";
+      good = 0;
+    }
+    //
+    // read next string from pool
+    //
+    if (good)
+    {
+      int rc = tok.readnext(locpointer,posused,posnext);
+      if (rc != 0)
+      {
+        std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+        std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+        this->rc = "FAIL";
+        good = 0;
+      }
+    }
+    //
+    // keep reading until a valid record is found
+    // take appropriate action if no such record exists
+    //
+    if (good)
+    {
+      std::streampos locpointerold = locpointer;
+      bool allready = 1;  // to learn if all records are
+                          // ready
+      while (1)
+      {
+        if (!tok.getready())
+        {
+	  allready = 0;
+	  if (tok.getcommitted() >= this->maxiter)
+	  {
+	    good = 0;
+	    this->rc = "MAX_EXCEEDED";
+	    break;
+	  }
+          this->key = posused;
+          this->status = tok.getstatus();
+          this->committed = tok.getcommitted();
+          this->value = tok.getvalue();
+	  this->linenr = tok.getlinenr();
+          tok.setcommitted(tok.getcommitted()+1);
+          tok.writetoken(posused);
+          tok.writelocpointer(posused);
+          break;
+        }
+        locpointer = posnext;
+	if (locpointer == locpointerold)
+	{
+	  //
+	  // so we are cycling, nothing to find, it seems
+	  //
+	  good = 0;
+	  if (allready)
+	    this->rc = "ALL_READY";
+	  else
+	    this->rc = "MAX_EXCEEDED";
+	  break;
+	}
+        int rc = tok.readnext(locpointer,posused,posnext);
+        if (rc != 0)
+        {
+	  std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+          std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+          this->rc = "FAIL";
+          good = 0;
+          break;
+        }
+      }
+    }
+  }
+  else
+  {
+    std::cerr<<"Strange error: cannot open '"<<path_link<<"'"<<std::endl;
+    return 1;
+  }
+
+  file.close();
+  this->removelock();
+  return !good;
+}
+
+// 
+// get a lock on the poolfile
+// The normal methods don't work on an nfs filesystem.
+// Luckily, there is a method based on links to the
+// file to be locked, see also man(2) open under O_EXCL
+//
+// Creating a link is atomic, so it suffices to see if 
+// the link count equals to two. If not, we try again.
+//
+// It seems that the file used for linking contains displays
+// an up-to-date content. 
+//
+int disparm::getlock(void)
+{
+  struct stat buf;
+  int rc;
+  int sleeptime=2;
+
+  int iter = 0;
+  int ntryremovelock = 0;
+  while(1)
+  {
+    //
+    // check poolfile's linkcount
+    // if link count == 1, we try to create a link, else wait
+    //
+    rc  = stat(this->path_pool.c_str(),&buf);
+    std::string er="";
+    switch (errno)
+    {
+      case (EACCES): er="Search permission is denied for one of the  directories  in  the path prefix of path.  (See also path_resolution(7).)";
+		     break;
+
+      case(EBADF):  er="fd is bad.";
+		    break;
+
+      case(EFAULT): er="Bad address.";
+		    break;
+
+      case(ELOOP):  er="Too many symbolic links encountered while traversing the path.";
+		    break;
+
+      case(ENAMETOOLONG):er="File name too long.";
+			 break;
+
+      case(ENOENT): er="A component of path does not exist, or path is an empty string.";
+		    break;
+
+      case(ENOMEM): er="Out of memory (i.e., kernel memory).";
+		    break;
+
+      case(ENOTDIR):
+              er="A component of the path prefix of path is not a directory.";
+	      break;
+
+      case(EOVERFLOW):er="(stat())  path refers to a file whose size cannot be represented in the type off_t.  This can occur when an application  compiled on a 32-bit platform without -D_FILE_OFFSET_BITS=64 calls stat() on a file whose size exceeds (2<<31)-1 bits";
+		      break;
+
+    }
+    if (rc !=0 )
+    {
+      std::cerr<<er<<std::endl;
+      perror(" Error stat poolfile:");
+      std::cerr<<"Cannot stat the pool file:'"<<this->path_pool.c_str()<<"'"<< std::endl;
+      return 1;
+    }
+    this->createlinkfilename();
+    rc = mkdir(this->path_lock.c_str(), 0700);
+    if (rc == 0)
+    {
+      // create link
+      int rc = link(this->path_pool.c_str(),this->path_link.c_str());
+      if (rc < 0)
+      {
+	std::cerr<<"Cannot make link '"<<this->path_link<<"' to '"
+	  <<this->path_pool<<"'"<<std::endl<<" Is poolname '"<<path_pool
+	  <<"' correct?"<<std::endl;
+	return 1;
+      }
+      break;
+    }
+    //
+    // wait some time before trying again
+    //
+    iter++;
+    std::cerr << iter <<": waiting for lock " << std::endl;
+    sleep(sleeptime);
+    if (iter > 9)
+    {
+      std::cerr << "Trying to remove locks" << std::endl;
+      //
+      // remove stale link files
+      //
+      this->remove_lock_and_links();
+      ntryremovelock ++;
+      if (ntryremovelock > 10)
+      {
+	std::cerr << "Cannot get lock after many tries." << std::endl;
+	return 1;
+      }
+      iter = 0;
+    }
+  }
+  return 0;
+}
+
+//
+// mark string as ready
+//
+int disparm::ready()
+{
+  if (this->getlock()!=0)
+  {
+    return 1;
+  }
+
+  //
+  // if no key given as parameter, read it from environment
+  //
+  if (this->key < 0)
+  {
+    char * s = getenv(this->keyname.c_str());
+    if (s == NULL || *s == 0)
+    {
+      this->removelock();
+      std::cerr << "No key given" << std::endl;
+      return 1;
+    }
+    else
+    {
+      this->key = from_string<long long>(std::string(s));
+    }
+  }
+
+  std::streampos locpointer = this->key;
+  std::fstream file;
+
+  file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out);
+  if (file.is_open())
+  {
+    token tok;
+    std::string line;
+    tok.setfile(file);
+    std::streampos posused,posnext;
+    //
+    // read next record
+    //
+    int rc = tok.readnext(locpointer,posused,posnext);
+    if (rc != 0)
+    {
+      std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+      std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+      file.close();
+      this->removelock();
+      return 1;
+    }
+    //
+    // mark record as ready
+    //
+    tok.ready();
+    //
+    // write record back
+    //
+    tok.writetoken(locpointer);
+  }
+  file.close();
+  this->removelock();
+  return 0;
+}
+
+//
+// remove poolfile
+//
+int disparm::REMOVE()
+{
+  if (this->getlock()!=0)
+  {
+    return 1;
+  }
+  this->removelock();
+
+  int rc = unlink(this->path_pool.c_str());
+  if (rc)
+  {
+    std::cerr << "Cannot remove " << this->path_pool << std::endl;
+    return 1;
+  }
+  return 0;
+}
+
+//
+// return statistics about the pool
+// no wait for lock, so be careful interpretating the results
+//
+stats disparm::getstats()
+{
+  stats statistics;
+  token tok;
+  std::fstream file;
+  statistics.records   = -1;
+  statistics.committed = -1;
+  statistics.ready     = -1;
+  statistics.commax    = -1;
+
+
+  file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out);
+
+  if (file.is_open())
+  {
+    tok.setfile(file);
+    std::streampos pos = 0;
+    std::streampos posused;
+    std::streampos posnext;
+    statistics.records   = 0;
+    statistics.committed = 0;
+    statistics.ready     = 0;
+    statistics.commax    = 0;
+    
+    while(1)
+    {
+      tok.readnext(pos, posused,posnext);
+      if (pos != posused)
+        break;
+      if (tok.getready())
+        statistics.ready++;
+      if (tok.getcommitted()>0)
+        statistics.committed++;
+      if (tok.getcommitted() > statistics.commax)
+        statistics.commax = tok.getcommitted();
+      statistics.records++;
+      pos = posnext;
+    }
+  }
+
+  file.close();
+  return statistics;
+}
+
+int disparm::echounhandled(void)
+{
+  token tok;
+  std::fstream file;
+
+  file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out);
+
+  if (file.is_open())
+  {
+    tok.setfile(file);
+    std::streampos pos = 0;
+    std::streampos posused;
+    std::streampos posnext;
+    std::cerr << "linenr:committed:value"<< std::endl;
+    
+    while(1)
+    {
+      tok.readnext(pos, posused,posnext);
+      if (pos != posused)
+        break;
+
+      if (!tok.getready()&&tok.getcommitted()>0)
+      {
+	std::cerr << tok.getlinenr() << ":"<<tok.getcommitted()<<":" << tok.getvalue() << std::endl;
+      }
+      pos = posnext;
+    }
+  }
+  else
+  {
+    std::cerr <<"Cannot open pool file '"<<this->path_pool<<"'"<<std::endl;
+    return 1;
+  }
+
+  file.close();
+  return 0;
+}
+
+
+int disparm::remove_lock_and_links(const std::string path, const std::string dirl, const std::string path_lock, int timer)
+{
+  // removes lock and hard links in directory "dirl" to file "path" 
+  // in directory "dir", if they are older than "time" seconds. 
+  // Method: 
+  // if lock is older than timer seconds, remove it.
+  // and:
+  // using readdir, examine all components of dirl and see
+  // if they have the same inode as "path". If so, remove, unless
+  // the realname is the same as the realname of "path"
+  //
+  DIR *pdir;
+  struct dirent *pent;
+  int rc;
+  struct stat buf;
+
+  pdir = opendir(dirl.c_str());
+  if (!pdir)
+  {
+    std::cerr << "Cannot read link directory '"<<dirl<<"'"<<std::endl;
+    return 1;
+  }
+  // look if lock can be removed, if yes: and make it so 
+  rc = stat(path_lock.c_str(),&buf);
+  if (rc == 0)
+  {
+    if (time(0) > buf.st_mtime + timer)
+    {
+      std::cerr << "Trying to remove '"<<path_lock<<"'"<<std::endl;
+      rc = rmdir(path_lock.c_str());
+      // It could happen that rmdir failed, for example another process
+      // already deleted it, or path is invalid etc.
+      // We ignore this, relying on the fact that eventually
+      // getlock will time out.
+    }
+  }
+  rc = stat(path.c_str(),&buf);
+  if (rc !=0)
+  {
+    std::cerr << "Cannot stat '"<<path<<"'"<<std::endl;
+    return 1;
+  }
+  ino_t inodenr = buf.st_ino;
+  char* rpathc = (char*)malloc(PATH_MAX+1);
+  rpathc = realpath(path.c_str(),rpathc);
+  std::string rpath = rpathc;
+  std::string rpath1;
+  
+  while ((pent=readdir(pdir)))
+  {
+    struct stat buf;
+    std::string name = pent->d_name;
+    if (name == ".") continue;
+    if (name == "..") continue;
+    rpathc = realpath((dirl+"/"+name).c_str(),rpathc);
+    rpath1 = rpathc;
+    std::cerr<<"TD: looking at '"<<rpath1<<"'"<<std::endl;
+    if (rpath != rpath1)
+    {
+      int rc = stat(rpath1.c_str(),&buf);
+      if (rc != 0)
+      {
+	std::cerr<<"Cannot stat '"<<rpath1<<"'"<<" this cannot happen!" << std::endl;
+	free(rpathc);
+	return 1;
+      }
+      if (buf.st_ino == inodenr)
+      {
+	std::cerr << "time: " << time(0) << " " << buf.st_mtime << " " << timer << std::endl;
+	if (time(0) > buf.st_mtime + timer)
+	{
+	  int rc = unlink(rpath1.c_str());
+	  if (rc == 0)
+	    std::cerr<<"Unlinked '"<<rpath1<<"'"<<std::endl;
+	  else
+	    std::cerr<<"Could not unlink '"<<rpath1<<"'"<<std::endl;
+	}
+	else
+	  std::cerr << "TD: not unlinking because time: '"<<rpath1<<"'"<<std::endl;
+      }
+    }
+    else
+      std::cerr<<"TD: not unlinking '"<<rpath1<<"'"<<std::endl;
+  }
+  free(rpathc);
+  return 0;
+}
+
+
+//
+// remove lock and links
+// method: do a glob on baselinkfilename and unlink
+// the files.
+//
+// Only remove if links are older than 60 seconds.
+//
+void disparm::remove_lock_and_links()
+{
+  if (this->debug)
+    this->debugger("Before remove_lock_and_links:");
+
+  remove_lock_and_links(this->path_pool,this->path_lockdir,this->path_lock,60);
+  if (this->debug)
+    this->debugger("Before remove_lock_and_links:");
+}
+
+void disparm::setdebug()
+{
+  this->debug = 1;
+}
+void disparm::debugger(const std::string s)
+{
+  std::cerr << s << std::endl;
+  std::cerr << "baselinkfilename: '" << this->baselinkfilename << "'" << std::endl;
+  std::cerr << "committed:        '" << this->committed << "'" << std::endl;
+  std::cerr << "committedname:    '" << this->committedname << "'" << std::endl;
+  std::cerr << "definputfilename: '" << this->definputfilename << "'" << std::endl;
+  std::cerr << "defpath_pool:     '" << this->defpath_pool << "'" << std::endl;
+  std::cerr << "defpath_lockdir:  '" << this->defpath_lockdir << "'" << std::endl;
+  std::cerr << "inputfilename:    '" << this->inputfilename << "'" << std::endl;
+  std::cerr << "key:              '" << this->key << "'" << std::endl;
+  std::cerr << "keyname:          '" << this->keyname << "'" << std::endl;
+  std::cerr << "linename:         '" << this->linename << "'" << std::endl;
+  std::cerr << "linenr:           '" << this->linenr << "'" << std::endl;
+  std::cerr << "lockname:         '" << this->lockname << "'" << std::endl;
+  std::cerr << "maxiter:          '" << this->maxiter << "'" << std::endl;
+  std::cerr << "path_link:        '" << this->path_link << "'" << std::endl;
+  std::cerr << "path_lock:        '" << this->path_lock << "'" << std::endl;
+  std::cerr << "path_lockdir:     '" << this->path_lockdir << "'" << std::endl;
+  std::cerr << "path_pool:         '" << this->path_pool << "'" << std::endl;
+  std::cerr << "pmarker:          '" << this->pmarker << "'" << std::endl;
+  std::cerr << "pmarkerlen:       '" << this->pmarkerlen << "'" << std::endl;
+  std::cerr << "pointerlen:       '" << this->pointerlen << "'" << std::endl;
+  std::cerr << "rc:               '" << this->rc << "'" << std::endl;
+  std::cerr << "rcname:           '" << this->rcname << "'" << std::endl;
+  std::cerr << "shelltype:        '" << this->shelltype << "'" << std::endl;
+  std::cerr << "status:           '" << this->status << "'" << std::endl;
+  std::cerr << "value:            '" << this->value << "'" << std::endl;
+  std::cerr << "valuename:        '" << this->valuename << "'" << std::endl;
+}
+
+//
+// Just to teach me again how to make an object 
+// suitable for the << operator
+//
+std::ostream& operator <<(std::ostream &os,disparm &obj)
+{
+  stats sta;
+  sta = obj.getstats();
+  os << "Total:     " << sta.records   << "\n" 
+     << "Committed: " << sta.committed << "\n"
+     << "Ready:     " << sta.ready     << "\n"
+     << "Max:       " << sta.commax;
+  return os;
+}
+
Index: /tags/1.0/disparm.h
===================================================================
--- /tags/1.0/disparm.h	(revision 5)
+++ /tags/1.0/disparm.h	(revision 5)
@@ -0,0 +1,65 @@
+struct stats{unsigned int records;unsigned int ready;unsigned int committed;unsigned int commax;};
+class disparm{
+  private:
+  static const std::string keyname;
+  static const std::string committedname;
+  static const std::string valuename;
+  static const std::string rcname;
+  static const std::string linename;
+
+  bool debug;
+
+  std::streampos key;
+  int            committed;
+  std::string    rc;
+  std::string    value;
+  char           status;
+  unsigned int   linenr;
+
+  std::string path_pool;
+  std::string path_link;
+  std::string baselinkfilename;
+  std::string inputfilename;
+  std::string path_lockdir;
+  std::string path_lock;
+  unsigned int maxiter;
+  const static int pointerlen = 16;
+  std::string lockname;
+  std::string pmarker;
+  int pmarkerlen;
+  int shelltype;
+  void setpath_link(std::string filename);
+  int getlock(void);
+  void removelock();
+  void setpath_lock(std::string p);
+  void setbaselinkfilename(std::string filename);
+  void createlinkfilename(void);
+  void removedanglinglinks(void);
+  int remove_lock_and_links(const std::string path, const std::string dirl, 
+      const std::string path_lock, int timer);
+  void remove_lock_and_links(void);
+  int realnext(void);
+  void debugger(const std::string s);
+  std::string createrc(void);
+  
+  public:
+  void setdebug();
+  static const std::string defpath_pool;
+  std::string defpath_lockdir;
+  static const std::string definputfilename;
+  disparm(void);
+  void setinputfilename(std::string inputfilename);
+  void setmaxiter(int maxiter);
+  std::streampos getkey(void);
+  int create(std::string &s);
+  int next(std::string& s);
+  int ready(void);
+  int REMOVE(void);
+  int getstat(int& records, int& committed, int& ready);
+  int echounhandled(void);
+  void setpath_pool(std::string pool);
+  int setpath_lockdir(std::string lockdir);
+  void setkey(std::streampos key);
+  friend std::ostream& operator <<(std::ostream &os,disparm &obj);
+  stats getstats();
+};
Index: /tags/1.0/disparm.txt
===================================================================
--- /tags/1.0/disparm.txt	(revision 5)
+++ /tags/1.0/disparm.txt	(revision 5)
@@ -0,0 +1,62 @@
+disparm acts as a server for text lines. These lines constitute
+in general parameters for a command or a whole commandline.
+
+Using dispar it is possible to arrange that each of many processes,
+in general running concurrently on many nodes gets it's own parameterlist.
+
+The parameterlists are put in a simple database, residing on a shared
+file system, which must be readable and writable on the nodes by the
+user of dispar.
+
+Example:
+
+- Create the parameter database
+  First, create a text file, each line with a parameter set, for example:
+
+  seq 100 > parmfile
+
+  (this results in a simple parameter file, each line containing one of
+  the numbers 1-100)
+
+  Then, create the database to be used by disparm:
+
+  disparm -c -i parmfile
+
+  This creates a parameter database with the name 'parmfile.disparm'.
+  It is a text file, to be managed by subsequent calls to dispar.
+
+- Get the next, not already used, line:
+
+  eval "`dispar -n`"
+
+  This will create the following environment variables:
+
+  DISPAR_VALUE: the line exactly as present in the original parameter file
+  DISPAR_KEY: to be used at a next 'dispar -r' call, see below
+  DISPAR_STATUS: the status of the line in DISPAR, always 0 in
+                 this version of dispar
+  DISPAR_COMMITTED: the number of times this line has been produced
+                    (minimum 1)
+  DISPAR_RC: this value should be checked first: only if the value is 'OK',
+             other values are meaningful
+
+  A shell loop, using dispar to get the next parameters and start a program
+  using these parameters:
+
+    while true ; do
+      dispar -n
+      if ("$DISPAR_RC != "OK") ; then
+        break
+      fi
+      eval "myprogram $DISPAR_VALUE"
+    done
+
+  If many nodes are running this loop, and if the parameterdatabase is on a
+  filesystem shared by the nodes (a common case in clusters), the program
+  'myprogram' will run many times on those nodes. Every run will use another
+  line of the original parameterfile.
+
+  Using the -m flag, you can tell dispar to produce a line more than once.
+  This is useful if you submit a number of jobs, each containing loops as
+  above, and you expect that some jobs will fail because of a time-limit
+  for example. 
Index: /tags/1.0/disparmcmd.1
===================================================================
--- /tags/1.0/disparmcmd.1	(revision 5)
+++ /tags/1.0/disparmcmd.1	(revision 5)
@@ -0,0 +1,12 @@
+.TH DISPARMCMD 1 
+.SH NAME
+disparmcmd \- manage string pool
+.SH DESCRIPTION
+disparcom is an utility to distribute 
+strings (parameters) in a pool, in an orderly way.
+
+Normally, disparmcmd is called within a shell function
+or an alias, that evals the standard output of disparmcmd.
+
+See man(1) disparm for a complete description.
+
Index: /tags/1.0/disparmmain.cpp
===================================================================
--- /tags/1.0/disparmmain.cpp	(revision 5)
+++ /tags/1.0/disparmmain.cpp	(revision 5)
@@ -0,0 +1,168 @@
+#include <iostream>
+#include <getopt.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include "tofromstring.h"
+#include "disparm.h"
+
+const std::string programname = "disparm";
+void usage()
+{
+  disparm d=disparm();
+  std::cerr << "Usage:" << std::endl;
+
+  std::cerr << programname << " -h,--help : this message" << std::endl;
+  std::cerr << programname << " -c,--create [-p,--pool POOL] [-i,--input   INPUTFILE]" << std::endl;
+  std::cerr << programname << " -n,--next   [-p,--pool POOL] [-d --lockdir LOCKDIR  ] [-k,--key KEY] [-m,--max MAX]" << std::endl;
+  std::cerr << programname << " -r,--ready  [-p,--pool POOL] [-d --lockdir LOCKDIR  ] [-k,--key KEY]" << std::endl;
+  std::cerr << programname << " -s,--stats  [-p,--pool POOL] [-d --lockdir LOCKDIR  ]" << std::endl;
+  std::cerr << programname << " -u,--unhandled  [-p,--pool POOL] [-d --lockdir LOCKDIR  ]" << std::endl;
+  std::cerr << "Defaults:" << std::endl;
+  std::cerr << "  POOL     : " << d.defpath_pool << std::endl;
+  std::cerr << "  LOCKDIR  : " << d.defpath_lockdir  << std::endl;
+  std::cerr << "  MAX      : " << "1" << std::endl;
+  std::cerr << "  INPUTFILE: " << d.definputfilename;
+  if (d.definputfilename == "-")
+    std::cerr << " (standard input)";
+  std::cerr << std::endl;
+}
+
+int main(int argc, char*argv[])
+{
+  disparm disparmthing=disparm();
+  bool nextflag       = 0;
+  bool createflag     = 0;
+  bool readyflag      = 0;
+  bool REMOVEFLAG     = 0;
+  bool statsflag      = 0;
+  bool unhandledflag  = 0;
+  bool lockdirset     = 0;
+  while(1)
+  {
+    static struct option long_options[] =
+    {
+      {"help",     0, 0, 'h'},
+      {"pool",     1, 0, 'p'},
+      {"create",   0, 0, 'c'},
+      {"input",    1, 0, 'i'},
+      {"next",     0, 0, 'n'},
+      {"max",      1, 0, 'm'},
+      {"ready",    0, 0, 'r'},
+      {"stats",    0, 0, 's'},
+      {"lockdir",  1, 0, 'd'},
+      {"key",      1, 0, 'k'},
+      {"REMOVE",   0, 0, 'R'},
+      {"unhandled",0, 0, 'u'},
+      {"debug",    0, 0, 'D'},
+      {0,          0, 0, 0}
+    };
+    int c = getopt_long(argc,argv,"hp:ci:nm:rsuDd:k:R",long_options,0);
+    if (c == -1)
+    {
+      if (optind < argc)  // unexpected extra argument
+      {
+	std::cerr << "Unexpected argument:" << argv[optind] << std::endl;
+	return 1;
+      }
+      break;
+    }
+    if ( c == '?' || c == ':' )
+    {
+      std::cerr << "Invalid parameter, try "<< argv[0]<<" --help " <<std::endl;
+      return 1;
+    }
+    switch(c)
+    {
+      case 'h':
+	usage();
+	break;
+      case 'p':
+	disparmthing.setpath_pool(std::string(optarg));
+	if (!lockdirset)
+	{
+	  std::string d = dirname(optarg);
+	  int rc = disparmthing.setpath_lockdir(d);
+	  if (rc != 0)
+	  {
+	    std::cerr << "Cannot create directory '"<< d << "'" << std::endl;
+	    return 1;
+	  }
+	}
+	break;
+      case 'c':
+	createflag = 1;
+	break;
+      case 'k':
+	disparmthing.setkey(from_string <long long>(optarg));
+	break;
+      case 'i':
+	 disparmthing.setinputfilename(std::string(optarg));
+	break;
+      case 'n':
+	nextflag = 1;
+	break;
+      case 'm':
+	disparmthing.setmaxiter(atoi(optarg));
+	break;
+      case 'r':
+	readyflag = 1;
+	break;
+      case 'R':
+	REMOVEFLAG = 1;
+	break;
+      case 's':
+	statsflag = 1;
+	break;
+      case 'd':
+	{
+	  int rc = disparmthing.setpath_lockdir(std::string(optarg));
+	  if (rc != 0)
+	  {
+	    std::cerr << "Cannot create directory '"<<optarg<<"'"<<std::endl;
+	    return 1;
+	  }
+	}
+	lockdirset = 1;
+	break;
+      case 'u':
+	unhandledflag = 1;
+	break;
+      case 'D':
+	disparmthing.setdebug();
+	break;
+    }
+  }
+
+  int rc=0;
+  if(createflag)
+  {
+    std::string s;
+    rc |= disparmthing.create(s);
+    std::cout << s;
+    if (rc != 0)
+      exit(1);
+  }
+  if(nextflag)
+  {
+    std::string s;
+    rc |= disparmthing.next(s);
+    std::cout << s;
+  }
+  if(readyflag)
+  {
+    rc |= disparmthing.ready();
+  }
+  if(REMOVEFLAG)
+  {
+    rc |= disparmthing.REMOVE();
+  }
+  if(statsflag)
+  {
+    std::cerr << disparmthing << std::endl;
+  }
+  if(unhandledflag)
+  {
+    rc |=disparmthing.echounhandled();
+  }
+  return rc;
+}
Index: /tags/1.0/ex.sh
===================================================================
--- /tags/1.0/ex.sh	(revision 5)
+++ /tags/1.0/ex.sh	(revision 5)
@@ -0,0 +1,3 @@
+z="aa00:n   a  : aaa"
+echo "${z#*:}"
+echo "${z%%:*}"
Index: /tags/1.0/job
===================================================================
--- /tags/1.0/job	(revision 5)
+++ /tags/1.0/job	(revision 5)
@@ -0,0 +1,24 @@
+#PBS -lnodes=1 -lwalltime=2301 
+cd $PBS_O_WORKDIR/jobdir || exit
+ncores=`cat /proc/cpuinfo|grep bogo|wc -l`
+for i in `seq $ncores` ; do
+(
+  while true ; do
+    eval "`../locker -n -p hoppa -m 3 -d lockdir`"
+    case "$LOCKER_RC" in
+    OK)
+	    sleep $(( RANDOM%60 ))
+	    echo "$LOCKER_VALUE" > file.`printf "%04d" "$LOCKER_VALUE"`.`hostname`.$i
+	    echo "$LOCKER_KEY" | ../locker -p hoppa -r -d lockdir
+	    #b=${a#*:}
+	    #echo "$a" > file.`printf "%04d" "$b"`.`hostname`.$i
+	    #echo "$a" | ../locker -p hoppa -r
+	    ;;
+    *) echo "Klaar $LOCKER_RC"
+       break
+       ;;
+    esac
+  done
+)&
+done
+wait
Index: /tags/1.0/jobs/job1
===================================================================
--- /tags/1.0/jobs/job1	(revision 5)
+++ /tags/1.0/jobs/job1	(revision 5)
@@ -0,0 +1,30 @@
+#PBS -lnodes=1 -lwalltime=360
+module load disparm
+root=$HOME/disparm/jobs
+scanner=$root/scanner
+workdir=$root/workdir
+mkdir -p $workdir
+cd $workdir|| exit
+poolfile=$root/pool1
+ncores=`sara-get-num-cores`
+outdir=$root/outdir
+mkdir -p $outdir
+for ((i=1; i<=ncores; i++)) ; do
+(
+  alias
+  while true ; do
+    disparm -n -p $poolfile -m 3
+    if [ "$DISPARM_RC" != "OK" ] ; then
+      break
+    fi
+    outfile=`tempfile`
+    outname=${DISPARM_VALUE// /_}.`basename $outfile`
+    eval "$scanner $DISPARM_VALUE > $outfile"
+    mv $outfile $outdir/$outname
+    date > $outdir/$outname.done
+    disparm -r -p $poolfile
+  done
+) &
+done
+wait
+
Index: /tags/1.0/jobs/job2
===================================================================
--- /tags/1.0/jobs/job2	(revision 5)
+++ /tags/1.0/jobs/job2	(revision 5)
@@ -0,0 +1,30 @@
+#PBS -lnodes=1 -lwalltime=1500
+module load disparm
+root=$HOME/disparm/jobs
+workdir=$root/workdir2
+mkdir -p $workdir
+cd $workdir|| exit
+poolfile=$root/pool2
+ncores=`sara-get-num-cores`
+outdir=$root/outdir2
+mkdir -p $outdir
+for ((i=1; i<=ncores; i++)) ; do
+(
+  while true ; do
+    disparm -n -p $poolfile -m 3
+    if [ "$DISPARM_RC" != "OK" ] ; then
+      break
+    fi
+    outfile=`tempfile`
+    outname=${DISPARM_VALUE// /_}.`basename $outfile`
+    sleep 120
+    eval "echo $DISPARM_VALUE > $outfile"
+    mv $outfile $outdir/$outname
+    date > $outdir/$outname.done
+    disparm -r -p $poolfile
+  done
+  echo DISPARM_RC: $DISPARM_RC
+) &
+done
+wait
+
Index: /tags/1.0/jobs/jobje
===================================================================
--- /tags/1.0/jobs/jobje	(revision 5)
+++ /tags/1.0/jobs/jobje	(revision 5)
@@ -0,0 +1,4 @@
+#PBS -lnodes=1 -lwalltime=300
+#PBS -S /bin/csh
+alias hoppa 'date'
+hoppa
Index: /tags/1.0/makefunction
===================================================================
--- /tags/1.0/makefunction	(revision 5)
+++ /tags/1.0/makefunction	(revision 5)
@@ -0,0 +1,1 @@
+eval 'disparm() { eval "`'$PWD'/disparmcmd "$@"`"; }'
Index: /tags/1.0/makeit
===================================================================
--- /tags/1.0/makeit	(revision 5)
+++ /tags/1.0/makeit	(revision 5)
@@ -0,0 +1,18 @@
+#!/bin/bash
+umask 022
+case `hostname` in
+  *r7n1*|piccolo) installdir=/sara/sw/disparm-1.0
+    ;;
+  *) installdir=$HOME/myroot/sara/sw/disparm-1.0
+    ;;
+esac
+mkdir -p $installdir/{bin,man/man1}
+chmod 755 $installdir
+chmod 755 $installdir/{bin,man,man/man1}
+make || exit
+echo installing in $installdir :
+cp disparmcmd sara-get-mem-size sara-get-num-cores $installdir/bin || exit
+cp disparm.1 disparmcmd.1 sara-get-mem-size.1 sara-get-num-cores.1 $installdir/man/man1 || exit
+chmod 755 $installdir/bin/* || exit
+chmod 644 $installdir/man/man1/* || exit
+echo done
Index: /tags/1.0/modulefile
===================================================================
--- /tags/1.0/modulefile	(revision 5)
+++ /tags/1.0/modulefile	(revision 5)
@@ -0,0 +1,27 @@
+#%Module
+##
+##        disparm 1.0
+##
+
+proc ModulesHelp { } {
+        puts stderr "Sets up paths you need to use the disparm software"
+}
+
+set root /sara/sw/disparm-1.0
+
+if { [ module-info shelltype ] == "csh" } {
+  set-alias disparm  "eval \"`$root/bin/disparmcmd $*`\""
+} else { 
+         if { [module-info mode] == "load" } {
+
+#         set-alias disparm "disparm_helper() { eval \"`$root/bin/disparmcmd \"$@\"`\"; }; disparm_helper"
+	    puts stdout "disparm() { eval \"`$root/bin/disparmcmd \"$@\"`\"; }; export -f disparm;"
+	 } else {
+	    puts stdout "unset disparm;"
+	 }
+}
+
+prepend-path PATH               $root/bin
+prepend-path MANPATH            $root/man
+
+setenv SARA_DISPARM_PATH          $root
Index: /tags/1.0/sara-get-mem-size
===================================================================
--- /tags/1.0/sara-get-mem-size	(revision 5)
+++ /tags/1.0/sara-get-mem-size	(revision 5)
@@ -0,0 +1,3 @@
+#!/bin/bash
+cat /proc/meminfo | awk '
+  /MemTotal:/ { print int($2/1000000) ; exit }'
Index: /tags/1.0/sara-get-mem-size.1
===================================================================
--- /tags/1.0/sara-get-mem-size.1	(revision 5)
+++ /tags/1.0/sara-get-mem-size.1	(revision 5)
@@ -0,0 +1,17 @@
+.TH SARA-GET-MEM-SIZE 1 
+.SH NAME
+sara-get-mem-size \-  get the amount of memory on the node
+.SH SYNOPSIS
+.B sara-get-mem-size
+.SH DESCRIPTION
+.I Sara-get-mem-size
+Writes the amount of memory in Mbytes to standard output.
+
+.TP 8
+.SH "SEE ALSO"
+sara-get-num-cores(1), topos(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.0/sara-get-num-cores
===================================================================
--- /tags/1.0/sara-get-num-cores	(revision 5)
+++ /tags/1.0/sara-get-num-cores	(revision 5)
@@ -0,0 +1,4 @@
+#!/bin/bash
+cat /proc/cpuinfo | awk '
+  /processor/ { p = $3 }
+  END { print p+1 }'
Index: /tags/1.0/sara-get-num-cores.1
===================================================================
--- /tags/1.0/sara-get-num-cores.1	(revision 5)
+++ /tags/1.0/sara-get-num-cores.1	(revision 5)
@@ -0,0 +1,17 @@
+.TH SARA-GET-NUM-CORES 1 
+.SH NAME
+sara-get-num-cores \-  get the number of cores on the node
+.SH SYNOPSIS
+.B sara-get-num-cores
+.SH DESCRIPTION
+.I Sara-get-num-cores
+Writes the number of cores in the node to standard output.
+
+.TP 8
+.SH "SEE ALSO"
+sara-get-mem-size(1), topos(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.0/shellenv.cpp
===================================================================
--- /tags/1.0/shellenv.cpp	(revision 5)
+++ /tags/1.0/shellenv.cpp	(revision 5)
@@ -0,0 +1,38 @@
+#include <string>
+#include "shellenv.h"
+//
+// Example:
+//   shellenv("aap noot mies", "LEESPLANK", SHELL_SH);
+// returns the string:
+//   LEESPLANK='aap noot mies';export LEESPLANK;
+//
+//  "'" characters in s are escaped
+//
+std::string shellenv(std::string s, const std::string name, int shelltype)
+{
+  std::string r = "'";
+  std::string::iterator it;
+  for ( it=s.begin() ; it < s.end(); it++ )
+  {
+    if ( *it == '\'')
+    {
+      r.push_back('\'');
+      r.push_back('\\');
+      r.push_back('\'');
+      r.push_back('\'');
+    }
+    else
+      r.push_back(*it);
+  }
+  r.push_back('\'');
+  switch (shelltype)
+  {
+    case SHELL_CSH:
+      r = "setenv " + name + " " + r + ";\n";
+      break;
+    case SHELL_SH: 
+      r = name + "=" + r + ";" + "export " + name + ";\n";
+      break;
+  }
+  return r;
+}
Index: /tags/1.0/shellenv.h
===================================================================
--- /tags/1.0/shellenv.h	(revision 5)
+++ /tags/1.0/shellenv.h	(revision 5)
@@ -0,0 +1,3 @@
+const int SHELL_SH = 0;
+const int SHELL_CSH = 1;
+std::string shellenv(const std::string s, const std::string name, int shelltype);
Index: /tags/1.0/test/dir.c
===================================================================
--- /tags/1.0/test/dir.c	(revision 5)
+++ /tags/1.0/test/dir.c	(revision 5)
@@ -0,0 +1,25 @@
+#include <errno.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main()
+{
+ DIR *pdir;
+ struct dirent *pent;
+
+ pdir=opendir("."); //"." refers to the current dir
+ if (!pdir){
+ printf ("opendir() failure; terminating");
+ exit(1);
+ }
+ errno=0;
+ while ((pent=readdir(pdir))){
+  printf("%s\n", pent->d_name);
+ }
+ if (errno){
+ printf ("readdir() failure; terminating");
+ exit(1);
+ }
+ closedir(pdir);
+}
Index: /tags/1.0/test/remove_links_main.cpp
===================================================================
--- /tags/1.0/test/remove_links_main.cpp	(revision 5)
+++ /tags/1.0/test/remove_links_main.cpp	(revision 5)
@@ -0,0 +1,89 @@
+#include <errno.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <iostream>
+
+int remove_links(const std::string path, const std::string dirl, int timer)
+{
+  // removes hard links in directory "dirl" to file "path" 
+  // in directory "dir", if they are older than "time" seconds. 
+  // Method: 
+  // using readdir, examine all components of dirl and see
+  // if they have the same inode as "path". If so, remove, unless
+  // the realname is the same as the realname of "path"
+  //
+  DIR *pdir;
+  struct dirent *pent;
+  int rc;
+  struct stat buf;
+
+  pdir = opendir(dirl.c_str());
+  if (!pdir)
+  {
+    std::cerr << "Cannot read link directory '"<<dirl<<"'"<<std::endl;
+    return 1;
+  }
+  rc = stat(path.c_str(),&buf);
+  if (rc !=0)
+  {
+    std::cerr << "Cannot stat '"<<path<<"'"<<std::endl;
+    return 1;
+  }
+  ino_t inodenr = buf.st_ino;
+  char rpathc[PATH_MAX+1];
+  realpath(path.c_str(),rpathc);
+  std::string rpath = rpathc;
+  std::cerr<<"TD: basefile '"<<rpath<<"'"<<std::endl;
+  std::string rpath1;
+  
+  while ((pent=readdir(pdir)))
+  {
+    struct stat buf;
+    std::string name = pent->d_name;
+    if (name == ".") continue;
+    if (name == "..") continue;
+    realpath((dirl+"/"+name).c_str(),rpathc);
+    rpath1 = rpathc;
+    std::cerr<<"TD: looking at '"<<rpath1<<"'"<<std::endl;
+    if (rpath != rpath1)
+    {
+      int rc = stat(rpath1.c_str(),&buf);
+      if (rc != 0)
+      {
+	std::cerr<<"Cannot stat '"<<rpath1<<"'"<<" this cannot happen!" << std::endl;
+	return 1;
+      }
+      if (buf.st_ino == inodenr)
+      {
+	std::cerr << "time: " << time(0) << " " << buf.st_mtime << " " << timer << std::endl;
+	if (time(0) > buf.st_mtime + timer)
+	{
+	  int rc = unlink(rpath1.c_str());
+	  if (rc == 0)
+	    std::cerr<<"Unlinked '"<<rpath1<<"'"<<std::endl;
+	  else
+	    std::cerr<<"Could not unlink '"<<rpath1<<"'"<<std::endl;
+	}
+	else
+	  std::cerr << "TD: not unlinking because time: '"<<rpath1<<"'"<<std::endl;
+      }
+    }
+    else
+      std::cerr<<"TD: not unlinking '"<<rpath1<<"'"<<std::endl;
+  }
+  return 0;
+}
+int main()
+{
+  std::string poolfilename="pool";
+  std::string linkdirname="linkdir";
+  int timer = 60;
+  remove_links(poolfilename, linkdirname,timer);
+  return 0;
+}
Index: /tags/1.0/test/stattest.cpp
===================================================================
--- /tags/1.0/test/stattest.cpp	(revision 5)
+++ /tags/1.0/test/stattest.cpp	(revision 5)
@@ -0,0 +1,13 @@
+#include <iostream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int main()
+{
+  struct stat buf;
+  int rc = stat("aap",&buf);
+  std::cout << "rc: " << rc << std::endl;
+  std::cout << "mtime: " << buf.st_mtime << std::endl;
+  return 0;
+}
Index: /tags/1.0/tofromstring.h
===================================================================
--- /tags/1.0/tofromstring.h	(revision 5)
+++ /tags/1.0/tofromstring.h	(revision 5)
@@ -0,0 +1,25 @@
+#include <sstream>
+#include <iomanip>
+template <class T>
+inline std::string to_string (const T& t)
+{
+  std::stringstream ss;
+  ss << t;
+  return ss.str();
+}
+template <class T>
+inline std::string to_string (const T& t,int w)
+{
+  std::stringstream ss;
+  ss << std::setfill('0') << std::setw(w) << t;
+  return ss.str();
+}
+template <class T>
+inline T from_string (std::string t)
+{
+  std::stringstream ss;
+  ss.str(t);
+  T x;
+  ss >> x;
+  return x;
+}
Index: /tags/1.0/token.cpp
===================================================================
--- /tags/1.0/token.cpp	(revision 5)
+++ /tags/1.0/token.cpp	(revision 5)
@@ -0,0 +1,236 @@
+#include <fstream>
+#include "token.h"
+#include "tofromstring.h"
+
+//
+// a token is a record in the pool file.
+// Format:
+// nmmmsssssssssssssssssssssssssssssssssssssssssssssssssssssssss
+// ||  |
+// ||  string
+// |number of times this string has been geven out   
+// 0: not ready. 1: ready
+// 
+// constructor:
+token::token()
+{
+  this->setvalue("");    // value (string)
+  this->setlinenr(0);    // line number
+  this->clearstatus();   // status: ready or not
+  this->setcommitted(0); // counter
+  this->pmarker = "pp";  // this marks the start of the pointer record
+  this->pmarkerlen = this->pmarker.length();
+}
+void token::clearstatus()
+{
+  this->status = '0';
+}
+char token::getstatus(void)
+{
+  return this->status;
+}
+void token::ready()
+{
+  this->status = '1';
+}
+bool token::getready(void)
+{
+  return this->status == '1';
+}
+void token::setvalue(std::string v)
+{
+  this->value = v;
+}
+void token::setlinenr(unsigned int l)
+{
+  this->linenr = l;
+}
+unsigned int token::getlinenr(void)
+{
+  return this->linenr;
+}
+std::string token::getvalue(void)
+{
+  return this->value;
+}
+void token::setcommitted(unsigned int n)
+{
+  this->committed = n;
+}
+unsigned int token::getcommitted(void)
+{
+  return this->committed;
+}
+std::string token::gettotal(void)
+{
+  return this->status + to_string(this->committed,comlen) + 
+            to_string(this->linenr,linenrlen)+ this->value;
+}
+//
+// puts everything that we know in repective variables
+//
+int token::puttotal(std::string t)
+{
+  if (t.length() < this->valuestart)
+    return 1;
+  // check that all chars before the value are [0-9]:
+  for (unsigned int i=0; i<valuestart; i++)
+  {
+    if (t[i] < '0' || t[i] > '9')
+      return 1;
+  }
+
+  switch (t[this->statstart])
+  {
+    case '0': break;
+    case '1': break;
+    default: return 1;
+             break;
+  }
+
+  this->committed = from_string<long>(t.substr(comstart,comlen));
+  this->status = t[0];
+  this->linenr = from_string<unsigned int>(t.substr(linenrstart,linenrlen));
+  if (t.length() == valuestart)
+    this->value="";
+  else
+    this->value=t.substr(valuestart);
+  return 0;
+}
+//
+// the file with the tokens
+//
+void token::setfile(std::fstream& file)
+{
+  this->file = &file;
+}
+
+//
+// read token from file, from position pos, possibly
+// wrap around
+// on return: posused is used position
+//            posnext is position of next token
+//
+int token::readnext(std::streampos pos, std::streampos& posused, std::streampos& posnext)
+{
+  //
+  // note: file is member of class token
+  //
+  posused = pos;
+  file->seekg(posused);
+  std::string line;
+  getline(*file,line);
+  if (file->eof() || file->fail() || file->bad())
+    return -1;
+  if (line.substr(0,this->pmarkerlen) == this->pmarker)
+  {
+    posused = 0;
+    file->seekg(posused);
+    getline(*file,line);
+    if (line.substr(0,this->pmarkerlen) == this->pmarker)
+      return -1;
+  }
+  if (file->eof() || file->fail() || file->bad())
+    return -1;
+  posnext = file->tellg();
+  return puttotal(line);
+}
+
+//
+// rewrite token on position p
+// on return, p contains the postion of the next token
+//
+int token::writetoken(std::streampos& p)
+{
+  this->file->seekp(p);
+  *file << this->gettotal() << std::endl;
+  p = this->file->tellp();
+  this->file->flush();
+
+  return 0;
+}
+
+//
+// rewrites the location pointer
+//
+int token::writelocpointer(std::streampos locpointer)
+{
+  //
+  // note: in this function, file is member of class token
+  //
+  std::streampos g = file->tellg();
+  std::streampos p = file->tellp();
+  //
+  // check if last line already contains locpointer
+  //
+  file->seekg(0,std::ios::end);
+  long long filelength = file->tellg();
+  long long l = this->pointerlen +this->pmarkerlen+2;
+  if ( l > filelength)
+  {
+    //
+    // this file has no locpointer
+    //
+    file->seekp(0,std::ios::end);
+  }
+  else
+  {
+    file->seekg(-l,std::ios::end);
+    char c  = file->get();
+    std::string line;
+    getline(*file,line);
+    if (line.length() == this->pointerlen + this->pmarkerlen
+	&& line.substr(0,pmarkerlen) == pmarker
+	&& c == '\n')
+    {
+      //
+      // this is a record with a locpointer value
+      //
+      file->seekp(-(std::streampos)((this->pointerlen)+this->pmarkerlen+1),std::ios::end);
+    }
+    else
+    {
+      file->seekp(0,std::ios::end);
+    }
+  }
+  *file << pmarker << std::setfill('0') << std::setw(this->pointerlen) <<
+    locpointer << std::endl;
+  file->flush();
+  file->seekg(g);
+  file->seekp(p);
+  return 0;
+}
+
+//
+// reads the location pinter
+// returns the value of the location pointer,
+//   -1 if error
+//
+std::streampos token::readlocpointer()
+{
+  std::streampos g = file->tellg();
+
+  long long l = this->pointerlen + this->pmarkerlen +2;
+  file->seekg(0,std::ios::end);
+  char c;
+  if (file->tellg() < l)
+  {
+    file->seekg(0);
+    c = '\n';
+  }
+  else
+  {
+    file->seekg(-l,std::ios::end);
+    c  = file->get();
+  }
+  std::string line;
+  getline(*file,line);
+  if (line.length() == this->pointerlen + this->pmarkerlen
+      && line.substr(0,pmarkerlen) == pmarker
+      && c == '\n')
+    return from_string<long>(line.substr(2));
+  else
+    return -1;
+  file->seekg(g);
+  return 0;
+}
Index: /tags/1.0/token.h
===================================================================
--- /tags/1.0/token.h	(revision 5)
+++ /tags/1.0/token.h	(revision 5)
@@ -0,0 +1,43 @@
+class token{
+  private:
+    std::string value;
+    long id;
+    unsigned int committed;
+    std::fstream *file;
+    char status;
+    unsigned int pmarkerlen;
+    std::string pmarker;
+    //                                        following lenths in char:
+    static const unsigned int pointerlen = 16;    // length of pointer
+    static const unsigned int statstart = 0;
+    static const unsigned int statlen =     1;    // length of status
+    static const unsigned int comstart = statstart + statlen;
+    static const unsigned int comlen =      4;    // length of nr commits
+    static const unsigned int linenrstart = comstart + comlen;
+    static const unsigned int linenrlen =  10;    // length of linenr
+    static const unsigned int valuestart = linenrstart + linenrlen;
+    unsigned int linenr;
+    unsigned int lenlen;
+  public:
+    token(void);
+    void setid(long id);
+    long getid(void);
+    void ready(void);
+    bool getready(void);
+    void clearstatus(void);
+    char getstatus(void);
+    void setlinenr(unsigned int l);
+    unsigned int getlinenr(void);
+    void setvalue(std::string v);
+    std::string getvalue(void);
+    void setcommitted(unsigned int c);
+    unsigned int getcommitted(void);
+    void inccommitted(void);
+    std::string gettotal(void);
+    int puttotal(std::string t);
+    void setfile(std::fstream& file);
+    int readnext(std::streampos pos, std::streampos& posused,std::streampos& posnext);
+    int writetoken(std::streampos& p);
+    int writelocpointer(std::streampos locpointer);
+    std::streampos readlocpointer(void);
+};
Index: /tags/1.1/Makefile
===================================================================
--- /tags/1.1/Makefile	(revision 5)
+++ /tags/1.1/Makefile	(revision 5)
@@ -0,0 +1,24 @@
+CXX = g++
+CXXFLAGS = -g -O3 -Wall
+
+OBJS = disparm.o disparmmain.o token.o shellenv.o clocks.o
+SRCS = $(OBJS:.o=.cpp)
+
+all: disparmcmd
+
+disparmcmd: $(OBJS)
+	$(CXX) -o $@ $(CXXFLAGS) $(OBJS)
+
+%.o: 	%.cpp
+	$(CXX) $(CXXFLAGS) -c -o $@ $<
+
+clean:
+	rm -f disparmcmd $(OBJS) core .depend
+
+include .depend
+
+dep .depend:
+	$(CXX) $(CXXFLAGS) -M $(SRCS) > .depend
+
+test:
+	echo $(SRCS)
Index: /tags/1.1/README
===================================================================
--- /tags/1.1/README	(revision 5)
+++ /tags/1.1/README	(revision 5)
@@ -0,0 +1,23 @@
+See man disparm for an explanation of the external specs of disparm.
+
+I is not easy to arrange in a nfs file system that only one process
+will change a file (the poolfile), and to ensure that this process
+gets the most up-to-date version of the file.
+
+In disparm this is accomplished by three steps:
+
+ - create a lock directory. This is an atomic operation, if
+   many processes are doing a 'mkdir' of the same directory,
+   only one process can succeed.
+ - create a hard link in that directory to the poolfile.
+   If the link count of that link equals 2, it is considered
+   successful, otherwize it is assumed that no exclusive
+   access to the file is possible at this moment.
+ - open de link with O_DIRECT, and read a block, to persuade the 
+   system to get the latest version of the file.
+
+If the lock cannot be obtained directly, it is tried some sconds later.
+
+
+
+
Index: /tags/1.1/clocks.cpp
===================================================================
--- /tags/1.1/clocks.cpp	(revision 5)
+++ /tags/1.1/clocks.cpp	(revision 5)
@@ -0,0 +1,55 @@
+#include <sys/times.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include "clocks.h"
+#include <unistd.h>
+double cputime() /* aix, xlf */
+{ struct tms b;
+  clock_t r;
+  times( &b);
+  r = b.tms_utime + b.tms_stime;
+  return ( (double) r/(double) sysconf(_SC_CLK_TCK));
+}
+double CPUTIME() /* cray  */
+{ 
+  return ( cputime());
+}
+double cputime_() /* g77, gcc */
+{ 
+  return ( cputime());
+}
+
+double wallclock() 
+{ 
+	struct timeval toot;
+	double r;
+
+	gettimeofday(&toot,0);
+	r=toot.tv_sec+0.000001*(double)toot.tv_usec;
+	return(r);
+}
+double WALLCLOCK()
+{
+  return (wallclock());
+}
+double wallclock_()
+{
+  return wallclock();
+}
+		
+void fortransleep(int *i)
+  {
+    sleep(*i);
+  }
+ 
+void FORTRANSLEEP(int *i)
+  {
+    sleep(*i);
+  }
+
+void fortransleep_(int *i)
+  {
+    sleep(*i);
+  }
+
Index: /tags/1.1/clocks.h
===================================================================
--- /tags/1.1/clocks.h	(revision 5)
+++ /tags/1.1/clocks.h	(revision 5)
@@ -0,0 +1,8 @@
+#ifdef __cplusplus
+extern "C"  {
+#endif
+double wallclock(void);
+double cputime(void);
+#ifdef __cplusplus
+}
+#endif
Index: /tags/1.1/disparm.1
===================================================================
--- /tags/1.1/disparm.1	(revision 5)
+++ /tags/1.1/disparm.1	(revision 5)
@@ -0,0 +1,152 @@
+.TH DISPARM 1 
+.SH NAME
+disparm \- alias using disparmcmd to call the program disparmcmd
+.SH SYNOPSIS
+.B disparm
+\fB\ -h,--help  \fP  
+.P 8
+.B disparm
+\fB\ -c,--create  \fP [\fB OPTIONS\fP ] [\fB -i,--input INPUTFILE \fB]
+.P 8
+.B disparm
+\fB\ -n,--next  \fP [\fB OPTIONS\fP ] [\fB -m,--max MAX \fB]
+.P 8
+.B disparm
+\fB\ -r,--ready  \fP [\fB OPTIONS\fP] [\fB -k, --key KEY \fP ]
+.P 8
+.B disparm
+\fB\ -R,--REMOVE  \fP [\fB OPTIONS\fP] [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -s,--stats [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -u,--unhandled [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -v,--version
+.P 8
+.B disparm
+\fB\ -D,--debug [\fB -p, --pool POOL \fP ]
+.SH DESCRIPTION
+disparm is an alias using disparmcmd, an utility to distribute 
+strings (parameters) in a pool, in an orderly way.
+In general, the
+strings are used as command parameters, see Examples.
+.SH COMMANDS
+.TP 4
+.B -h,--help
+Prints usage information.
+.TP 4
+.B -c,--create [ -i,--input INPUTFILE ] [ -p,--pool POOL ]
+Creates new pool and fills it with lines from INPUTFILE, default
+from stdin. INPUTFILENAME '-' stands for stdin.
+See OPTIONS for a description of POOL.
+The environment
+variable 'DISPARM_RC' is set, see ENVIRONMENT.
+.TP 4
+.B -n,--next [ -m,--max MAX ] [ -p,--pool POOL ]
+Gets the next string.
+The environment variables DISPARM_RC, DISPARM_VALUE, DISPARM_COMMITTED and DISPARM_KEY are set,
+see ENVIRONMENT.
+The same string in the pool will be
+produced MAX times, the default is 1. By setting this to a higher
+value, the same string will be produced more times. This can be useful
+for dealing with crashed jobs.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -r,--ready [ -k,--key KEY ] [-p,--pool POOL ]
+Marks the token with key KEY as ready. If not specified on the commandline, the
+value of environment variable DISPARM_KEY is used.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -R,--REMOVE --p,--pool POOL ]
+Removes the pool.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -s,--stats
+Prints to standard error:
+
+.br 
+total number of strings in pool
+.br 
+number of strings delivered
+.br
+number of strings marked as ready
+.br
+maximum one string has been delivered
+.TP 4
+.B -u,--unhandled
+Prints to standard error the strings that are committed, but not marked
+as ready.
+.TP 4
+.B -v,--version
+Prints to standard error the version number of disparm.
+.TP 4
+.B -D,--debug
+Prints to standard error some debug information.
+.SH OPTIONS
+All options accept the following general parameters:
+.TP 4
+.B -p,--pool POOL
+POOL is a unique name of the file that contains the pool. 
+Default: pool.disparm in the current directory.
+.TP 4
+.B -d,--lockdir LOCKDIR
+LOCKDIR is a directory to hold the lockfile (implemented as a hard
+link to the pool file). All lockfiles have different names,
+so it is permitted to use the same directory for all uses of disparm. 
+Default: the directory where the pool resides. 
+
+.SH ENVIRONMENT
+The following environment variables can be set by disparm:
+.P
+.B DISPARM_RC
+if the value is OK, than no errors were found
+.P
+.B DISPARM_KEY
+contains the keyvalue of the token
+.P
+.B DISPARM_LINENO
+contains the line number of the string in the file from which
+the pool file has been created
+.P
+.B DISPARM_COMMITTED
+contains the number of times the string has been committed before
+.P
+.B DISPARM_VALUE
+contains the token
+.SH EXAMPLES
+.TP 4
+
+Create a pool with the firat 10 lines from the man page of sed:
+
+ man sed | head > parmfile
+ disparm -c -p demo1 -i parmfile
+
+Get a line:
+
+ disparm -n -p demo1
+
+The environment variable DISPARM_VALUE contains now one of the
+first ten lines of the man page of sed.
+
+You can use this in a command like this:
+
+ eval "myparser $DISPARM_VALUE"
+
+The token can be marked as ready by:
+
+ disparm -r -p demo1
+
+Finally, the pool can be completely removed by:
+
+ disparm -R -p demo1
+
+.SH "SEE ALSO"
+sara-get-mem-size(1), sara-get-num-cores(1), disparmcmd(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.1/disparm.cpp
===================================================================
--- /tags/1.1/disparm.cpp	(revision 5)
+++ /tags/1.1/disparm.cpp	(revision 5)
@@ -0,0 +1,948 @@
+#include <iostream>
+#include <fstream>
+#include <libgen.h>
+#include <cstdlib>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "disparm.h"
+#include "tofromstring.h"
+#include "token.h"
+#include "shellenv.h"
+#include <errno.h>
+#include <dirent.h>
+
+const std::string disparm::keyname       = "DISPARM_KEY";
+const std::string disparm::committedname = "DISPARM_COMMITTED";
+const std::string disparm::valuename     = "DISPARM_VALUE";
+const std::string disparm::rcname        = "DISPARM_RC";
+const std::string disparm::linename      = "DISPARM_LINENO";
+
+const std::string disparm::defpath_pool   = "pool.disparm";
+const std::string disparm::definputfilename = "-";
+
+//
+//constructor disparm
+//
+disparm::disparm()
+{
+  char defpoolname_c[this->defpath_pool.length()+1];        // default path_pool
+  strcpy(defpoolname_c,this->defpath_pool.c_str());         
+  //
+  // A note about basename and dirname: they need as argument a char*
+  // NOT a const char*. Both functions may modify the argument,
+  // so, in general, do not use the same argument more than
+  // once.
+  //
+  char defpath_lockdir_c[this->defpath_lockdir.length()+1];  // default directory for the lockfile
+  strcpy(defpath_lockdir_c,this->defpath_lockdir.c_str());
+  this->setpath_pool(this->defpath_pool);
+  this->defpath_lockdir = std::string(dirname(defpoolname_c));
+  this->setpath_link("");
+  this->setpath_lock("");     // the name of the lock folder
+  this->setbaselinkfilename("");
+  this->setinputfilename(this->definputfilename);  // default input file name
+  this->setpath_lockdir(this->defpath_lockdir);      // default lock directory
+  this->setmaxiter(1);                             // default maximum times a string can be given out
+  this->pmarker="pp";                              // start of record with pointer to next string in pool
+  this->pmarkerlen = this->pmarker.length();
+  this->lockname="lock.disparm";
+
+  std::string shell = getenv("SHELL");
+  if (shell.find("csh") != std::string::npos) 
+    this->shelltype = SHELL_CSH;
+  else
+    this->shelltype = SHELL_SH;
+
+  //
+  // following items are returned on stdout as in:
+  // export DISPARM_KEY=1234
+  // See also above
+  //
+  this->key       = -1;     // key: pointer to next item in pool
+  this->status    = '0';
+  this->committed = 0;      // the number of times the string is given out
+  this->rc        = "OK";   // return code
+  this->value     = "";     // the string to give out
+  this->linenr    = 0;      // the line number in the pool
+
+  this->debug     = 0;
+
+  int rc = posix_memalign((void**)&buffer, 4096, 512);
+  if (rc != 0)
+    std::cerr << "Disparm: posix_memalign fails" << std::endl;
+}
+
+// destructor
+disparm::~disparm()
+{
+  free(this->buffer);
+}
+
+void disparm::removelock()
+{
+  unlink(this->path_link.c_str());
+  rmdir(this->path_lock.c_str());
+}
+
+void disparm::setpath_pool(std::string pool)
+{
+  this->path_pool = pool;
+}
+
+void disparm::setpath_link(std::string path_link)
+{
+  this->path_link = path_link;
+}
+
+void disparm::setpath_lock(std::string path_lock)
+{
+  this->path_lock = path_lock;
+}
+
+void disparm::setbaselinkfilename(std::string baselinkfilename)
+{
+  this->baselinkfilename = baselinkfilename;
+}
+
+int disparm::setpath_lockdir(std::string lockdir)
+{
+  this->path_lockdir = lockdir;
+  int rc = mkdir(path_lockdir.c_str(),0777);
+  if (rc ==0)
+    return 0;
+  else
+  {
+    if (errno == EEXIST)
+    {
+      struct stat buf;
+      stat(path_lockdir.c_str(),&buf);
+      if (S_ISDIR(buf.st_mode))
+	return 0;
+    }
+    else
+      return -1;
+  }
+  return 0;  // never reached
+}
+
+//
+// Create a name for the directory to be used as lock: this->path_lock
+// This directory will reside in this->path_lockdir
+// We try to generate a name for a linkfile that is unique.
+// In the name is also the creation time.
+// The link file will be placed in the lock directory
+//
+void disparm::createlinkfilename()
+{
+  if (this->debug)
+    this->debugger("Before createlinkfilename:");
+  char hostname[1025];
+  gethostname(hostname,1024);
+  char poolname_c[this->path_pool.length()+1];
+  strcpy(poolname_c,this->path_pool.c_str());
+  std::string poolnamebase = basename(poolname_c);
+  this->lockname=poolnamebase+".lock.disparm";
+  this->setpath_lock(this->path_lockdir + "/" + this->lockname);
+  std::string base = this->path_lock + "/" + poolnamebase + ".link.";
+  this->setbaselinkfilename(base);
+  this->setpath_link(   base
+                         + to_string(time(0))
+                         + "." 
+                         + hostname 
+                         + "." 
+                         + to_string(getpid()));
+  if (this->debug)
+    this->debugger("After createlinkfilename:");
+}
+
+void disparm::setinputfilename(std::string inputfilename)
+{
+  this->inputfilename = inputfilename;
+}
+
+void disparm::setkey(std::streampos key)
+{
+  this->key = key;
+}
+
+std::streampos disparm::getkey(void)
+{
+  return this->key;
+}
+
+void disparm::setmaxiter(int maxiter)
+{
+  this->maxiter = maxiter;
+}
+
+//
+// Create poolfile from input file
+//
+// On return, s wil contain the output of createrc
+//
+int disparm::create(std::string &s)
+{
+  if (this->debug)
+    this->debugger("Before create:");
+  std::ifstream inp;
+  std::istream *pinp;
+  s = "";
+  //
+  // open inputfile, take care that stdin is also allowed
+  //
+  if (this->inputfilename == "-")
+    pinp = &std::cin;
+  else
+  {
+    inp.open(this->inputfilename.c_str());
+    if (!inp.is_open())
+    {
+      std::cerr<<"Cannot open '"<<inputfilename<<"'"<<std::endl;
+      this->rc = "FAIL";
+      s = this->createrc();
+      return 1;
+    }
+    pinp = &inp;
+  }
+  //
+  // open poolfile
+  //
+  std::string line;
+  std::fstream out;
+  out.open(this->path_pool.c_str(),std::fstream::in | std::fstream::out
+                               |  std::fstream::trunc);
+
+  //
+  // fill poolfile
+  //
+  token tok;
+  tok.setfile(out);
+  std::streampos p;
+  unsigned int linenr = 0;
+  while (getline(*pinp,line))
+  {
+    tok.setlinenr(++linenr);
+    tok.setcommitted(0);
+    tok.clearstatus();
+    tok.setvalue(line);
+    tok.writetoken(p);
+  }
+  tok.writelocpointer(0);
+
+  //
+  // close input file, only if it is not stdin
+  //
+  if (pinp == &inp)
+    inp.close();
+  out.close();
+
+  this->key = 0;
+  s = createrc();
+  if (this->debug)
+    this->debugger("After create:");
+
+  return 0;
+}
+
+//
+// get next string from pool
+//
+int disparm::next(std::string& s)
+{
+  if (this->debug)
+    this->debugger("Before next:");
+  int rc = this->realnext();
+  s = this->createrc();
+  if (this->debug)
+    this->debugger("After next:");
+  return rc;
+}
+
+//
+// create string to be eval'd by the shell
+//
+std::string disparm::createrc(void)
+{
+  std::string s = "";
+  s = s + shellenv(to_string(this->key),      this->keyname,      this->shelltype);
+  s = s + shellenv(to_string(this->committed),this->committedname,this->shelltype);
+  s = s + shellenv(to_string(this->value),    this->valuename,    this->shelltype);
+  s = s + shellenv(to_string(this->rc),       this->rcname,       this->shelltype);
+  s = s + shellenv(to_string(this->linenr),   this->linename,     this->shelltype);
+  return s;
+}
+
+// 
+// get the next string from the pool
+//
+int disparm::realnext()
+{
+  std::fstream file;
+  bool good = 1;
+
+  this->key       = 0;
+  this->status    = '0';
+  this->committed = 0;
+  this->rc        = "OK";
+  this->linenr    = 0;
+  this->value     = "";
+
+  //
+  // get lock, return if that fails
+  //
+  if (this->getlock()!=0)
+  {
+    this->rc = "FAIL";
+    return 1;
+  }
+
+  //
+  // read next string from pool
+  // take care of ready records, maximum iterations
+  // allowed and some kinds of mishaps
+  //
+  // open first using O_DIRECT, to force a fresh cache.
+  int f = open(this->path_link.c_str(),O_DIRECT|O_RDONLY);
+  int bytesread = read(f,buffer,512);
+  if (bytesread <0)
+  {
+    std::cerr << "How strange: cannot read from poolfile." << std::endl;
+  }
+  close(f);
+  file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out);
+  if (file.is_open())
+  {
+    token       tok;
+    std::string line;
+    tok.setfile(file);
+    std::streampos locpointer, posused, posnext;
+
+    //
+    // get pointer to next string
+    //
+    locpointer = tok.readlocpointer();
+    if (locpointer < 0)
+    {
+      std::cerr << "Corrupt parameterfile: cannot find locpointer\n" << std::endl;
+      this->rc = "FAIL";
+      good = 0;
+    }
+    //
+    // read next string from pool
+    //
+    if (good)
+    {
+      int rc = tok.readnext(locpointer,posused,posnext);
+      if (rc != 0)
+      {
+        std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+        std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+        this->rc = "FAIL";
+        good = 0;
+      }
+    }
+    //
+    // keep reading until a valid record is found
+    // take appropriate action if no such record exists
+    //
+    if (good)
+    {
+      std::streampos locpointerold = locpointer;
+      bool allready = 1;  // to learn if all records are
+                          // ready
+      while (1)
+      {
+        if (!tok.getready())
+        {
+	  allready = 0;
+	  if (tok.getcommitted() >= this->maxiter)
+	  {
+	    good = 0;
+	    this->rc = "MAX_EXCEEDED";
+	    break;
+	  }
+          this->key = posused;
+          this->status = tok.getstatus();
+          this->committed = tok.getcommitted();
+          this->value = tok.getvalue();
+	  this->linenr = tok.getlinenr();
+          tok.setcommitted(tok.getcommitted()+1);
+          tok.writetoken(posused);
+          tok.writelocpointer(posused);
+          break;
+        }
+        locpointer = posnext;
+	if (locpointer == locpointerold)
+	{
+	  //
+	  // so we are cycling, nothing to find, it seems
+	  //
+	  good = 0;
+	  if (allready)
+	    this->rc = "ALL_READY";
+	  else
+	    this->rc = "MAX_EXCEEDED";
+	  break;
+	}
+        int rc = tok.readnext(locpointer,posused,posnext);
+        if (rc != 0)
+        {
+	  std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+          std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+          this->rc = "FAIL";
+          good = 0;
+          break;
+        }
+      }
+    }
+  }
+  else
+  {
+    std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+    std::cerr<< this->arg << ": Strange error: cannot open '"<<path_link<<"'"<<std::endl;
+    return 1;
+  }
+
+  file.close();
+  this->removelock();
+  return !good;
+}
+
+// 
+// get a lock on the poolfile
+//
+// The normal methods don't work on an nfs file system.
+// Luckily, there is a method based on the creation
+// of a directory, which is an atomic operation.
+// If we can create the directory, we are sure that no 
+// other instantiation of this program was also successful.
+// After successful creation of the lock directory, we create
+// a hard link to the poolfile, and use this link to read from and
+// write to. We do not use the poolfile itself to read and write,
+// we can never be sure in a nfs filesystem of the status of a 
+// file. It appears that creating a new hard link suffices to
+// get an up to date version of the poolfile.
+//
+int disparm::getlock(void)
+{
+  struct stat buf;
+  int rc;
+  int sleeptime=2;
+
+  int iter = 0;
+  int ntryremovelock = 0;
+  while(1)
+  {
+    rc  = stat(this->path_pool.c_str(),&buf);
+    std::string er="";
+    switch (errno)
+    {
+      case (EACCES): er="Search permission is denied for one of the  directories  in  the path prefix of path.  (See also path_resolution(7).)";
+		     break;
+
+      case(EBADF):  er="fd is bad.";
+		    break;
+
+      case(EFAULT): er="Bad address.";
+		    break;
+
+      case(ELOOP):  er="Too many symbolic links encountered while traversing the path.";
+		    break;
+
+      case(ENAMETOOLONG):er="File name too long.";
+			 break;
+
+      case(ENOENT): er="A component of path does not exist, or path is an empty string.";
+		    break;
+
+      case(ENOMEM): er="Out of memory (i.e., kernel memory).";
+		    break;
+
+      case(ENOTDIR):
+              er="A component of the path prefix of path is not a directory.";
+	      break;
+
+      case(EOVERFLOW):er="(stat())  path refers to a file whose size cannot be represented in the type off_t.  This can occur when an application  compiled on a 32-bit platform without -D_FILE_OFFSET_BITS=64 calls stat() on a file whose size exceeds (2<<31)-1 bits";
+		      break;
+
+    }
+    if (rc !=0 )
+    {
+      perror("Disparm: Error stat poolfile:");
+      std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+      std::cerr<<er<<std::endl;
+      std::cerr<< this->arg << ": Cannot stat the pool file:'"<<this->path_pool.c_str()<<"'"<< std::endl;
+      return 1;
+    }
+    this->createlinkfilename();
+    //
+    // Try to create the lock directory
+    //
+    rc = mkdir(this->path_lock.c_str(), 0700);
+    if (rc == 0)
+    {
+      // Creation of lock directory was successful, now create link
+      // to the poolfile
+      // As an extra check: check if the number of hard links to the 
+      // poolfile equals 2.
+      //
+      // Strange: sometimes, when many threads are involved on one node,
+      // the poolfile seems to be gone, get error: "no such file or directory"
+      // I guess this is a race condition in the NFS filesystem
+      // so, I'll try 10 times, and then give up.
+      //
+      int tries = 0;
+      while (1)
+      {
+	tries++;
+	int rc = link(this->path_pool.c_str(),this->path_link.c_str());
+	if (rc < 0)
+	{
+	  if (tries < 10)
+	  {
+	    sleep(1);
+	  }
+	  else
+	  {
+	    perror("Disparm: Error making link:");
+	    std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+	    std::cerr<< this->arg << ": Cannot make link '"<<this->path_link<<"' to '"
+	      <<this->path_pool<<"'"<<std::endl<<" Is poolname '"<<path_pool
+	      <<"' correct?"<<std::endl;
+	    return 1;
+	  }
+	}
+	else
+	  break;
+      }
+      //
+      // check if number of hard links equals 2
+      //
+      struct stat linkbuf;
+      rc = stat(this->path_link.c_str(),&linkbuf);
+      if (rc !=0)
+      {
+	std::cerr << this->arg << ": Cannot stat just created link'"<<this->path_link<<"'"<<std::endl;
+	return 1;
+      }
+      if (linkbuf.st_nlink == 2)
+      {
+	break;
+      }
+      else
+      {
+	rc = unlink(this->path_link.c_str());
+      }
+    }
+    //
+    // wait some time before trying again
+    //
+    iter++;
+#ifdef VERBOSE
+    std::cerr << iter <<": waiting for lock " << std::endl;
+#endif
+    sleep(sleeptime);
+    if (iter > 9)
+    {
+#ifdef VERBOSE
+      std::cerr << "Trying to remove locks" << std::endl;
+#endif
+      //
+      // remove stale link files
+      //
+      this->remove_lock_and_links();
+      ntryremovelock ++;
+      if (ntryremovelock > 100)
+      {
+	std::cerr << this->arg << ": Cannot get lock after many tries." << std::endl;
+	return 1;
+      }
+      iter = 0;
+    }
+  }
+  return 0;
+}
+
+//
+// mark string as ready
+//
+int disparm::ready()
+{
+  if (this->getlock()!=0)
+  {
+    return 1;
+  }
+
+  //
+  // if no key given as parameter, read it from environment
+  //
+  if (this->key < 0)
+  {
+    char * s = getenv(this->keyname.c_str());
+    if (s == NULL || *s == 0)
+    {
+      this->removelock();
+      std::cerr << this->arg << ": No key given" << std::endl;
+      return 1;
+    }
+    else
+    {
+      this->key = from_string<long long>(std::string(s));
+    }
+  }
+
+  std::streampos locpointer = this->key;
+  std::fstream file;
+
+  // open first using O_DIRECT, to force a fresh cache.
+  int f = open(this->path_link.c_str(),O_DIRECT|O_RDONLY);
+  int bytesread = read(f,buffer,512);
+  if (bytesread <0)
+  {
+    std::cerr << this->arg << ": cannot read from poolfile." << std::endl;
+    return 1;
+  }
+  close(f);
+  file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out);
+  if (file.is_open())
+  {
+    token tok;
+    std::string line;
+    tok.setfile(file);
+    std::streampos posused,posnext;
+    //
+    // read next record
+    //
+    int rc = tok.readnext(locpointer,posused,posnext);
+    if (rc != 0)
+    {
+      std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+      std::cerr << this->arg << ": Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+      file.close();
+      this->removelock();
+      return 1;
+    }
+    //
+    // mark record as ready
+    //
+    tok.ready();
+    //
+    // write record back
+    //
+    tok.writetoken(locpointer);
+  }
+  else
+  {
+    std::cerr << this->arg << ": Cannot read " << this->path_link << std::endl;
+  }
+  file.close();
+  this->removelock();
+  return 0;
+}
+
+//
+// remove poolfile
+//
+int disparm::REMOVE()
+{
+  if (this->getlock()!=0)
+  {
+    return 1;
+  }
+  this->removelock();
+
+  int rc = unlink(this->path_pool.c_str());
+  if (rc)
+  {
+    std::cerr << this->arg << ": Cannot remove " << this->path_pool << std::endl;
+    return 1;
+  }
+  return 0;
+}
+
+//
+// return statistics about the pool
+// no wait for lock, so be careful interpretating the results
+//
+stats disparm::getstats()
+{
+  stats statistics;
+  token tok;
+  std::fstream file;
+  statistics.valid     =  0;
+  statistics.records   = -1;
+  statistics.committed = -1;
+  statistics.ready     = -1;
+  statistics.commax    = -1;
+
+
+  file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out);
+
+  if (file.is_open())
+  {
+    tok.setfile(file);
+    std::streampos pos = 0;
+    std::streampos posused;
+    std::streampos posnext;
+    statistics.valid     = 1;
+    statistics.records   = 0;
+    statistics.committed = 0;
+    statistics.ready     = 0;
+    statistics.commax    = 0;
+    
+    while(1)
+    {
+      tok.readnext(pos, posused,posnext);
+      if (pos != posused)
+        break;
+      if (tok.getready())
+        statistics.ready++;
+      if (tok.getcommitted()>0)
+        statistics.committed++;
+      if (tok.getcommitted() > statistics.commax)
+        statistics.commax = tok.getcommitted();
+      statistics.records++;
+      pos = posnext;
+    }
+    file.close();
+  }
+  else
+  {
+    std::cerr << this->arg << ": Cannot open poolfile '"<<this->path_pool<<"'"<<std::endl;
+  }
+
+  return statistics;
+}
+
+int disparm::echounhandled(void)
+{
+  token tok;
+  std::fstream file;
+
+  file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out);
+
+  if (file.is_open())
+  {
+    tok.setfile(file);
+    std::streampos pos = 0;
+    std::streampos posused;
+    std::streampos posnext;
+    std::cerr << "linenr:committed:value"<< std::endl;
+    
+    while(1)
+    {
+      tok.readnext(pos, posused,posnext);
+      if (pos != posused)
+        break;
+
+      if (!tok.getready()&&tok.getcommitted()>0)
+      {
+	std::cerr << tok.getlinenr() << ":"<<tok.getcommitted()<<":" << tok.getvalue() << std::endl;
+      }
+      pos = posnext;
+    }
+  }
+  else
+  {
+    std::cerr << this->arg << ": Cannot open pool file '"<<this->path_pool<<"'"<<std::endl;
+    return 1;
+  }
+
+  file.close();
+  return 0;
+}
+
+
+int disparm::remove_lock_and_links(const std::string path, const std::string dirl, const std::string path_lock, int timer)
+{
+  // removes lock and hard links in directory "dirl" to file "path" 
+  // in directory "dir", if they are older than "time" seconds. 
+  // Method: 
+  // if lock is older than timer seconds, remove it.
+  // and:
+  // using readdir, examine all components of dirl and see
+  // if they have the same inode as "path". If so, remove, unless
+  // the realname is the same as the realname of "path"
+  //
+  DIR *pdir;
+  struct dirent *pent;
+  int rc;
+  struct stat buf;
+
+  pdir = opendir(dirl.c_str());
+  if (!pdir)
+  {
+    std::cerr << this->arg << ": Cannot read link directory '"<<dirl<<"'"<<std::endl;
+    return 1;
+  }
+  rc = stat(path.c_str(),&buf);
+  if (rc !=0)
+  {
+    std::cerr << this->arg << ": Cannot stat '"<<path<<"'"<<std::endl;
+    return 1;
+  }
+  ino_t inodenr = buf.st_ino;
+  char* rpathc = (char*)malloc(PATH_MAX+1);
+  rpathc = realpath(path.c_str(),rpathc);
+  std::string rpath = rpathc;
+  std::string rpath1;
+  
+  while ((pent=readdir(pdir)))
+  {
+    struct stat buf;
+    std::string name = pent->d_name;
+    if (name == ".") continue;
+    if (name == "..") continue;
+    rpathc = realpath((dirl+"/"+name).c_str(),rpathc);
+    rpath1 = rpathc;
+#ifdef VERBOSE
+    std::cerr<<"TD: looking at '"<<rpath1<<"'"<<std::endl;
+#endif
+    if (rpath != rpath1)
+    {
+      int rc = stat(rpath1.c_str(),&buf);
+      if (rc != 0)
+      {
+	std::cerr << this->arg << ": Cannot stat '"<<rpath1<<"'"<<" this cannot happen!" << std::endl;
+	free(rpathc);
+	return 1;
+      }
+      if (buf.st_ino == inodenr)
+      {
+#ifdef VERBOSE
+	std::cerr << "time: " << time(0) << " " << buf.st_mtime << " " << timer << std::endl;
+#endif
+	if (time(0) > buf.st_mtime + timer)
+	{
+	  int rc = unlink(rpath1.c_str());
+	  if (rc == 0)
+	  {
+#ifdef VERBOSE
+	    std::cerr<<"Unlinked '"<<rpath1<<"'"<<std::endl;
+#endif
+	  }
+	  else
+	  {
+#ifdef VERBOSE
+	    std::cerr<<"Could not unlink '"<<rpath1<<"'"<<std::endl;
+#endif
+	  }
+	}
+	else
+	{
+#ifdef VERBOSE
+	  std::cerr << "TD: not unlinking because time: '"<<rpath1<<"'"<<std::endl;
+#endif
+	}
+      }
+    }
+    else
+    {
+#ifdef VERBOSE
+      std::cerr<<"TD: not unlinking '"<<rpath1<<"'"<<std::endl;
+#endif
+    }
+  }
+  free(rpathc);
+  // look if lock can be removed, if yes: and make it so 
+  rc = stat(path_lock.c_str(),&buf);
+  if (rc == 0)
+  {
+    if (time(0) > buf.st_mtime + timer)
+    {
+#ifdef VERBOSE
+      std::cerr << "Trying to remove '"<<path_lock<<"'"<<std::endl;
+#endif
+      rc = rmdir(path_lock.c_str());
+      // It could happen that rmdir failed, for example another process
+      // already deleted it, or path is invalid etc.
+      // We ignore this, relying on the fact that eventually
+      // getlock will time out.
+    }
+  }
+  return 0;
+}
+
+
+//
+// remove lock and links
+// method: do a glob on baselinkfilename and unlink
+// the files.
+//
+// Only remove if links are older than 60 seconds.
+//
+void disparm::remove_lock_and_links()
+{
+  if (this->debug)
+    this->debugger("Before remove_lock_and_links:");
+
+  remove_lock_and_links(this->path_pool,this->path_lockdir,this->path_lock,60);
+  if (this->debug)
+    this->debugger("Before remove_lock_and_links:");
+}
+
+void disparm::setdebug()
+{
+  this->debug = 1;
+}
+void disparm::debugger(const std::string s)
+{
+  std::cerr << s << std::endl;
+  std::cerr << "baselinkfilename: '" << this->baselinkfilename << "'" << std::endl;
+  std::cerr << "committed:        '" << this->committed << "'" << std::endl;
+  std::cerr << "committedname:    '" << this->committedname << "'" << std::endl;
+  std::cerr << "definputfilename: '" << this->definputfilename << "'" << std::endl;
+  std::cerr << "defpath_pool:     '" << this->defpath_pool << "'" << std::endl;
+  std::cerr << "defpath_lockdir:  '" << this->defpath_lockdir << "'" << std::endl;
+  std::cerr << "inputfilename:    '" << this->inputfilename << "'" << std::endl;
+  std::cerr << "key:              '" << this->key << "'" << std::endl;
+  std::cerr << "keyname:          '" << this->keyname << "'" << std::endl;
+  std::cerr << "linename:         '" << this->linename << "'" << std::endl;
+  std::cerr << "linenr:           '" << this->linenr << "'" << std::endl;
+  std::cerr << "lockname:         '" << this->lockname << "'" << std::endl;
+  std::cerr << "maxiter:          '" << this->maxiter << "'" << std::endl;
+  std::cerr << "path_link:        '" << this->path_link << "'" << std::endl;
+  std::cerr << "path_lock:        '" << this->path_lock << "'" << std::endl;
+  std::cerr << "path_lockdir:     '" << this->path_lockdir << "'" << std::endl;
+  std::cerr << "path_pool:        '" << this->path_pool << "'" << std::endl;
+  std::cerr << "pmarker:          '" << this->pmarker << "'" << std::endl;
+  std::cerr << "pmarkerlen:       '" << this->pmarkerlen << "'" << std::endl;
+  std::cerr << "pointerlen:       '" << this->pointerlen << "'" << std::endl;
+  std::cerr << "rc:               '" << this->rc << "'" << std::endl;
+  std::cerr << "rcname:           '" << this->rcname << "'" << std::endl;
+  std::cerr << "shelltype:        '" << this->shelltype << "'" << std::endl;
+  std::cerr << "status:           '" << this->status << "'" << std::endl;
+  std::cerr << "value:            '" << this->value << "'" << std::endl;
+  std::cerr << "valuename:        '" << this->valuename << "'" << std::endl;
+}
+
+void disparm::setarg(int argc, char*argv[])
+{
+  this->arg = argv[0];
+  for (int i=1; i<argc; i++)
+    this->arg += std::string(" ") + std::string(argv[i]);
+  this->arg += " pid:" + to_string(getpid());
+}
+
+//
+// Just to teach me again how to make an object 
+// suitable for the << operator
+//
+std::ostream& operator <<(std::ostream &os,disparm &obj)
+{
+  stats sta;
+  sta = obj.getstats();
+  if ( sta.valid )
+  {
+    os << "Total:     " << sta.records   << "\n" 
+       << "Committed: " << sta.committed << "\n"
+       << "Ready:     " << sta.ready     << "\n"
+       << "Max:       " << sta.commax;
+  }
+  return os;
+}
+
Index: /tags/1.1/disparm.h
===================================================================
--- /tags/1.1/disparm.h	(revision 5)
+++ /tags/1.1/disparm.h	(revision 5)
@@ -0,0 +1,72 @@
+struct stats{bool valid; unsigned int records;unsigned int ready;unsigned int committed;unsigned int commax;};
+const std::string programname = "disparm";
+class disparm{
+  private:
+  static const std::string keyname;
+  static const std::string committedname;
+  static const std::string valuename;
+  static const std::string rcname;
+  static const std::string linename;
+
+  bool debug;
+
+  std::string arg;
+
+  std::streampos key;
+  int            committed;
+  std::string    rc;
+  std::string    value;
+  char           status;
+  unsigned int   linenr;
+
+  std::string path_pool;
+  std::string path_link;
+  std::string baselinkfilename;
+  std::string inputfilename;
+  std::string path_lockdir;
+  std::string path_lock;
+  unsigned int maxiter;
+  const static int pointerlen = 16;
+  std::string lockname;
+  std::string pmarker;
+  int pmarkerlen;
+  int shelltype;
+  void setpath_link(std::string filename);
+  int getlock(void);
+  void removelock();
+  void setpath_lock(std::string p);
+  void setbaselinkfilename(std::string filename);
+  void createlinkfilename(void);
+  void removedanglinglinks(void);
+  int remove_lock_and_links(const std::string path, const std::string dirl, 
+      const std::string path_lock, int timer);
+  void remove_lock_and_links(void);
+  int realnext(void);
+  void debugger(const std::string s);
+  std::string createrc(void);
+  char *buffer; 
+
+  public:
+
+  ~disparm();
+  void setarg(int argc, char*argv[]);
+  void setdebug();
+  static const std::string defpath_pool;
+  std::string defpath_lockdir;
+  static const std::string definputfilename;
+  disparm(void);
+  void setinputfilename(std::string inputfilename);
+  void setmaxiter(int maxiter);
+  std::streampos getkey(void);
+  int create(std::string &s);
+  int next(std::string& s);
+  int ready(void);
+  int REMOVE(void);
+  int getstat(int& records, int& committed, int& ready);
+  int echounhandled(void);
+  void setpath_pool(std::string pool);
+  int setpath_lockdir(std::string lockdir);
+  void setkey(std::streampos key);
+  friend std::ostream& operator <<(std::ostream &os,disparm &obj);
+  stats getstats();
+};
Index: /tags/1.1/disparm.txt
===================================================================
--- /tags/1.1/disparm.txt	(revision 5)
+++ /tags/1.1/disparm.txt	(revision 5)
@@ -0,0 +1,62 @@
+disparm acts as a server for text lines. These lines constitute
+in general parameters for a command or a whole commandline.
+
+Using dispar it is possible to arrange that each of many processes,
+in general running concurrently on many nodes gets it's own parameterlist.
+
+The parameterlists are put in a simple database, residing on a shared
+file system, which must be readable and writable on the nodes by the
+user of dispar.
+
+Example:
+
+- Create the parameter database
+  First, create a text file, each line with a parameter set, for example:
+
+  seq 100 > parmfile
+
+  (this results in a simple parameter file, each line containing one of
+  the numbers 1-100)
+
+  Then, create the database to be used by disparm:
+
+  disparm -c -i parmfile
+
+  This creates a parameter database with the name 'parmfile.disparm'.
+  It is a text file, to be managed by subsequent calls to dispar.
+
+- Get the next, not already used, line:
+
+  eval "`dispar -n`"
+
+  This will create the following environment variables:
+
+  DISPAR_VALUE: the line exactly as present in the original parameter file
+  DISPAR_KEY: to be used at a next 'dispar -r' call, see below
+  DISPAR_STATUS: the status of the line in DISPAR, always 0 in
+                 this version of dispar
+  DISPAR_COMMITTED: the number of times this line has been produced
+                    (minimum 1)
+  DISPAR_RC: this value should be checked first: only if the value is 'OK',
+             other values are meaningful
+
+  A shell loop, using dispar to get the next parameters and start a program
+  using these parameters:
+
+    while true ; do
+      dispar -n
+      if ("$DISPAR_RC != "OK") ; then
+        break
+      fi
+      eval "myprogram $DISPAR_VALUE"
+    done
+
+  If many nodes are running this loop, and if the parameterdatabase is on a
+  filesystem shared by the nodes (a common case in clusters), the program
+  'myprogram' will run many times on those nodes. Every run will use another
+  line of the original parameterfile.
+
+  Using the -m flag, you can tell dispar to produce a line more than once.
+  This is useful if you submit a number of jobs, each containing loops as
+  above, and you expect that some jobs will fail because of a time-limit
+  for example. 
Index: /tags/1.1/disparmcmd.1
===================================================================
--- /tags/1.1/disparmcmd.1	(revision 5)
+++ /tags/1.1/disparmcmd.1	(revision 5)
@@ -0,0 +1,12 @@
+.TH DISPARMCMD 1 
+.SH NAME
+disparmcmd \- manage string pool
+.SH DESCRIPTION
+disparcom is an utility to distribute 
+strings (parameters) in a pool, in an orderly way.
+
+Normally, disparmcmd is called within a shell function
+or an alias, that evals the standard output of disparmcmd.
+
+See man(1) disparm for a complete description.
+
Index: /tags/1.1/disparmmain.cpp
===================================================================
--- /tags/1.1/disparmmain.cpp	(revision 5)
+++ /tags/1.1/disparmmain.cpp	(revision 5)
@@ -0,0 +1,176 @@
+#include <iostream>
+#include <getopt.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include "tofromstring.h"
+#include "disparm.h"
+#include "version.h"
+
+void usage()
+{
+  disparm d=disparm();
+  std::cerr << "Usage:" << std::endl;
+
+  std::cerr << programname << " -h,--help : this message" << std::endl;
+  std::cerr << programname << " -c,--create [-p,--pool POOL] [-i,--input   INPUTFILE]" << std::endl;
+  std::cerr << programname << " -n,--next   [-p,--pool POOL] [-d --lockdir LOCKDIR  ] [-k,--key KEY] [-m,--max MAX]" << std::endl;
+  std::cerr << programname << " -r,--ready  [-p,--pool POOL] [-d --lockdir LOCKDIR  ] [-k,--key KEY]" << std::endl;
+  std::cerr << programname << " -s,--stats  [-p,--pool POOL] [-d --lockdir LOCKDIR  ]" << std::endl;
+  std::cerr << programname << " -u,--unhandled  [-p,--pool POOL] [-d --lockdir LOCKDIR  ]" << std::endl;
+  std::cerr << programname << " -v,--version" << std::endl;
+  std::cerr << "Defaults:" << std::endl;
+  std::cerr << "  POOL     : " << d.defpath_pool << std::endl;
+  std::cerr << "  LOCKDIR  : " << d.defpath_lockdir  << std::endl;
+  std::cerr << "  MAX      : " << "1" << std::endl;
+  std::cerr << "  INPUTFILE: " << d.definputfilename;
+  if (d.definputfilename == "-")
+    std::cerr << " (standard input)";
+  std::cerr << std::endl;
+}
+
+int main(int argc, char*argv[])
+{
+  disparm disparmthing=disparm();
+  bool nextflag       = 0;
+  bool createflag     = 0;
+  bool readyflag      = 0;
+  bool REMOVEFLAG     = 0;
+  bool statsflag      = 0;
+  bool unhandledflag  = 0;
+  bool lockdirset     = 0;
+
+  disparmthing.setarg(argc,argv);
+
+  while(1)
+  {
+    static struct option long_options[] =
+    {
+      {"help",     0, 0, 'h'},
+      {"pool",     1, 0, 'p'},
+      {"create",   0, 0, 'c'},
+      {"input",    1, 0, 'i'},
+      {"next",     0, 0, 'n'},
+      {"max",      1, 0, 'm'},
+      {"ready",    0, 0, 'r'},
+      {"stats",    0, 0, 's'},
+      {"lockdir",  1, 0, 'd'},
+      {"key",      1, 0, 'k'},
+      {"REMOVE",   0, 0, 'R'},
+      {"unhandled",0, 0, 'u'},
+      {"debug",    0, 0, 'D'},
+      {"version",  0, 0, 'v'},
+      {0,          0, 0, 0}
+    };
+    int c = getopt_long(argc,argv,"hp:ci:nm:rsuDvd:k:R",long_options,0);
+    if (c == -1)
+    {
+      if (optind < argc)  // unexpected extra argument
+      {
+	std::cerr << programname << ": Unexpected argument:" << argv[optind] << std::endl;
+	return 1;
+      }
+      break;
+    }
+    if ( c == '?' || c == ':' )
+    {
+      std::cerr << "Invalid parameter, try "<< argv[0]<<" --help " <<std::endl;
+      return 1;
+    }
+    switch(c)
+    {
+      case 'h':
+	usage();
+	break;
+      case 'p':
+	disparmthing.setpath_pool(std::string(optarg));
+	if (!lockdirset)
+	{
+	  std::string d = dirname(optarg);
+	  int rc = disparmthing.setpath_lockdir(d);
+	  if (rc != 0)
+	  {
+	    std::cerr << programname << ": Cannot create directory '"<< d << "'" << std::endl;
+	    return 1;
+	  }
+	}
+	break;
+      case 'c':
+	createflag = 1;
+	break;
+      case 'k':
+	disparmthing.setkey(from_string <long long>(optarg));
+	break;
+      case 'i':
+	 disparmthing.setinputfilename(std::string(optarg));
+	break;
+      case 'n':
+	nextflag = 1;
+	break;
+      case 'm':
+	disparmthing.setmaxiter(atoi(optarg));
+	break;
+      case 'r':
+	readyflag = 1;
+	break;
+      case 'R':
+	REMOVEFLAG = 1;
+	break;
+      case 's':
+	statsflag = 1;
+	break;
+      case 'd':
+	{
+	  int rc = disparmthing.setpath_lockdir(std::string(optarg));
+	  if (rc != 0)
+	  {
+	    std::cerr << programname << ": Cannot create directory '"<<optarg<<"'"<<std::endl;
+	    return 1;
+	  }
+	}
+	lockdirset = 1;
+	break;
+      case 'u':
+	unhandledflag = 1;
+	break;
+      case 'D':
+	disparmthing.setdebug();
+	break;
+      case 'v':
+	std::cerr << programversion << std::endl;
+	break;
+    }
+  }
+
+  int rc=0;
+  if(createflag)
+  {
+    std::string s;
+    rc |= disparmthing.create(s);
+    std::cout << s;
+    if (rc != 0)
+      exit(1);
+  }
+  if(nextflag)
+  {
+    std::string s;
+    rc |= disparmthing.next(s);
+    std::cout << s;
+  }
+  if(readyflag)
+  {
+    rc |= disparmthing.ready();
+  }
+  if(REMOVEFLAG)
+  {
+    rc |= disparmthing.REMOVE();
+  }
+  if(statsflag)
+  {
+    std::cerr << disparmthing << std::endl;
+  }
+  if(unhandledflag)
+  {
+    rc |=disparmthing.echounhandled();
+  }
+  return rc;
+}
Index: /tags/1.1/ex.sh
===================================================================
--- /tags/1.1/ex.sh	(revision 5)
+++ /tags/1.1/ex.sh	(revision 5)
@@ -0,0 +1,3 @@
+z="aa00:n   a  : aaa"
+echo "${z#*:}"
+echo "${z%%:*}"
Index: /tags/1.1/makefunction
===================================================================
--- /tags/1.1/makefunction	(revision 5)
+++ /tags/1.1/makefunction	(revision 5)
@@ -0,0 +1,1 @@
+eval 'disparm() { eval "`'$PWD'/disparmcmd "$@"`"; }'
Index: /tags/1.1/makeit
===================================================================
--- /tags/1.1/makeit	(revision 5)
+++ /tags/1.1/makeit	(revision 5)
@@ -0,0 +1,18 @@
+#!/bin/bash
+umask 022
+case `hostname` in
+  *r7n1*|piccolo) installdir=/sara/sw/disparm-1.1
+    ;;
+  *) installdir=$HOME/myroot/sara/sw/disparm-1.1
+    ;;
+esac
+mkdir -p $installdir/{bin,man/man1}
+chmod 755 $installdir
+chmod 755 $installdir/{bin,man,man/man1}
+make || exit
+echo installing in $installdir :
+cp disparmcmd sara-get-mem-size sara-get-num-cores $installdir/bin || exit
+cp disparm.1 disparmcmd.1 sara-get-mem-size.1 sara-get-num-cores.1 $installdir/man/man1 || exit
+chmod 755 $installdir/bin/* || exit
+chmod 644 $installdir/man/man1/* || exit
+echo done
Index: /tags/1.1/modulefile
===================================================================
--- /tags/1.1/modulefile	(revision 5)
+++ /tags/1.1/modulefile	(revision 5)
@@ -0,0 +1,27 @@
+#%Module
+##
+##        disparm 1.1
+##
+
+proc ModulesHelp { } {
+        puts stderr "Sets up paths you need to use the disparm software"
+}
+
+set root /home/willem/myroot/sara/sw/disparm-1.1
+
+if { [ module-info shelltype ] == "csh" } {
+  set-alias disparm  "eval \"`$root/bin/disparmcmd $*`\""
+} else { 
+         if { [module-info mode] == "load" } {
+
+#         set-alias disparm "disparm_helper() { eval \"`$root/bin/disparmcmd \"$@\"`\"; }; disparm_helper"
+	    puts stdout "disparm() { eval \"`$root/bin/disparmcmd \"$@\"`\"; }; export -f disparm;"
+	 } else {
+	    puts stdout "unset disparm;"
+	 }
+}
+
+prepend-path PATH               $root/bin
+prepend-path MANPATH            $root/man
+
+setenv SARA_DISPARM_ROOT          $root
Index: /tags/1.1/sara-get-mem-size
===================================================================
--- /tags/1.1/sara-get-mem-size	(revision 5)
+++ /tags/1.1/sara-get-mem-size	(revision 5)
@@ -0,0 +1,3 @@
+#!/bin/bash
+cat /proc/meminfo | awk '
+  /MemTotal:/ { print int($2/1024) ; exit }'
Index: /tags/1.1/sara-get-mem-size.1
===================================================================
--- /tags/1.1/sara-get-mem-size.1	(revision 5)
+++ /tags/1.1/sara-get-mem-size.1	(revision 5)
@@ -0,0 +1,17 @@
+.TH SARA-GET-MEM-SIZE 1 
+.SH NAME
+sara-get-mem-size \-  get the amount of memory on the node
+.SH SYNOPSIS
+.B sara-get-mem-size
+.SH DESCRIPTION
+.I Sara-get-mem-size
+Writes the amount of memory in Mbytes to standard output.
+
+.TP 8
+.SH "SEE ALSO"
+sara-get-num-cores(1), topos(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.1/sara-get-num-cores
===================================================================
--- /tags/1.1/sara-get-num-cores	(revision 5)
+++ /tags/1.1/sara-get-num-cores	(revision 5)
@@ -0,0 +1,4 @@
+#!/bin/bash
+cat /proc/cpuinfo | awk '
+  /processor/ { p = $3 }
+  END { print p+1 }'
Index: /tags/1.1/sara-get-num-cores.1
===================================================================
--- /tags/1.1/sara-get-num-cores.1	(revision 5)
+++ /tags/1.1/sara-get-num-cores.1	(revision 5)
@@ -0,0 +1,17 @@
+.TH SARA-GET-NUM-CORES 1 
+.SH NAME
+sara-get-num-cores \-  get the number of cores on the node
+.SH SYNOPSIS
+.B sara-get-num-cores
+.SH DESCRIPTION
+.I Sara-get-num-cores
+Writes the number of cores in the node to standard output.
+
+.TP 8
+.SH "SEE ALSO"
+sara-get-mem-size(1), topos(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.1/shellenv.cpp
===================================================================
--- /tags/1.1/shellenv.cpp	(revision 5)
+++ /tags/1.1/shellenv.cpp	(revision 5)
@@ -0,0 +1,38 @@
+#include <string>
+#include "shellenv.h"
+//
+// Example:
+//   shellenv("aap noot mies", "LEESPLANK", SHELL_SH);
+// returns the string:
+//   LEESPLANK='aap noot mies';export LEESPLANK;
+//
+//  "'" characters in s are escaped
+//
+std::string shellenv(std::string s, const std::string name, int shelltype)
+{
+  std::string r = "'";
+  std::string::iterator it;
+  for ( it=s.begin() ; it < s.end(); it++ )
+  {
+    if ( *it == '\'')
+    {
+      r.push_back('\'');
+      r.push_back('\\');
+      r.push_back('\'');
+      r.push_back('\'');
+    }
+    else
+      r.push_back(*it);
+  }
+  r.push_back('\'');
+  switch (shelltype)
+  {
+    case SHELL_CSH:
+      r = "setenv " + name + " " + r + ";\n";
+      break;
+    case SHELL_SH: 
+      r = name + "=" + r + ";" + "export " + name + ";\n";
+      break;
+  }
+  return r;
+}
Index: /tags/1.1/shellenv.h
===================================================================
--- /tags/1.1/shellenv.h	(revision 5)
+++ /tags/1.1/shellenv.h	(revision 5)
@@ -0,0 +1,3 @@
+const int SHELL_SH = 0;
+const int SHELL_CSH = 1;
+std::string shellenv(const std::string s, const std::string name, int shelltype);
Index: /tags/1.1/test/dir.c
===================================================================
--- /tags/1.1/test/dir.c	(revision 5)
+++ /tags/1.1/test/dir.c	(revision 5)
@@ -0,0 +1,25 @@
+#include <errno.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main()
+{
+ DIR *pdir;
+ struct dirent *pent;
+
+ pdir=opendir("."); //"." refers to the current dir
+ if (!pdir){
+ printf ("opendir() failure; terminating");
+ exit(1);
+ }
+ errno=0;
+ while ((pent=readdir(pdir))){
+  printf("%s\n", pent->d_name);
+ }
+ if (errno){
+ printf ("readdir() failure; terminating");
+ exit(1);
+ }
+ closedir(pdir);
+}
Index: /tags/1.1/test/remove_links_main.cpp
===================================================================
--- /tags/1.1/test/remove_links_main.cpp	(revision 5)
+++ /tags/1.1/test/remove_links_main.cpp	(revision 5)
@@ -0,0 +1,89 @@
+#include <errno.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <iostream>
+
+int remove_links(const std::string path, const std::string dirl, int timer)
+{
+  // removes hard links in directory "dirl" to file "path" 
+  // in directory "dir", if they are older than "time" seconds. 
+  // Method: 
+  // using readdir, examine all components of dirl and see
+  // if they have the same inode as "path". If so, remove, unless
+  // the realname is the same as the realname of "path"
+  //
+  DIR *pdir;
+  struct dirent *pent;
+  int rc;
+  struct stat buf;
+
+  pdir = opendir(dirl.c_str());
+  if (!pdir)
+  {
+    std::cerr << "Cannot read link directory '"<<dirl<<"'"<<std::endl;
+    return 1;
+  }
+  rc = stat(path.c_str(),&buf);
+  if (rc !=0)
+  {
+    std::cerr << "Cannot stat '"<<path<<"'"<<std::endl;
+    return 1;
+  }
+  ino_t inodenr = buf.st_ino;
+  char rpathc[PATH_MAX+1];
+  realpath(path.c_str(),rpathc);
+  std::string rpath = rpathc;
+  std::cerr<<"TD: basefile '"<<rpath<<"'"<<std::endl;
+  std::string rpath1;
+  
+  while ((pent=readdir(pdir)))
+  {
+    struct stat buf;
+    std::string name = pent->d_name;
+    if (name == ".") continue;
+    if (name == "..") continue;
+    realpath((dirl+"/"+name).c_str(),rpathc);
+    rpath1 = rpathc;
+    std::cerr<<"TD: looking at '"<<rpath1<<"'"<<std::endl;
+    if (rpath != rpath1)
+    {
+      int rc = stat(rpath1.c_str(),&buf);
+      if (rc != 0)
+      {
+	std::cerr<<"Cannot stat '"<<rpath1<<"'"<<" this cannot happen!" << std::endl;
+	return 1;
+      }
+      if (buf.st_ino == inodenr)
+      {
+	std::cerr << "time: " << time(0) << " " << buf.st_mtime << " " << timer << std::endl;
+	if (time(0) > buf.st_mtime + timer)
+	{
+	  int rc = unlink(rpath1.c_str());
+	  if (rc == 0)
+	    std::cerr<<"Unlinked '"<<rpath1<<"'"<<std::endl;
+	  else
+	    std::cerr<<"Could not unlink '"<<rpath1<<"'"<<std::endl;
+	}
+	else
+	  std::cerr << "TD: not unlinking because time: '"<<rpath1<<"'"<<std::endl;
+      }
+    }
+    else
+      std::cerr<<"TD: not unlinking '"<<rpath1<<"'"<<std::endl;
+  }
+  return 0;
+}
+int main()
+{
+  std::string poolfilename="pool";
+  std::string linkdirname="linkdir";
+  int timer = 60;
+  remove_links(poolfilename, linkdirname,timer);
+  return 0;
+}
Index: /tags/1.1/test/stattest.cpp
===================================================================
--- /tags/1.1/test/stattest.cpp	(revision 5)
+++ /tags/1.1/test/stattest.cpp	(revision 5)
@@ -0,0 +1,13 @@
+#include <iostream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int main()
+{
+  struct stat buf;
+  int rc = stat("aap",&buf);
+  std::cout << "rc: " << rc << std::endl;
+  std::cout << "mtime: " << buf.st_mtime << std::endl;
+  return 0;
+}
Index: /tags/1.1/tofromstring.h
===================================================================
--- /tags/1.1/tofromstring.h	(revision 5)
+++ /tags/1.1/tofromstring.h	(revision 5)
@@ -0,0 +1,25 @@
+#include <sstream>
+#include <iomanip>
+template <class T>
+inline std::string to_string (const T& t)
+{
+  std::stringstream ss;
+  ss << t;
+  return ss.str();
+}
+template <class T>
+inline std::string to_string (const T& t,int w)
+{
+  std::stringstream ss;
+  ss << std::setfill('0') << std::setw(w) << t;
+  return ss.str();
+}
+template <class T>
+inline T from_string (std::string t)
+{
+  std::stringstream ss;
+  ss.str(t);
+  T x;
+  ss >> x;
+  return x;
+}
Index: /tags/1.1/token.cpp
===================================================================
--- /tags/1.1/token.cpp	(revision 5)
+++ /tags/1.1/token.cpp	(revision 5)
@@ -0,0 +1,236 @@
+#include <fstream>
+#include "token.h"
+#include "tofromstring.h"
+
+//
+// a token is a record in the pool file.
+// Format:
+// nmmmsssssssssssssssssssssssssssssssssssssssssssssssssssssssss
+// ||  |
+// ||  string
+// |number of times this string has been geven out   
+// 0: not ready. 1: ready
+// 
+// constructor:
+token::token()
+{
+  this->setvalue("");    // value (string)
+  this->setlinenr(0);    // line number
+  this->clearstatus();   // status: ready or not
+  this->setcommitted(0); // counter
+  this->pmarker = "pp";  // this marks the start of the pointer record
+  this->pmarkerlen = this->pmarker.length();
+}
+void token::clearstatus()
+{
+  this->status = '0';
+}
+char token::getstatus(void)
+{
+  return this->status;
+}
+void token::ready()
+{
+  this->status = '1';
+}
+bool token::getready(void)
+{
+  return this->status == '1';
+}
+void token::setvalue(std::string v)
+{
+  this->value = v;
+}
+void token::setlinenr(unsigned int l)
+{
+  this->linenr = l;
+}
+unsigned int token::getlinenr(void)
+{
+  return this->linenr;
+}
+std::string token::getvalue(void)
+{
+  return this->value;
+}
+void token::setcommitted(unsigned int n)
+{
+  this->committed = n;
+}
+unsigned int token::getcommitted(void)
+{
+  return this->committed;
+}
+std::string token::gettotal(void)
+{
+  return this->status + to_string(this->committed,comlen) + 
+            to_string(this->linenr,linenrlen)+ this->value;
+}
+//
+// puts everything that we know in repective variables
+//
+int token::puttotal(std::string t)
+{
+  if (t.length() < this->valuestart)
+    return 1;
+  // check that all chars before the value are [0-9]:
+  for (unsigned int i=0; i<valuestart; i++)
+  {
+    if (t[i] < '0' || t[i] > '9')
+      return 1;
+  }
+
+  switch (t[this->statstart])
+  {
+    case '0': break;
+    case '1': break;
+    default: return 1;
+             break;
+  }
+
+  this->committed = from_string<long>(t.substr(comstart,comlen));
+  this->status = t[0];
+  this->linenr = from_string<unsigned int>(t.substr(linenrstart,linenrlen));
+  if (t.length() == valuestart)
+    this->value="";
+  else
+    this->value=t.substr(valuestart);
+  return 0;
+}
+//
+// the file with the tokens
+//
+void token::setfile(std::fstream& file)
+{
+  this->file = &file;
+}
+
+//
+// read token from file, from position pos, possibly
+// wrap around
+// on return: posused is used position
+//            posnext is position of next token
+//
+int token::readnext(std::streampos pos, std::streampos& posused, std::streampos& posnext)
+{
+  //
+  // note: file is member of class token
+  //
+  posused = pos;
+  file->seekg(posused);
+  std::string line;
+  getline(*file,line);
+  if (file->eof() || file->fail() || file->bad())
+    return -1;
+  if (line.substr(0,this->pmarkerlen) == this->pmarker)
+  {
+    posused = 0;
+    file->seekg(posused);
+    getline(*file,line);
+    if (line.substr(0,this->pmarkerlen) == this->pmarker)
+      return -1;
+  }
+  if (file->eof() || file->fail() || file->bad())
+    return -1;
+  posnext = file->tellg();
+  return puttotal(line);
+}
+
+//
+// rewrite token on position p
+// on return, p contains the postion of the next token
+//
+int token::writetoken(std::streampos& p)
+{
+  this->file->seekp(p);
+  *file << this->gettotal() << std::endl;
+  p = this->file->tellp();
+  this->file->flush();
+
+  return 0;
+}
+
+//
+// rewrites the location pointer
+//
+int token::writelocpointer(std::streampos locpointer)
+{
+  //
+  // note: in this function, file is member of class token
+  //
+  std::streampos g = file->tellg();
+  std::streampos p = file->tellp();
+  //
+  // check if last line already contains locpointer
+  //
+  file->seekg(0,std::ios::end);
+  long long filelength = file->tellg();
+  long long l = this->pointerlen +this->pmarkerlen+2;
+  if ( l > filelength)
+  {
+    //
+    // this file has no locpointer
+    //
+    file->seekp(0,std::ios::end);
+  }
+  else
+  {
+    file->seekg(-l,std::ios::end);
+    char c  = file->get();
+    std::string line;
+    getline(*file,line);
+    if (line.length() == this->pointerlen + this->pmarkerlen
+	&& line.substr(0,pmarkerlen) == pmarker
+	&& c == '\n')
+    {
+      //
+      // this is a record with a locpointer value
+      //
+      file->seekp(-(std::streampos)((this->pointerlen)+this->pmarkerlen+1),std::ios::end);
+    }
+    else
+    {
+      file->seekp(0,std::ios::end);
+    }
+  }
+  *file << pmarker << std::setfill('0') << std::setw(this->pointerlen) <<
+    locpointer << std::endl;
+  file->flush();
+  file->seekg(g);
+  file->seekp(p);
+  return 0;
+}
+
+//
+// reads the location pinter
+// returns the value of the location pointer,
+//   -1 if error
+//
+std::streampos token::readlocpointer()
+{
+  std::streampos g = file->tellg();
+
+  long long l = this->pointerlen + this->pmarkerlen +2;
+  file->seekg(0,std::ios::end);
+  char c;
+  if (file->tellg() < l)
+  {
+    file->seekg(0);
+    c = '\n';
+  }
+  else
+  {
+    file->seekg(-l,std::ios::end);
+    c  = file->get();
+  }
+  std::string line;
+  getline(*file,line);
+  if (line.length() == this->pointerlen + this->pmarkerlen
+      && line.substr(0,pmarkerlen) == pmarker
+      && c == '\n')
+    return from_string<long>(line.substr(2));
+  else
+    return -1;
+  file->seekg(g);
+  return 0;
+}
Index: /tags/1.1/token.h
===================================================================
--- /tags/1.1/token.h	(revision 5)
+++ /tags/1.1/token.h	(revision 5)
@@ -0,0 +1,43 @@
+class token{
+  private:
+    std::string value;
+    long id;
+    unsigned int committed;
+    std::fstream *file;
+    char status;
+    unsigned int pmarkerlen;
+    std::string pmarker;
+    //                                        following lenths in char:
+    static const unsigned int pointerlen = 16;    // length of pointer
+    static const unsigned int statstart = 0;
+    static const unsigned int statlen =     1;    // length of status
+    static const unsigned int comstart = statstart + statlen;
+    static const unsigned int comlen =      4;    // length of nr commits
+    static const unsigned int linenrstart = comstart + comlen;
+    static const unsigned int linenrlen =  10;    // length of linenr
+    static const unsigned int valuestart = linenrstart + linenrlen;
+    unsigned int linenr;
+    unsigned int lenlen;
+  public:
+    token(void);
+    void setid(long id);
+    long getid(void);
+    void ready(void);
+    bool getready(void);
+    void clearstatus(void);
+    char getstatus(void);
+    void setlinenr(unsigned int l);
+    unsigned int getlinenr(void);
+    void setvalue(std::string v);
+    std::string getvalue(void);
+    void setcommitted(unsigned int c);
+    unsigned int getcommitted(void);
+    void inccommitted(void);
+    std::string gettotal(void);
+    int puttotal(std::string t);
+    void setfile(std::fstream& file);
+    int readnext(std::streampos pos, std::streampos& posused,std::streampos& posnext);
+    int writetoken(std::streampos& p);
+    int writelocpointer(std::streampos locpointer);
+    std::streampos readlocpointer(void);
+};
Index: /tags/1.1/version.h
===================================================================
--- /tags/1.1/version.h	(revision 5)
+++ /tags/1.1/version.h	(revision 5)
@@ -0,0 +1,4 @@
+#ifndef VERSION_H
+#define VERSION_H
+const std::string programversion = "1.1";
+#endif
Index: /tags/1.2/Makefile
===================================================================
--- /tags/1.2/Makefile	(revision 5)
+++ /tags/1.2/Makefile	(revision 5)
@@ -0,0 +1,24 @@
+CXX = g++
+CXXFLAGS = -g -O3 -Wall
+
+OBJS = disparm.o disparmmain.o token.o shellenv.o clocks.o
+SRCS = $(OBJS:.o=.cpp)
+
+all: disparmcmd
+
+disparmcmd: $(OBJS)
+	$(CXX) -o $@ $(CXXFLAGS) $(OBJS)
+
+%.o: 	%.cpp
+	$(CXX) $(CXXFLAGS) -c -o $@ $<
+
+clean:
+	rm -f disparmcmd $(OBJS) core .depend
+
+include .depend
+
+dep .depend:
+	$(CXX) $(CXXFLAGS) -M $(SRCS) > .depend
+
+test:
+	echo $(SRCS)
Index: /tags/1.2/README
===================================================================
--- /tags/1.2/README	(revision 5)
+++ /tags/1.2/README	(revision 5)
@@ -0,0 +1,23 @@
+See man disparm for an explanation of the external specs of disparm.
+
+I is not easy to arrange in a nfs file system that only one process
+will change a file (the poolfile), and to ensure that this process
+gets the most up-to-date version of the file.
+
+In disparm this is accomplished by three steps:
+
+ - create a lock directory. This is an atomic operation, if
+   many processes are doing a 'mkdir' of the same directory,
+   only one process can succeed.
+ - create a hard link in that directory to the poolfile.
+   If the link count of that link equals 2, it is considered
+   successful, otherwize it is assumed that no exclusive
+   access to the file is possible at this moment.
+ - open de link with O_DIRECT, and read a block, to persuade the 
+   system to get the latest version of the file.
+
+If the lock cannot be obtained directly, it is tried some sconds later.
+
+
+
+
Index: /tags/1.2/clocks.cpp
===================================================================
--- /tags/1.2/clocks.cpp	(revision 5)
+++ /tags/1.2/clocks.cpp	(revision 5)
@@ -0,0 +1,55 @@
+#include <sys/times.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include "clocks.h"
+#include <unistd.h>
+double cputime() /* aix, xlf */
+{ struct tms b;
+  clock_t r;
+  times( &b);
+  r = b.tms_utime + b.tms_stime;
+  return ( (double) r/(double) sysconf(_SC_CLK_TCK));
+}
+double CPUTIME() /* cray  */
+{ 
+  return ( cputime());
+}
+double cputime_() /* g77, gcc */
+{ 
+  return ( cputime());
+}
+
+double wallclock() 
+{ 
+	struct timeval toot;
+	double r;
+
+	gettimeofday(&toot,0);
+	r=toot.tv_sec+0.000001*(double)toot.tv_usec;
+	return(r);
+}
+double WALLCLOCK()
+{
+  return (wallclock());
+}
+double wallclock_()
+{
+  return wallclock();
+}
+		
+void fortransleep(int *i)
+  {
+    sleep(*i);
+  }
+ 
+void FORTRANSLEEP(int *i)
+  {
+    sleep(*i);
+  }
+
+void fortransleep_(int *i)
+  {
+    sleep(*i);
+  }
+
Index: /tags/1.2/clocks.h
===================================================================
--- /tags/1.2/clocks.h	(revision 5)
+++ /tags/1.2/clocks.h	(revision 5)
@@ -0,0 +1,8 @@
+#ifdef __cplusplus
+extern "C"  {
+#endif
+double wallclock(void);
+double cputime(void);
+#ifdef __cplusplus
+}
+#endif
Index: /tags/1.2/disparm.1
===================================================================
--- /tags/1.2/disparm.1	(revision 5)
+++ /tags/1.2/disparm.1	(revision 5)
@@ -0,0 +1,152 @@
+.TH DISPARM 1 
+.SH NAME
+disparm \- alias using disparmcmd to call the program disparmcmd
+.SH SYNOPSIS
+.B disparm
+\fB\ -h,--help  \fP  
+.P 8
+.B disparm
+\fB\ -c,--create  \fP [\fB OPTIONS\fP ] [\fB -i,--input INPUTFILE \fB]
+.P 8
+.B disparm
+\fB\ -n,--next  \fP [\fB OPTIONS\fP ] [\fB -m,--max MAX \fB]
+.P 8
+.B disparm
+\fB\ -r,--ready  \fP [\fB OPTIONS\fP] [\fB -k, --key KEY \fP ]
+.P 8
+.B disparm
+\fB\ -R,--REMOVE  \fP [\fB OPTIONS\fP] [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -s,--stats [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -u,--unhandled [\fB -p, --pool POOL \fP ]
+.P 8
+.B disparm
+\fB\ -v,--version
+.P 8
+.B disparm
+\fB\ -D,--debug [\fB -p, --pool POOL \fP ]
+.SH DESCRIPTION
+disparm is an alias using disparmcmd, an utility to distribute 
+strings (parameters) in a pool, in an orderly way.
+In general, the
+strings are used as command parameters, see Examples.
+.SH COMMANDS
+.TP 4
+.B -h,--help
+Prints usage information.
+.TP 4
+.B -c,--create [ -i,--input INPUTFILE ] [ -p,--pool POOL ]
+Creates new pool and fills it with lines from INPUTFILE, default
+from stdin. INPUTFILENAME '-' stands for stdin.
+See OPTIONS for a description of POOL.
+The environment
+variable 'DISPARM_RC' is set, see ENVIRONMENT.
+.TP 4
+.B -n,--next [ -m,--max MAX ] [ -p,--pool POOL ]
+Gets the next string.
+The environment variables DISPARM_RC, DISPARM_VALUE, DISPARM_COMMITTED and DISPARM_KEY are set,
+see ENVIRONMENT.
+The same string in the pool will be
+produced MAX times, the default is 1. By setting this to a higher
+value, the same string will be produced more times. This can be useful
+for dealing with crashed jobs.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -r,--ready [ -k,--key KEY ] [-p,--pool POOL ]
+Marks the token with key KEY as ready. If not specified on the commandline, the
+value of environment variable DISPARM_KEY is used.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -R,--REMOVE --p,--pool POOL ]
+Removes the pool.
+See OPTIONS for a description of POOL.
+.TP 4
+.B -s,--stats
+Prints to standard error:
+
+.br 
+total number of strings in pool
+.br 
+number of strings delivered
+.br
+number of strings marked as ready
+.br
+maximum one string has been delivered
+.TP 4
+.B -u,--unhandled
+Prints to standard error the strings that are committed, but not marked
+as ready.
+.TP 4
+.B -v,--version
+Prints to standard error the version number of disparm.
+.TP 4
+.B -D,--debug
+Prints to standard error some debug information.
+.SH OPTIONS
+All options accept the following general parameters:
+.TP 4
+.B -p,--pool POOL
+POOL is a unique name of the file that contains the pool. 
+Default: pool.disparm in the current directory.
+.TP 4
+.B -d,--lockdir LOCKDIR
+LOCKDIR is a directory to hold the lockfile (implemented as a hard
+link to the pool file). All lockfiles have different names,
+so it is permitted to use the same directory for all uses of disparm. 
+Default: the directory where the pool resides. 
+
+.SH ENVIRONMENT
+The following environment variables can be set by disparm:
+.P
+.B DISPARM_RC
+if the value is OK, than no errors were found
+.P
+.B DISPARM_KEY
+contains the keyvalue of the token
+.P
+.B DISPARM_LINENO
+contains the line number of the string in the file from which
+the pool file has been created
+.P
+.B DISPARM_COMMITTED
+contains the number of times the string has been committed before
+.P
+.B DISPARM_VALUE
+contains the token
+.SH EXAMPLES
+.TP 4
+
+Create a pool with the firat 10 lines from the man page of sed:
+
+ man sed | head > parmfile
+ disparm -c -p demo1 -i parmfile
+
+Get a line:
+
+ disparm -n -p demo1
+
+The environment variable DISPARM_VALUE contains now one of the
+first ten lines of the man page of sed.
+
+You can use this in a command like this:
+
+ eval "myparser $DISPARM_VALUE"
+
+The token can be marked as ready by:
+
+ disparm -r -p demo1
+
+Finally, the pool can be completely removed by:
+
+ disparm -R -p demo1
+
+.SH "SEE ALSO"
+sara-get-mem-size(1), sara-get-num-cores(1), disparmcmd(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.2/disparm.cpp
===================================================================
--- /tags/1.2/disparm.cpp	(revision 5)
+++ /tags/1.2/disparm.cpp	(revision 5)
@@ -0,0 +1,959 @@
+#include <iostream>
+#include <fstream>
+#include <libgen.h>
+#include <cstdlib>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "disparm.h"
+#include "tofromstring.h"
+#include "token.h"
+#include "shellenv.h"
+#include <errno.h>
+#include <dirent.h>
+
+const std::string disparm::keyname       = "DISPARM_KEY";
+const std::string disparm::committedname = "DISPARM_COMMITTED";
+const std::string disparm::valuename     = "DISPARM_VALUE";
+const std::string disparm::rcname        = "DISPARM_RC";
+const std::string disparm::linename      = "DISPARM_LINENO";
+
+const std::string disparm::defpath_pool   = "pool.disparm";
+const std::string disparm::definputfilename = "-";
+
+//
+//constructor disparm
+//
+disparm::disparm()
+{
+  char defpoolname_c[this->defpath_pool.length()+1];        // default path_pool
+  strcpy(defpoolname_c,this->defpath_pool.c_str());         
+  //
+  // A note about basename and dirname: they need as argument a char*
+  // NOT a const char*. Both functions may modify the argument,
+  // so, in general, do not use the same argument more than
+  // once.
+  //
+  char defpath_lockdir_c[this->defpath_lockdir.length()+1];  // default directory for the lockfile
+  strcpy(defpath_lockdir_c,this->defpath_lockdir.c_str());
+  this->setpath_pool(this->defpath_pool);
+  this->defpath_lockdir = std::string(dirname(defpoolname_c));
+  this->setpath_link("");
+  this->setpath_lock("");     // the name of the lock folder
+  this->setbaselinkfilename("");
+  this->setinputfilename(this->definputfilename);  // default input file name
+  this->setpath_lockdir(this->defpath_lockdir);      // default lock directory
+  this->setmaxiter(1);                             // default maximum times a string can be given out
+  this->pmarker="pp";                              // start of record with pointer to next string in pool
+  this->pmarkerlen = this->pmarker.length();
+  this->lockname="lock.disparm";
+
+  std::string shell = getenv("SHELL");
+  if (shell.find("csh") != std::string::npos) 
+    this->shelltype = SHELL_CSH;
+  else
+    this->shelltype = SHELL_SH;
+
+  //
+  // following items are returned on stdout as in:
+  // export DISPARM_KEY=1234
+  // See also above
+  //
+  this->key       = -1;     // key: pointer to next item in pool
+  this->status    = '0';
+  this->committed = 0;      // the number of times the string is given out
+  this->rc        = "OK";   // return code
+  this->value     = "";     // the string to give out
+  this->linenr    = 0;      // the line number in the pool
+
+  this->debug     = 0;
+
+  int rc = posix_memalign((void**)&buffer, 4096, 512);
+  if (rc != 0)
+    std::cerr << "Disparm: posix_memalign fails" << std::endl;
+}
+
+// destructor
+disparm::~disparm()
+{
+  free(this->buffer);
+}
+
+void disparm::removelock()
+{
+  unlink(this->path_link.c_str());
+  // wwvv not removing lock directory
+  // rmdir(this->path_lock.c_str());
+}
+
+void disparm::setpath_pool(std::string pool)
+{
+  this->path_pool = pool;
+}
+
+void disparm::setpath_link(std::string path_link)
+{
+  this->path_link = path_link;
+}
+
+void disparm::setpath_lock(std::string path_lock)
+{
+  this->path_lock = path_lock;
+}
+
+void disparm::setbaselinkfilename(std::string baselinkfilename)
+{
+  this->baselinkfilename = baselinkfilename;
+}
+
+int disparm::setpath_lockdir(std::string lockdir)
+{
+  this->path_lockdir = lockdir;
+  int rc = mkdir(path_lockdir.c_str(),0777);
+  if (rc ==0)
+    return 0;
+  else
+  {
+    if (errno == EEXIST)
+    {
+      struct stat buf;
+      stat(path_lockdir.c_str(),&buf);
+      if (S_ISDIR(buf.st_mode))
+	return 0;
+    }
+    else
+      return -1;
+  }
+  return 0;  // never reached
+}
+
+//
+// Create a name for the directory to be used as lock: this->path_lock
+// This directory will reside in this->path_lockdir
+// We try to generate a name for a linkfile that is unique.
+// In the name is also the creation time.
+// The link file will be placed in the lock directory
+//
+void disparm::createlinkfilename()
+{
+  if (this->debug)
+    this->debugger("Before createlinkfilename:");
+  char hostname[1025];
+  gethostname(hostname,1024);
+  char poolname_c[this->path_pool.length()+1];
+  strcpy(poolname_c,this->path_pool.c_str());
+  std::string poolnamebase = basename(poolname_c);
+  this->lockname=poolnamebase+".lock.disparm";
+  this->setpath_lock(this->path_lockdir + "/" + this->lockname);
+  std::string base = this->path_lock + "/" + poolnamebase + ".link.";
+  this->setbaselinkfilename(base);
+  this->setpath_link(   base
+                         + to_string(time(0))
+                         + "." 
+                         + hostname 
+                         + "." 
+                         + to_string(getpid()));
+  if (this->debug)
+    this->debugger("After createlinkfilename:");
+}
+
+void disparm::setinputfilename(std::string inputfilename)
+{
+  this->inputfilename = inputfilename;
+}
+
+void disparm::setkey(std::streampos key)
+{
+  this->key = key;
+}
+
+std::streampos disparm::getkey(void)
+{
+  return this->key;
+}
+
+void disparm::setmaxiter(int maxiter)
+{
+  this->maxiter = maxiter;
+}
+
+//
+// Create poolfile from input file
+//
+// On return, s wil contain the output of createrc
+//
+int disparm::create(std::string &s)
+{
+  if (this->debug)
+    this->debugger("Before create:");
+  std::ifstream inp;
+  std::istream *pinp;
+  s = "";
+  //
+  // open inputfile, take care that stdin is also allowed
+  //
+  if (this->inputfilename == "-")
+    pinp = &std::cin;
+  else
+  {
+    inp.open(this->inputfilename.c_str());
+    if (!inp.is_open())
+    {
+      std::cerr<<"Cannot open '"<<inputfilename<<"'"<<std::endl;
+      this->rc = "FAIL";
+      s = this->createrc();
+      return 1;
+    }
+    pinp = &inp;
+  }
+  //
+  // open poolfile
+  //
+  std::string line;
+  std::fstream out;
+  out.open(this->path_pool.c_str(),std::fstream::in | std::fstream::out
+                               |  std::fstream::trunc);
+
+  //
+  // fill poolfile
+  //
+  token tok;
+  tok.setfile(out);
+  std::streampos p;
+  unsigned int linenr = 0;
+  while (getline(*pinp,line))
+  {
+    tok.setlinenr(++linenr);
+    tok.setcommitted(0);
+    tok.clearstatus();
+    tok.setvalue(line);
+    tok.writetoken(p);
+  }
+  tok.writelocpointer(0);
+
+  //
+  // close input file, only if it is not stdin
+  //
+  if (pinp == &inp)
+    inp.close();
+  out.close();
+
+  this->key = 0;
+  s = createrc();
+  if (this->debug)
+    this->debugger("After create:");
+
+  return 0;
+}
+
+//
+// get next string from pool
+//
+int disparm::next(std::string& s)
+{
+  if (this->debug)
+    this->debugger("Before next:");
+  int rc = this->realnext();
+  s = this->createrc();
+  if (this->debug)
+    this->debugger("After next:");
+  return rc;
+}
+
+//
+// create string to be eval'd by the shell
+//
+std::string disparm::createrc(void)
+{
+  std::string s = "";
+  s = s + shellenv(to_string(this->key),      this->keyname,      this->shelltype);
+  s = s + shellenv(to_string(this->committed),this->committedname,this->shelltype);
+  s = s + shellenv(to_string(this->value),    this->valuename,    this->shelltype);
+  s = s + shellenv(to_string(this->rc),       this->rcname,       this->shelltype);
+  s = s + shellenv(to_string(this->linenr),   this->linename,     this->shelltype);
+  return s;
+}
+
+// 
+// get the next string from the pool
+//
+int disparm::realnext()
+{
+  std::fstream file;
+  bool good = 1;
+
+  this->key       = 0;
+  this->status    = '0';
+  this->committed = 0;
+  this->rc        = "OK";
+  this->linenr    = 0;
+  this->value     = "";
+
+  //
+  // get lock, return if that fails
+  //
+  if (this->getlock()!=0)
+  {
+    this->rc = "FAIL";
+    return 1;
+  }
+
+  //
+  // read next string from pool
+  // take care of ready records, maximum iterations
+  // allowed and some kinds of mishaps
+  //
+  // open first using O_DIRECT, to force a fresh cache.
+  int f = open(this->path_link.c_str(),O_DIRECT|O_RDONLY);
+  int bytesread = read(f,buffer,512);
+  if (bytesread <0)
+  {
+    std::cerr << "How strange: cannot read from poolfile." << std::endl;
+  }
+  close(f);
+  file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out);
+  if (file.is_open())
+  {
+    token       tok;
+    std::string line;
+    tok.setfile(file);
+    std::streampos locpointer, posused, posnext;
+
+    //
+    // get pointer to next string
+    //
+    locpointer = tok.readlocpointer();
+    if (locpointer < 0)
+    {
+      std::cerr << "Corrupt parameterfile: cannot find locpointer\n" << std::endl;
+      this->rc = "FAIL";
+      good = 0;
+    }
+    //
+    // read next string from pool
+    //
+    if (good)
+    {
+      int rc = tok.readnext(locpointer,posused,posnext);
+      if (rc != 0)
+      {
+        std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+        std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+        this->rc = "FAIL";
+        good = 0;
+      }
+    }
+    //
+    // keep reading until a valid record is found
+    // take appropriate action if no such record exists
+    //
+    if (good)
+    {
+      std::streampos locpointerold = locpointer;
+      bool allready = 1;  // to learn if all records are
+                          // ready
+      while (1)
+      {
+        if (!tok.getready())
+        {
+	  allready = 0;
+	  if (tok.getcommitted() >= this->maxiter)
+	  {
+	    good = 0;
+	    this->rc = "MAX_EXCEEDED";
+	    break;
+	  }
+          this->key = posused;
+          this->status = tok.getstatus();
+          this->committed = tok.getcommitted();
+          this->value = tok.getvalue();
+	  this->linenr = tok.getlinenr();
+          tok.setcommitted(tok.getcommitted()+1);
+          tok.writetoken(posused);
+          tok.writelocpointer(posused);
+          break;
+        }
+        locpointer = posnext;
+	if (locpointer == locpointerold)
+	{
+	  //
+	  // so we are cycling, nothing to find, it seems
+	  //
+	  good = 0;
+	  if (allready)
+	    this->rc = "ALL_READY";
+	  else
+	    this->rc = "MAX_EXCEEDED";
+	  break;
+	}
+        int rc = tok.readnext(locpointer,posused,posnext);
+        if (rc != 0)
+        {
+	  std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+          std::cerr << "Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+          this->rc = "FAIL";
+          good = 0;
+          break;
+        }
+      }
+    }
+  }
+  else
+  {
+    std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+    std::cerr<< this->arg << ": Strange error: cannot open '"<<path_link<<"'"<<std::endl;
+    return 1;
+  }
+
+  file.close();
+  this->removelock();
+  return !good;
+}
+
+// 
+// get a lock on the poolfile
+//
+// The normal methods don't work on an nfs file system.
+// Luckily, there is a method based on the creation
+// of a directory, which is an atomic operation.
+// If we can create the directory, we are sure that no 
+// other instantiation of this program was also successful.
+// After successful creation of the lock directory, we create
+// a hard link to the poolfile, and use this link to read from and
+// write to. We do not use the poolfile itself to read and write,
+// we can never be sure in a nfs filesystem of the status of a 
+// file. It appears that creating a new hard link suffices to
+// get an up to date version of the poolfile.
+//
+int disparm::getlock(void)
+{
+  struct stat buf;
+  int rc;
+  int sleeptime=2;
+
+  int iter = 0;
+  int ntryremovelock = 0;
+  while(1)
+  {
+    rc  = stat(this->path_pool.c_str(),&buf);
+    std::string er="";
+    switch (errno)
+    {
+      case (EACCES): er="Search permission is denied for one of the  directories  in  the path prefix of path.  (See also path_resolution(7).)";
+		     break;
+
+      case(EBADF):  er="fd is bad.";
+		    break;
+
+      case(EFAULT): er="Bad address.";
+		    break;
+
+      case(ELOOP):  er="Too many symbolic links encountered while traversing the path.";
+		    break;
+
+      case(ENAMETOOLONG):er="File name too long.";
+			 break;
+
+      case(ENOENT): er="A component of path does not exist, or path is an empty string.";
+		    break;
+
+      case(ENOMEM): er="Out of memory (i.e., kernel memory).";
+		    break;
+
+      case(ENOTDIR):
+              er="A component of the path prefix of path is not a directory.";
+	      break;
+
+      case(EOVERFLOW):er="(stat())  path refers to a file whose size cannot be represented in the type off_t.  This can occur when an application  compiled on a 32-bit platform without -D_FILE_OFFSET_BITS=64 calls stat() on a file whose size exceeds (2<<31)-1 bits";
+		      break;
+
+    }
+    if (rc !=0 )
+    {
+      perror("Disparm: Error stat poolfile:");
+      std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+      std::cerr<<er<<std::endl;
+      std::cerr<< this->arg << ": Cannot stat the pool file:'"<<this->path_pool.c_str()<<"'"<< std::endl;
+      return 1;
+    }
+    this->createlinkfilename();
+    //
+    // Try to create the lock directory
+    //
+    rc = mkdir(this->path_lock.c_str(), 0700);
+    // it seems that relying on the atomicity of 
+    // mkdir does not work on the Lisa system.
+    // therefore, we skip the test and rely on the creation of 
+    // links
+    rc = 0;
+    if (rc == 0)
+    {
+      // Creation of lock directory was successful, now create link
+      // to the poolfile
+      // As an extra check: check if the number of hard links to the 
+      // poolfile equals 2.
+      //
+      // Strange: sometimes, when many threads are involved on one node,
+      // the poolfile seems to be gone, get error: "no such file or directory"
+      // I guess this is a race condition in the NFS filesystem
+      // so, I'll try 10 times, and then give up.
+      //
+      int tries = 0;
+      while (1)
+      {
+	tries++;
+	int rc = link(this->path_pool.c_str(),this->path_link.c_str());
+	if (rc < 0)
+	{
+	  if (tries < 10)
+	  {
+	    sleep(1);
+	  }
+	  else
+	  {
+	    perror("Disparm: Error making link:");
+	    std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+	    std::cerr<< this->arg << ": Cannot make link '"<<this->path_link<<"' to '"
+	      <<this->path_pool<<"'"<<std::endl<<" Is poolname '"<<path_pool
+	      <<"' correct?"<<std::endl;
+
+            this->removelock();
+	    return 1;
+	  }
+	}
+	else
+	  break;
+      }
+      //
+      // check if number of hard links equals 2
+      //
+      struct stat linkbuf;
+      rc = stat(this->path_link.c_str(),&linkbuf);
+      if (rc !=0)
+      {
+	std::cerr << this->arg << ": Cannot stat just created link'"<<this->path_link<<"'"<<std::endl;
+	this->removelock();
+	return 1;
+      }
+      if (linkbuf.st_nlink == 2)
+      {
+	break;
+      }
+      else
+      {
+	rc = unlink(this->path_link.c_str());
+      }
+    }
+    //
+    // wait some time before trying again
+    //
+    iter++;
+#ifdef VERBOSE
+    std::cerr << iter <<": waiting for lock " << std::endl;
+#endif
+    sleep(sleeptime);
+    if (iter > 9)
+    {
+#ifdef VERBOSE
+      std::cerr << "Trying to remove locks" << std::endl;
+#endif
+      //
+      // remove stale link files
+      //
+      this->remove_lock_and_links();
+      ntryremovelock ++;
+      if (ntryremovelock > 100)
+      {
+	std::cerr << this->arg << ": Cannot get lock after many tries." << std::endl;
+	this->removelock();
+	return 1;
+      }
+      iter = 0;
+    }
+  }
+  return 0;
+}
+
+//
+// mark string as ready
+//
+int disparm::ready()
+{
+  if (this->getlock()!=0)
+  {
+    return 1;
+  }
+
+  //
+  // if no key given as parameter, read it from environment
+  //
+  if (this->key < 0)
+  {
+    char * s = getenv(this->keyname.c_str());
+    if (s == NULL || *s == 0)
+    {
+      this->removelock();
+      std::cerr << this->arg << ": No key given" << std::endl;
+      return 1;
+    }
+    else
+    {
+      this->key = from_string<long long>(std::string(s));
+    }
+  }
+
+  std::streampos locpointer = this->key;
+  std::fstream file;
+
+  // open first using O_DIRECT, to force a fresh cache.
+  int f = open(this->path_link.c_str(),O_DIRECT|O_RDONLY);
+  int bytesread = read(f,buffer,512);
+  if (bytesread <0)
+  {
+    std::cerr << this->arg << ": cannot read from poolfile." << std::endl;
+    return 1;
+  }
+  close(f);
+  file.open (this->path_link.c_str(), std::fstream::in | std::fstream::out);
+  if (file.is_open())
+  {
+    token tok;
+    std::string line;
+    tok.setfile(file);
+    std::streampos posused,posnext;
+    //
+    // read next record
+    //
+    int rc = tok.readnext(locpointer,posused,posnext);
+    if (rc != 0)
+    {
+      std::cerr << __FILE__ << ":" << __LINE__ << std::endl;
+      std::cerr << this->arg << ": Corrupt or empty parameterfile: cannot read next line\n" << std::endl;
+      file.close();
+      this->removelock();
+      return 1;
+    }
+    //
+    // mark record as ready
+    //
+    tok.ready();
+    //
+    // write record back
+    //
+    tok.writetoken(locpointer);
+  }
+  else
+  {
+    std::cerr << this->arg << ": Cannot read " << this->path_link << std::endl;
+  }
+  file.close();
+  this->removelock();
+  return 0;
+}
+
+//
+// remove poolfile
+//
+int disparm::REMOVE()
+{
+  if (this->getlock()!=0)
+  {
+    return 1;
+  }
+  this->removelock();
+
+  int rc = unlink(this->path_pool.c_str());
+  if (rc)
+  {
+    std::cerr << this->arg << ": Cannot remove " << this->path_pool << std::endl;
+    return 1;
+  }
+  return 0;
+}
+
+//
+// return statistics about the pool
+// no wait for lock, so be careful interpretating the results
+//
+stats disparm::getstats()
+{
+  stats statistics;
+  token tok;
+  std::fstream file;
+  statistics.valid     =  0;
+  statistics.records   = -1;
+  statistics.committed = -1;
+  statistics.ready     = -1;
+  statistics.commax    = -1;
+
+
+  file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out);
+
+  if (file.is_open())
+  {
+    tok.setfile(file);
+    std::streampos pos = 0;
+    std::streampos posused;
+    std::streampos posnext;
+    statistics.valid     = 1;
+    statistics.records   = 0;
+    statistics.committed = 0;
+    statistics.ready     = 0;
+    statistics.commax    = 0;
+    
+    while(1)
+    {
+      tok.readnext(pos, posused,posnext);
+      if (pos != posused)
+        break;
+      if (tok.getready())
+        statistics.ready++;
+      if (tok.getcommitted()>0)
+        statistics.committed++;
+      if (tok.getcommitted() > statistics.commax)
+        statistics.commax = tok.getcommitted();
+      statistics.records++;
+      pos = posnext;
+    }
+    file.close();
+  }
+  else
+  {
+    std::cerr << this->arg << ": Cannot open poolfile '"<<this->path_pool<<"'"<<std::endl;
+  }
+
+  return statistics;
+}
+
+int disparm::echounhandled(void)
+{
+  token tok;
+  std::fstream file;
+
+  file.open (this->path_pool.c_str(), std::fstream::in | std::fstream::out);
+
+  if (file.is_open())
+  {
+    tok.setfile(file);
+    std::streampos pos = 0;
+    std::streampos posused;
+    std::streampos posnext;
+    std::cerr << "linenr:committed:value"<< std::endl;
+    
+    while(1)
+    {
+      tok.readnext(pos, posused,posnext);
+      if (pos != posused)
+        break;
+
+      if (!tok.getready()&&tok.getcommitted()>0)
+      {
+	std::cerr << tok.getlinenr() << ":"<<tok.getcommitted()<<":" << tok.getvalue() << std::endl;
+      }
+      pos = posnext;
+    }
+  }
+  else
+  {
+    std::cerr << this->arg << ": Cannot open pool file '"<<this->path_pool<<"'"<<std::endl;
+    return 1;
+  }
+
+  file.close();
+  return 0;
+}
+
+
+int disparm::remove_lock_and_links(const std::string path, const std::string dirl, const std::string path_lock, int timer)
+{
+  // removes lock and hard links in directory "dirl" to file "path" 
+  // in directory "dir", if they are older than "time" seconds. 
+  // Method: 
+  // if lock is older than timer seconds, remove it.
+  // and:
+  // using readdir, examine all components of dirl and see
+  // if they have the same inode as "path". If so, remove, unless
+  // the realname is the same as the realname of "path"
+  //
+  DIR *pdir;
+  struct dirent *pent;
+  int rc;
+  struct stat buf;
+
+  pdir = opendir(dirl.c_str());
+  if (!pdir)
+  {
+    std::cerr << this->arg << ": Cannot read link directory '"<<dirl<<"'"<<std::endl;
+    return 1;
+  }
+  rc = stat(path.c_str(),&buf);
+  if (rc !=0)
+  {
+    std::cerr << this->arg << ": Cannot stat '"<<path<<"'"<<std::endl;
+    return 1;
+  }
+  ino_t inodenr = buf.st_ino;
+  char* rpathc = (char*)malloc(PATH_MAX+1);
+  rpathc = realpath(path.c_str(),rpathc);
+  std::string rpath = rpathc;
+  std::string rpath1;
+  
+  while ((pent=readdir(pdir)))
+  {
+    struct stat buf;
+    std::string name = pent->d_name;
+    if (name == ".") continue;
+    if (name == "..") continue;
+    rpathc = realpath((dirl+"/"+name).c_str(),rpathc);
+    rpath1 = rpathc;
+#ifdef VERBOSE
+    std::cerr<<"TD: looking at '"<<rpath1<<"'"<<std::endl;
+#endif
+    if (rpath != rpath1)
+    {
+      int rc = stat(rpath1.c_str(),&buf);
+      if (rc != 0)
+      {
+	std::cerr << this->arg << ": Cannot stat '"<<rpath1<<"'"<<" this cannot happen!" << std::endl;
+	free(rpathc);
+	return 1;
+      }
+      if (buf.st_ino == inodenr)
+      {
+#ifdef VERBOSE
+	std::cerr << "time: " << time(0) << " " << buf.st_mtime << " " << timer << std::endl;
+#endif
+	if (time(0) > buf.st_mtime + timer)
+	{
+	  int rc = unlink(rpath1.c_str());
+	  if (rc == 0)
+	  {
+#ifdef VERBOSE
+	    std::cerr<<"Unlinked '"<<rpath1<<"'"<<std::endl;
+#endif
+	  }
+	  else
+	  {
+#ifdef VERBOSE
+	    std::cerr<<"Could not unlink '"<<rpath1<<"'"<<std::endl;
+#endif
+	  }
+	}
+	else
+	{
+#ifdef VERBOSE
+	  std::cerr << "TD: not unlinking because time: '"<<rpath1<<"'"<<std::endl;
+#endif
+	}
+      }
+    }
+    else
+    {
+#ifdef VERBOSE
+      std::cerr<<"TD: not unlinking '"<<rpath1<<"'"<<std::endl;
+#endif
+    }
+  }
+  free(rpathc);
+  // look if lock can be removed, if yes: and make it so 
+  rc = stat(path_lock.c_str(),&buf);
+  if (rc == 0)
+  {
+    if (time(0) > buf.st_mtime + timer)
+    {
+#ifdef VERBOSE
+      std::cerr << "Trying to remove '"<<path_lock<<"'"<<std::endl;
+#endif
+      // wwvv not removing lock directory
+      // rc = rmdir(path_lock.c_str());
+      // It could happen that rmdir failed, for example another process
+      // already deleted it, or path is invalid etc.
+      // We ignore this, relying on the fact that eventually
+      // getlock will time out.
+    }
+  }
+  return 0;
+}
+
+
+//
+// remove lock and links
+// method: do a glob on baselinkfilename and unlink
+// the files.
+//
+// Only remove if links are older than 60 seconds.
+//
+void disparm::remove_lock_and_links()
+{
+  if (this->debug)
+    this->debugger("Before remove_lock_and_links:");
+
+  remove_lock_and_links(this->path_pool,this->path_lockdir,this->path_lock,60);
+  if (this->debug)
+    this->debugger("Before remove_lock_and_links:");
+}
+
+void disparm::setdebug()
+{
+  this->debug = 1;
+}
+void disparm::debugger(const std::string s)
+{
+  std::cerr << s << std::endl;
+  std::cerr << "baselinkfilename: '" << this->baselinkfilename << "'" << std::endl;
+  std::cerr << "committed:        '" << this->committed << "'" << std::endl;
+  std::cerr << "committedname:    '" << this->committedname << "'" << std::endl;
+  std::cerr << "definputfilename: '" << this->definputfilename << "'" << std::endl;
+  std::cerr << "defpath_pool:     '" << this->defpath_pool << "'" << std::endl;
+  std::cerr << "defpath_lockdir:  '" << this->defpath_lockdir << "'" << std::endl;
+  std::cerr << "inputfilename:    '" << this->inputfilename << "'" << std::endl;
+  std::cerr << "key:              '" << this->key << "'" << std::endl;
+  std::cerr << "keyname:          '" << this->keyname << "'" << std::endl;
+  std::cerr << "linename:         '" << this->linename << "'" << std::endl;
+  std::cerr << "linenr:           '" << this->linenr << "'" << std::endl;
+  std::cerr << "lockname:         '" << this->lockname << "'" << std::endl;
+  std::cerr << "maxiter:          '" << this->maxiter << "'" << std::endl;
+  std::cerr << "path_link:        '" << this->path_link << "'" << std::endl;
+  std::cerr << "path_lock:        '" << this->path_lock << "'" << std::endl;
+  std::cerr << "path_lockdir:     '" << this->path_lockdir << "'" << std::endl;
+  std::cerr << "path_pool:        '" << this->path_pool << "'" << std::endl;
+  std::cerr << "pmarker:          '" << this->pmarker << "'" << std::endl;
+  std::cerr << "pmarkerlen:       '" << this->pmarkerlen << "'" << std::endl;
+  std::cerr << "pointerlen:       '" << this->pointerlen << "'" << std::endl;
+  std::cerr << "rc:               '" << this->rc << "'" << std::endl;
+  std::cerr << "rcname:           '" << this->rcname << "'" << std::endl;
+  std::cerr << "shelltype:        '" << this->shelltype << "'" << std::endl;
+  std::cerr << "status:           '" << this->status << "'" << std::endl;
+  std::cerr << "value:            '" << this->value << "'" << std::endl;
+  std::cerr << "valuename:        '" << this->valuename << "'" << std::endl;
+}
+
+void disparm::setarg(int argc, char*argv[])
+{
+  this->arg = argv[0];
+  for (int i=1; i<argc; i++)
+    this->arg += std::string(" ") + std::string(argv[i]);
+  this->arg += " pid:" + to_string(getpid());
+}
+
+//
+// Just to teach me again how to make an object 
+// suitable for the << operator
+//
+std::ostream& operator <<(std::ostream &os,disparm &obj)
+{
+  stats sta;
+  sta = obj.getstats();
+  if ( sta.valid )
+  {
+    os << "Total:     " << sta.records   << "\n" 
+       << "Committed: " << sta.committed << "\n"
+       << "Ready:     " << sta.ready     << "\n"
+       << "Max:       " << sta.commax;
+  }
+  return os;
+}
+
Index: /tags/1.2/disparm.h
===================================================================
--- /tags/1.2/disparm.h	(revision 5)
+++ /tags/1.2/disparm.h	(revision 5)
@@ -0,0 +1,72 @@
+struct stats{bool valid; unsigned int records;unsigned int ready;unsigned int committed;unsigned int commax;};
+const std::string programname = "disparm";
+class disparm{
+  private:
+  static const std::string keyname;
+  static const std::string committedname;
+  static const std::string valuename;
+  static const std::string rcname;
+  static const std::string linename;
+
+  bool debug;
+
+  std::string arg;
+
+  std::streampos key;
+  int            committed;
+  std::string    rc;
+  std::string    value;
+  char           status;
+  unsigned int   linenr;
+
+  std::string path_pool;
+  std::string path_link;
+  std::string baselinkfilename;
+  std::string inputfilename;
+  std::string path_lockdir;
+  std::string path_lock;
+  unsigned int maxiter;
+  const static int pointerlen = 16;
+  std::string lockname;
+  std::string pmarker;
+  int pmarkerlen;
+  int shelltype;
+  void setpath_link(std::string filename);
+  int getlock(void);
+  void removelock();
+  void setpath_lock(std::string p);
+  void setbaselinkfilename(std::string filename);
+  void createlinkfilename(void);
+  void removedanglinglinks(void);
+  int remove_lock_and_links(const std::string path, const std::string dirl, 
+      const std::string path_lock, int timer);
+  void remove_lock_and_links(void);
+  int realnext(void);
+  void debugger(const std::string s);
+  std::string createrc(void);
+  char *buffer; 
+
+  public:
+
+  ~disparm();
+  void setarg(int argc, char*argv[]);
+  void setdebug();
+  static const std::string defpath_pool;
+  std::string defpath_lockdir;
+  static const std::string definputfilename;
+  disparm(void);
+  void setinputfilename(std::string inputfilename);
+  void setmaxiter(int maxiter);
+  std::streampos getkey(void);
+  int create(std::string &s);
+  int next(std::string& s);
+  int ready(void);
+  int REMOVE(void);
+  int getstat(int& records, int& committed, int& ready);
+  int echounhandled(void);
+  void setpath_pool(std::string pool);
+  int setpath_lockdir(std::string lockdir);
+  void setkey(std::streampos key);
+  friend std::ostream& operator <<(std::ostream &os,disparm &obj);
+  stats getstats();
+};
Index: /tags/1.2/disparm.txt
===================================================================
--- /tags/1.2/disparm.txt	(revision 5)
+++ /tags/1.2/disparm.txt	(revision 5)
@@ -0,0 +1,62 @@
+disparm acts as a server for text lines. These lines constitute
+in general parameters for a command or a whole commandline.
+
+Using dispar it is possible to arrange that each of many processes,
+in general running concurrently on many nodes gets it's own parameterlist.
+
+The parameterlists are put in a simple database, residing on a shared
+file system, which must be readable and writable on the nodes by the
+user of dispar.
+
+Example:
+
+- Create the parameter database
+  First, create a text file, each line with a parameter set, for example:
+
+  seq 100 > parmfile
+
+  (this results in a simple parameter file, each line containing one of
+  the numbers 1-100)
+
+  Then, create the database to be used by disparm:
+
+  disparm -c -i parmfile
+
+  This creates a parameter database with the name 'parmfile.disparm'.
+  It is a text file, to be managed by subsequent calls to dispar.
+
+- Get the next, not already used, line:
+
+  eval "`dispar -n`"
+
+  This will create the following environment variables:
+
+  DISPAR_VALUE: the line exactly as present in the original parameter file
+  DISPAR_KEY: to be used at a next 'dispar -r' call, see below
+  DISPAR_STATUS: the status of the line in DISPAR, always 0 in
+                 this version of dispar
+  DISPAR_COMMITTED: the number of times this line has been produced
+                    (minimum 1)
+  DISPAR_RC: this value should be checked first: only if the value is 'OK',
+             other values are meaningful
+
+  A shell loop, using dispar to get the next parameters and start a program
+  using these parameters:
+
+    while true ; do
+      dispar -n
+      if ("$DISPAR_RC != "OK") ; then
+        break
+      fi
+      eval "myprogram $DISPAR_VALUE"
+    done
+
+  If many nodes are running this loop, and if the parameterdatabase is on a
+  filesystem shared by the nodes (a common case in clusters), the program
+  'myprogram' will run many times on those nodes. Every run will use another
+  line of the original parameterfile.
+
+  Using the -m flag, you can tell dispar to produce a line more than once.
+  This is useful if you submit a number of jobs, each containing loops as
+  above, and you expect that some jobs will fail because of a time-limit
+  for example. 
Index: /tags/1.2/disparmcmd.1
===================================================================
--- /tags/1.2/disparmcmd.1	(revision 5)
+++ /tags/1.2/disparmcmd.1	(revision 5)
@@ -0,0 +1,12 @@
+.TH DISPARMCMD 1 
+.SH NAME
+disparmcmd \- manage string pool
+.SH DESCRIPTION
+disparcom is an utility to distribute 
+strings (parameters) in a pool, in an orderly way.
+
+Normally, disparmcmd is called within a shell function
+or an alias, that evals the standard output of disparmcmd.
+
+See man(1) disparm for a complete description.
+
Index: /tags/1.2/disparmmain.cpp
===================================================================
--- /tags/1.2/disparmmain.cpp	(revision 5)
+++ /tags/1.2/disparmmain.cpp	(revision 5)
@@ -0,0 +1,176 @@
+#include <iostream>
+#include <getopt.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include "tofromstring.h"
+#include "disparm.h"
+#include "version.h"
+
+void usage()
+{
+  disparm d=disparm();
+  std::cerr << "Usage:" << std::endl;
+
+  std::cerr << programname << " -h,--help : this message" << std::endl;
+  std::cerr << programname << " -c,--create [-p,--pool POOL] [-i,--input   INPUTFILE]" << std::endl;
+  std::cerr << programname << " -n,--next   [-p,--pool POOL] [-d --lockdir LOCKDIR  ] [-k,--key KEY] [-m,--max MAX]" << std::endl;
+  std::cerr << programname << " -r,--ready  [-p,--pool POOL] [-d --lockdir LOCKDIR  ] [-k,--key KEY]" << std::endl;
+  std::cerr << programname << " -s,--stats  [-p,--pool POOL] [-d --lockdir LOCKDIR  ]" << std::endl;
+  std::cerr << programname << " -u,--unhandled  [-p,--pool POOL] [-d --lockdir LOCKDIR  ]" << std::endl;
+  std::cerr << programname << " -v,--version" << std::endl;
+  std::cerr << "Defaults:" << std::endl;
+  std::cerr << "  POOL     : " << d.defpath_pool << std::endl;
+  std::cerr << "  LOCKDIR  : " << d.defpath_lockdir  << std::endl;
+  std::cerr << "  MAX      : " << "1" << std::endl;
+  std::cerr << "  INPUTFILE: " << d.definputfilename;
+  if (d.definputfilename == "-")
+    std::cerr << " (standard input)";
+  std::cerr << std::endl;
+}
+
+int main(int argc, char*argv[])
+{
+  disparm disparmthing=disparm();
+  bool nextflag       = 0;
+  bool createflag     = 0;
+  bool readyflag      = 0;
+  bool REMOVEFLAG     = 0;
+  bool statsflag      = 0;
+  bool unhandledflag  = 0;
+  bool lockdirset     = 0;
+
+  disparmthing.setarg(argc,argv);
+
+  while(1)
+  {
+    static struct option long_options[] =
+    {
+      {"help",     0, 0, 'h'},
+      {"pool",     1, 0, 'p'},
+      {"create",   0, 0, 'c'},
+      {"input",    1, 0, 'i'},
+      {"next",     0, 0, 'n'},
+      {"max",      1, 0, 'm'},
+      {"ready",    0, 0, 'r'},
+      {"stats",    0, 0, 's'},
+      {"lockdir",  1, 0, 'd'},
+      {"key",      1, 0, 'k'},
+      {"REMOVE",   0, 0, 'R'},
+      {"unhandled",0, 0, 'u'},
+      {"debug",    0, 0, 'D'},
+      {"version",  0, 0, 'v'},
+      {0,          0, 0, 0}
+    };
+    int c = getopt_long(argc,argv,"hp:ci:nm:rsuDvd:k:R",long_options,0);
+    if (c == -1)
+    {
+      if (optind < argc)  // unexpected extra argument
+      {
+	std::cerr << programname << ": Unexpected argument:" << argv[optind] << std::endl;
+	return 1;
+      }
+      break;
+    }
+    if ( c == '?' || c == ':' )
+    {
+      std::cerr << "Invalid parameter, try "<< argv[0]<<" --help " <<std::endl;
+      return 1;
+    }
+    switch(c)
+    {
+      case 'h':
+	usage();
+	break;
+      case 'p':
+	disparmthing.setpath_pool(std::string(optarg));
+	if (!lockdirset)
+	{
+	  std::string d = dirname(optarg);
+	  int rc = disparmthing.setpath_lockdir(d);
+	  if (rc != 0)
+	  {
+	    std::cerr << programname << ": Cannot create directory '"<< d << "'" << std::endl;
+	    return 1;
+	  }
+	}
+	break;
+      case 'c':
+	createflag = 1;
+	break;
+      case 'k':
+	disparmthing.setkey(from_string <long long>(optarg));
+	break;
+      case 'i':
+	 disparmthing.setinputfilename(std::string(optarg));
+	break;
+      case 'n':
+	nextflag = 1;
+	break;
+      case 'm':
+	disparmthing.setmaxiter(atoi(optarg));
+	break;
+      case 'r':
+	readyflag = 1;
+	break;
+      case 'R':
+	REMOVEFLAG = 1;
+	break;
+      case 's':
+	statsflag = 1;
+	break;
+      case 'd':
+	{
+	  int rc = disparmthing.setpath_lockdir(std::string(optarg));
+	  if (rc != 0)
+	  {
+	    std::cerr << programname << ": Cannot create directory '"<<optarg<<"'"<<std::endl;
+	    return 1;
+	  }
+	}
+	lockdirset = 1;
+	break;
+      case 'u':
+	unhandledflag = 1;
+	break;
+      case 'D':
+	disparmthing.setdebug();
+	break;
+      case 'v':
+	std::cerr << programversion << std::endl;
+	break;
+    }
+  }
+
+  int rc=0;
+  if(createflag)
+  {
+    std::string s;
+    rc |= disparmthing.create(s);
+    std::cout << s;
+    if (rc != 0)
+      exit(1);
+  }
+  if(nextflag)
+  {
+    std::string s;
+    rc |= disparmthing.next(s);
+    std::cout << s;
+  }
+  if(readyflag)
+  {
+    rc |= disparmthing.ready();
+  }
+  if(REMOVEFLAG)
+  {
+    rc |= disparmthing.REMOVE();
+  }
+  if(statsflag)
+  {
+    std::cerr << disparmthing << std::endl;
+  }
+  if(unhandledflag)
+  {
+    rc |=disparmthing.echounhandled();
+  }
+  return rc;
+}
Index: /tags/1.2/ex.sh
===================================================================
--- /tags/1.2/ex.sh	(revision 5)
+++ /tags/1.2/ex.sh	(revision 5)
@@ -0,0 +1,3 @@
+z="aa00:n   a  : aaa"
+echo "${z#*:}"
+echo "${z%%:*}"
Index: /tags/1.2/makefunction
===================================================================
--- /tags/1.2/makefunction	(revision 5)
+++ /tags/1.2/makefunction	(revision 5)
@@ -0,0 +1,1 @@
+eval 'disparm() { eval "`'$PWD'/disparmcmd "$@"`"; }'
Index: /tags/1.2/makeit
===================================================================
--- /tags/1.2/makeit	(revision 5)
+++ /tags/1.2/makeit	(revision 5)
@@ -0,0 +1,18 @@
+#!/bin/bash
+umask 022
+case `hostname` in
+  *r7n1*|piccolo) installdir=/sara/sw/disparm-1.1
+    ;;
+  *) installdir=$HOME/myroot/sara/sw/disparm-1.1
+    ;;
+esac
+mkdir -p $installdir/{bin,man/man1}
+chmod 755 $installdir
+chmod 755 $installdir/{bin,man,man/man1}
+make || exit
+echo installing in $installdir :
+cp disparmcmd sara-get-mem-size sara-get-num-cores $installdir/bin || exit
+cp disparm.1 disparmcmd.1 sara-get-mem-size.1 sara-get-num-cores.1 $installdir/man/man1 || exit
+chmod 755 $installdir/bin/* || exit
+chmod 644 $installdir/man/man1/* || exit
+echo done
Index: /tags/1.2/modulefile
===================================================================
--- /tags/1.2/modulefile	(revision 5)
+++ /tags/1.2/modulefile	(revision 5)
@@ -0,0 +1,27 @@
+#%Module
+##
+##        disparm 1.1
+##
+
+proc ModulesHelp { } {
+        puts stderr "Sets up paths you need to use the disparm software"
+}
+
+set root /home/willem/myroot/sara/sw/disparm-1.1
+
+if { [ module-info shelltype ] == "csh" } {
+  set-alias disparm  "eval \"`$root/bin/disparmcmd $*`\""
+} else { 
+         if { [module-info mode] == "load" } {
+
+#         set-alias disparm "disparm_helper() { eval \"`$root/bin/disparmcmd \"$@\"`\"; }; disparm_helper"
+	    puts stdout "disparm() { eval \"`$root/bin/disparmcmd \"$@\"`\"; }; export -f disparm;"
+	 } else {
+	    puts stdout "unset disparm;"
+	 }
+}
+
+prepend-path PATH               $root/bin
+prepend-path MANPATH            $root/man
+
+setenv SARA_DISPARM_ROOT          $root
Index: /tags/1.2/sara-get-mem-size
===================================================================
--- /tags/1.2/sara-get-mem-size	(revision 5)
+++ /tags/1.2/sara-get-mem-size	(revision 5)
@@ -0,0 +1,3 @@
+#!/bin/bash
+cat /proc/meminfo | awk '
+  /MemTotal:/ { print int($2/1000000) ; exit }'
Index: /tags/1.2/sara-get-mem-size.1
===================================================================
--- /tags/1.2/sara-get-mem-size.1	(revision 5)
+++ /tags/1.2/sara-get-mem-size.1	(revision 5)
@@ -0,0 +1,17 @@
+.TH SARA-GET-MEM-SIZE 1 
+.SH NAME
+sara-get-mem-size \-  get the amount of memory on the node
+.SH SYNOPSIS
+.B sara-get-mem-size
+.SH DESCRIPTION
+.I Sara-get-mem-size
+Writes the amount of memory in Mbytes to standard output.
+
+.TP 8
+.SH "SEE ALSO"
+sara-get-num-cores(1), topos(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.2/sara-get-num-cores
===================================================================
--- /tags/1.2/sara-get-num-cores	(revision 5)
+++ /tags/1.2/sara-get-num-cores	(revision 5)
@@ -0,0 +1,4 @@
+#!/bin/bash
+cat /proc/cpuinfo | awk '
+  /processor/ { p = $3 }
+  END { print p+1 }'
Index: /tags/1.2/sara-get-num-cores.1
===================================================================
--- /tags/1.2/sara-get-num-cores.1	(revision 5)
+++ /tags/1.2/sara-get-num-cores.1	(revision 5)
@@ -0,0 +1,17 @@
+.TH SARA-GET-NUM-CORES 1 
+.SH NAME
+sara-get-num-cores \-  get the number of cores on the node
+.SH SYNOPSIS
+.B sara-get-num-cores
+.SH DESCRIPTION
+.I Sara-get-num-cores
+Writes the number of cores in the node to standard output.
+
+.TP 8
+.SH "SEE ALSO"
+sara-get-mem-size(1), topos(1)
+.SH AUTHORS
+Willem Vermin
+.br
+.SH BUGS
+No bugs known yet.
Index: /tags/1.2/shellenv.cpp
===================================================================
--- /tags/1.2/shellenv.cpp	(revision 5)
+++ /tags/1.2/shellenv.cpp	(revision 5)
@@ -0,0 +1,38 @@
+#include <string>
+#include "shellenv.h"
+//
+// Example:
+//   shellenv("aap noot mies", "LEESPLANK", SHELL_SH);
+// returns the string:
+//   LEESPLANK='aap noot mies';export LEESPLANK;
+//
+//  "'" characters in s are escaped
+//
+std::string shellenv(std::string s, const std::string name, int shelltype)
+{
+  std::string r = "'";
+  std::string::iterator it;
+  for ( it=s.begin() ; it < s.end(); it++ )
+  {
+    if ( *it == '\'')
+    {
+      r.push_back('\'');
+      r.push_back('\\');
+      r.push_back('\'');
+      r.push_back('\'');
+    }
+    else
+      r.push_back(*it);
+  }
+  r.push_back('\'');
+  switch (shelltype)
+  {
+    case SHELL_CSH:
+      r = "setenv " + name + " " + r + ";\n";
+      break;
+    case SHELL_SH: 
+      r = name + "=" + r + ";" + "export " + name + ";\n";
+      break;
+  }
+  return r;
+}
Index: /tags/1.2/shellenv.h
===================================================================
--- /tags/1.2/shellenv.h	(revision 5)
+++ /tags/1.2/shellenv.h	(revision 5)
@@ -0,0 +1,3 @@
+const int SHELL_SH = 0;
+const int SHELL_CSH = 1;
+std::string shellenv(const std::string s, const std::string name, int shelltype);
Index: /tags/1.2/test/dir.c
===================================================================
--- /tags/1.2/test/dir.c	(revision 5)
+++ /tags/1.2/test/dir.c	(revision 5)
@@ -0,0 +1,25 @@
+#include <errno.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main()
+{
+ DIR *pdir;
+ struct dirent *pent;
+
+ pdir=opendir("."); //"." refers to the current dir
+ if (!pdir){
+ printf ("opendir() failure; terminating");
+ exit(1);
+ }
+ errno=0;
+ while ((pent=readdir(pdir))){
+  printf("%s\n", pent->d_name);
+ }
+ if (errno){
+ printf ("readdir() failure; terminating");
+ exit(1);
+ }
+ closedir(pdir);
+}
Index: /tags/1.2/test/remove_links_main.cpp
===================================================================
--- /tags/1.2/test/remove_links_main.cpp	(revision 5)
+++ /tags/1.2/test/remove_links_main.cpp	(revision 5)
@@ -0,0 +1,89 @@
+#include <errno.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <iostream>
+
+int remove_links(const std::string path, const std::string dirl, int timer)
+{
+  // removes hard links in directory "dirl" to file "path" 
+  // in directory "dir", if they are older than "time" seconds. 
+  // Method: 
+  // using readdir, examine all components of dirl and see
+  // if they have the same inode as "path". If so, remove, unless
+  // the realname is the same as the realname of "path"
+  //
+  DIR *pdir;
+  struct dirent *pent;
+  int rc;
+  struct stat buf;
+
+  pdir = opendir(dirl.c_str());
+  if (!pdir)
+  {
+    std::cerr << "Cannot read link directory '"<<dirl<<"'"<<std::endl;
+    return 1;
+  }
+  rc = stat(path.c_str(),&buf);
+  if (rc !=0)
+  {
+    std::cerr << "Cannot stat '"<<path<<"'"<<std::endl;
+    return 1;
+  }
+  ino_t inodenr = buf.st_ino;
+  char rpathc[PATH_MAX+1];
+  realpath(path.c_str(),rpathc);
+  std::string rpath = rpathc;
+  std::cerr<<"TD: basefile '"<<rpath<<"'"<<std::endl;
+  std::string rpath1;
+  
+  while ((pent=readdir(pdir)))
+  {
+    struct stat buf;
+    std::string name = pent->d_name;
+    if (name == ".") continue;
+    if (name == "..") continue;
+    realpath((dirl+"/"+name).c_str(),rpathc);
+    rpath1 = rpathc;
+    std::cerr<<"TD: looking at '"<<rpath1<<"'"<<std::endl;
+    if (rpath != rpath1)
+    {
+      int rc = stat(rpath1.c_str(),&buf);
+      if (rc != 0)
+      {
+	std::cerr<<"Cannot stat '"<<rpath1<<"'"<<" this cannot happen!" << std::endl;
+	return 1;
+      }
+      if (buf.st_ino == inodenr)
+      {
+	std::cerr << "time: " << time(0) << " " << buf.st_mtime << " " << timer << std::endl;
+	if (time(0) > buf.st_mtime + timer)
+	{
+	  int rc = unlink(rpath1.c_str());
+	  if (rc == 0)
+	    std::cerr<<"Unlinked '"<<rpath1<<"'"<<std::endl;
+	  else
+	    std::cerr<<"Could not unlink '"<<rpath1<<"'"<<std::endl;
+	}
+	else
+	  std::cerr << "TD: not unlinking because time: '"<<rpath1<<"'"<<std::endl;
+      }
+    }
+    else
+      std::cerr<<"TD: not unlinking '"<<rpath1<<"'"<<std::endl;
+  }
+  return 0;
+}
+int main()
+{
+  std::string poolfilename="pool";
+  std::string linkdirname="linkdir";
+  int timer = 60;
+  remove_links(poolfilename, linkdirname,timer);
+  return 0;
+}
Index: /tags/1.2/test/stattest.cpp
===================================================================
--- /tags/1.2/test/stattest.cpp	(revision 5)
+++ /tags/1.2/test/stattest.cpp	(revision 5)
@@ -0,0 +1,13 @@
+#include <iostream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int main()
+{
+  struct stat buf;
+  int rc = stat("aap",&buf);
+  std::cout << "rc: " << rc << std::endl;
+  std::cout << "mtime: " << buf.st_mtime << std::endl;
+  return 0;
+}
Index: /tags/1.2/tofromstring.h
===================================================================
--- /tags/1.2/tofromstring.h	(revision 5)
+++ /tags/1.2/tofromstring.h	(revision 5)
@@ -0,0 +1,25 @@
+#include <sstream>
+#include <iomanip>
+template <class T>
+inline std::string to_string (const T& t)
+{
+  std::stringstream ss;
+  ss << t;
+  return ss.str();
+}
+template <class T>
+inline std::string to_string (const T& t,int w)
+{
+  std::stringstream ss;
+  ss << std::setfill('0') << std::setw(w) << t;
+  return ss.str();
+}
+template <class T>
+inline T from_string (std::string t)
+{
+  std::stringstream ss;
+  ss.str(t);
+  T x;
+  ss >> x;
+  return x;
+}
Index: /tags/1.2/token.cpp
===================================================================
--- /tags/1.2/token.cpp	(revision 5)
+++ /tags/1.2/token.cpp	(revision 5)
@@ -0,0 +1,236 @@
+#include <fstream>
+#include "token.h"
+#include "tofromstring.h"
+
+//
+// a token is a record in the pool file.
+// Format:
+// nmmmsssssssssssssssssssssssssssssssssssssssssssssssssssssssss
+// ||  |
+// ||  string
+// |number of times this string has been geven out   
+// 0: not ready. 1: ready
+// 
+// constructor:
+token::token()
+{
+  this->setvalue("");    // value (string)
+  this->setlinenr(0);    // line number
+  this->clearstatus();   // status: ready or not
+  this->setcommitted(0); // counter
+  this->pmarker = "pp";  // this marks the start of the pointer record
+  this->pmarkerlen = this->pmarker.length();
+}
+void token::clearstatus()
+{
+  this->status = '0';
+}
+char token::getstatus(void)
+{
+  return this->status;
+}
+void token::ready()
+{
+  this->status = '1';
+}
+bool token::getready(void)
+{
+  return this->status == '1';
+}
+void token::setvalue(std::string v)
+{
+  this->value = v;
+}
+void token::setlinenr(unsigned int l)
+{
+  this->linenr = l;
+}
+unsigned int token::getlinenr(void)
+{
+  return this->linenr;
+}
+std::string token::getvalue(void)
+{
+  return this->value;
+}
+void token::setcommitted(unsigned int n)
+{
+  this->committed = n;
+}
+unsigned int token::getcommitted(void)
+{
+  return this->committed;
+}
+std::string token::gettotal(void)
+{
+  return this->status + to_string(this->committed,comlen) + 
+            to_string(this->linenr,linenrlen)+ this->value;
+}
+//
+// puts everything that we know in repective variables
+//
+int token::puttotal(std::string t)
+{
+  if (t.length() < this->valuestart)
+    return 1;
+  // check that all chars before the value are [0-9]:
+  for (unsigned int i=0; i<valuestart; i++)
+  {
+    if (t[i] < '0' || t[i] > '9')
+      return 1;
+  }
+
+  switch (t[this->statstart])
+  {
+    case '0': break;
+    case '1': break;
+    default: return 1;
+             break;
+  }
+
+  this->committed = from_string<long>(t.substr(comstart,comlen));
+  this->status = t[0];
+  this->linenr = from_string<unsigned int>(t.substr(linenrstart,linenrlen));
+  if (t.length() == valuestart)
+    this->value="";
+  else
+    this->value=t.substr(valuestart);
+  return 0;
+}
+//
+// the file with the tokens
+//
+void token::setfile(std::fstream& file)
+{
+  this->file = &file;
+}
+
+//
+// read token from file, from position pos, possibly
+// wrap around
+// on return: posused is used position
+//            posnext is position of next token
+//
+int token::readnext(std::streampos pos, std::streampos& posused, std::streampos& posnext)
+{
+  //
+  // note: file is member of class token
+  //
+  posused = pos;
+  file->seekg(posused);
+  std::string line;
+  getline(*file,line);
+  if (file->eof() || file->fail() || file->bad())
+    return -1;
+  if (line.substr(0,this->pmarkerlen) == this->pmarker)
+  {
+    posused = 0;
+    file->seekg(posused);
+    getline(*file,line);
+    if (line.substr(0,this->pmarkerlen) == this->pmarker)
+      return -1;
+  }
+  if (file->eof() || file->fail() || file->bad())
+    return -1;
+  posnext = file->tellg();
+  return puttotal(line);
+}
+
+//
+// rewrite token on position p
+// on return, p contains the postion of the next token
+//
+int token::writetoken(std::streampos& p)
+{
+  this->file->seekp(p);
+  *file << this->gettotal() << std::endl;
+  p = this->file->tellp();
+  this->file->flush();
+
+  return 0;
+}
+
+//
+// rewrites the location pointer
+//
+int token::writelocpointer(std::streampos locpointer)
+{
+  //
+  // note: in this function, file is member of class token
+  //
+  std::streampos g = file->tellg();
+  std::streampos p = file->tellp();
+  //
+  // check if last line already contains locpointer
+  //
+  file->seekg(0,std::ios::end);
+  long long filelength = file->tellg();
+  long long l = this->pointerlen +this->pmarkerlen+2;
+  if ( l > filelength)
+  {
+    //
+    // this file has no locpointer
+    //
+    file->seekp(0,std::ios::end);
+  }
+  else
+  {
+    file->seekg(-l,std::ios::end);
+    char c  = file->get();
+    std::string line;
+    getline(*file,line);
+    if (line.length() == this->pointerlen + this->pmarkerlen
+	&& line.substr(0,pmarkerlen) == pmarker
+	&& c == '\n')
+    {
+      //
+      // this is a record with a locpointer value
+      //
+      file->seekp(-(std::streampos)((this->pointerlen)+this->pmarkerlen+1),std::ios::end);
+    }
+    else
+    {
+      file->seekp(0,std::ios::end);
+    }
+  }
+  *file << pmarker << std::setfill('0') << std::setw(this->pointerlen) <<
+    locpointer << std::endl;
+  file->flush();
+  file->seekg(g);
+  file->seekp(p);
+  return 0;
+}
+
+//
+// reads the location pinter
+// returns the value of the location pointer,
+//   -1 if error
+//
+std::streampos token::readlocpointer()
+{
+  std::streampos g = file->tellg();
+
+  long long l = this->pointerlen + this->pmarkerlen +2;
+  file->seekg(0,std::ios::end);
+  char c;
+  if (file->tellg() < l)
+  {
+    file->seekg(0);
+    c = '\n';
+  }
+  else
+  {
+    file->seekg(-l,std::ios::end);
+    c  = file->get();
+  }
+  std::string line;
+  getline(*file,line);
+  if (line.length() == this->pointerlen + this->pmarkerlen
+      && line.substr(0,pmarkerlen) == pmarker
+      && c == '\n')
+    return from_string<long>(line.substr(2));
+  else
+    return -1;
+  file->seekg(g);
+  return 0;
+}
Index: /tags/1.2/token.h
===================================================================
--- /tags/1.2/token.h	(revision 5)
+++ /tags/1.2/token.h	(revision 5)
@@ -0,0 +1,43 @@
+class token{
+  private:
+    std::string value;
+    long id;
+    unsigned int committed;
+    std::fstream *file;
+    char status;
+    unsigned int pmarkerlen;
+    std::string pmarker;
+    //                                        following lenths in char:
+    static const unsigned int pointerlen = 16;    // length of pointer
+    static const unsigned int statstart = 0;
+    static const unsigned int statlen =     1;    // length of status
+    static const unsigned int comstart = statstart + statlen;
+    static const unsigned int comlen =      4;    // length of nr commits
+    static const unsigned int linenrstart = comstart + comlen;
+    static const unsigned int linenrlen =  10;    // length of linenr
+    static const unsigned int valuestart = linenrstart + linenrlen;
+    unsigned int linenr;
+    unsigned int lenlen;
+  public:
+    token(void);
+    void setid(long id);
+    long getid(void);
+    void ready(void);
+    bool getready(void);
+    void clearstatus(void);
+    char getstatus(void);
+    void setlinenr(unsigned int l);
+    unsigned int getlinenr(void);
+    void setvalue(std::string v);
+    std::string getvalue(void);
+    void setcommitted(unsigned int c);
+    unsigned int getcommitted(void);
+    void inccommitted(void);
+    std::string gettotal(void);
+    int puttotal(std::string t);
+    void setfile(std::fstream& file);
+    int readnext(std::streampos pos, std::streampos& posused,std::streampos& posnext);
+    int writetoken(std::streampos& p);
+    int writelocpointer(std::streampos locpointer);
+    std::streampos readlocpointer(void);
+};
Index: /tags/1.2/version.h
===================================================================
--- /tags/1.2/version.h	(revision 5)
+++ /tags/1.2/version.h	(revision 5)
@@ -0,0 +1,4 @@
+#ifndef VERSION_H
+#define VERSION_H
+const std::string programversion = "1.2";
+#endif
