Index: /trunk/Makefile.in
===================================================================
--- /trunk/Makefile.in	(revision 168)
+++ /trunk/Makefile.in	(revision 169)
@@ -1698,3 +1698,3 @@
 sh_portcheck.o: $(srcsrc)/sh_portcheck.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error.h $(srcinc)/sh_mem.h $(srcinc)/sh_calls.h $(srcinc)/sh_utils.h $(srcinc)/sh_modules.h $(srcinc)/sh_static.h $(srcinc)/sh_pthread.h $(srcinc)/CuTest.h 
 sh_pthread.o: $(srcsrc)/sh_pthread.c Makefile config_xor.h $(srcinc)/sh_pthread.h $(srcinc)/sh_calls.h $(srcinc)/sh_modules.h 
-sh_string.o: $(srcsrc)/sh_string.c Makefile $(srcinc)/sh_string.h $(srcinc)/sh_mem.h $(srcinc)/CuTest.h 
+sh_string.o: $(srcsrc)/sh_string.c Makefile config_xor.h $(srcinc)/sh_string.h $(srcinc)/sh_mem.h $(srcinc)/CuTest.h 
Index: /trunk/aclocal.m4
===================================================================
--- /trunk/aclocal.m4	(revision 168)
+++ /trunk/aclocal.m4	(revision 169)
@@ -1914,5 +1914,5 @@
         CPPFLAGS="$ZLIB_OLD_CPPFLAGS"
         AC_MSG_RESULT(failed)
-        AC_MSG_ERROR([zlib not found in ZLIB_HOME, /usr/local, or /usr])
+        AC_MSG_WARN([zlib not found in ZLIB_HOME, /usr/local, or /usr])
 fi
 
Index: /trunk/configure.ac
===================================================================
--- /trunk/configure.ac	(revision 168)
+++ /trunk/configure.ac	(revision 169)
@@ -13,5 +13,5 @@
 dnl start
 dnl
-AM_INIT_AUTOMAKE(samhain, 2.4.3) 
+AM_INIT_AUTOMAKE(samhain, 2.4.4) 
 AC_CANONICAL_HOST
 
@@ -100,4 +100,5 @@
 	
 	*openbsd*)
+	AC_DEFINE([HOST_IS_OPENBSD], 1, [Define if host OS is OPENBSD])
 	selectconfig=freebsd
 	AC_MSG_RESULT(none)
@@ -589,5 +590,12 @@
 $PS ax >/dev/null 2>&1
 if test $? -eq 0; then
+   case "$host_os" in
+   *openbsd*)
+   one=`$PS akx | wc -l`
+   ;;
+   *)
    one=`$PS ax | wc -l`
+   ;;
+   esac
 else
    one=0
@@ -601,7 +609,14 @@
 if test $one -ge $two 
 then
-    PSARG="ax"
+   case "$host_os" in
+   *openbsd*)
+	PSARG="akx"
+	;;
+   *)
+	PSARG="ax"
+	;;
+   esac
 else
-    PSARG="-e"
+	PSARG="-e"
 fi
 AC_DEFINE_UNQUOTED([PSARG], _("$PSARG"), [Argument for ps])
@@ -2146,5 +2161,6 @@
         [
 	if test "x${cross_compiling}" = xyes; then
-		:
+		my_tmp_dir="$withval"
+		AC_DEFINE_UNQUOTED(SH_TMPDIR, _("${my_tmp_dir}") )
 	else
 		if test -d "${withval}"; then
@@ -2152,5 +2168,7 @@
 			AC_DEFINE_UNQUOTED(SH_TMPDIR, _("${my_tmp_dir}") )
 		else
-			AC_MSG_ERROR([--with-tmp-dir: tmp directory ${withval} does not exist])
+			my_tmp_dir="$withval"
+			AC_DEFINE_UNQUOTED(SH_TMPDIR, _("${my_tmp_dir}") )
+			AC_MSG_WARN([--with-tmp-dir: tmp directory ${withval} does not exist])
 		fi
 	fi
Index: /trunk/depend.dep
===================================================================
--- /trunk/depend.dep	(revision 168)
+++ /trunk/depend.dep	(revision 169)
@@ -63,3 +63,3 @@
 sh_pthread.o: $(srcsrc)/sh_pthread.c Makefile config_xor.h $(srcinc)/sh_pthread.h $(srcinc)/sh_calls.h $(srcinc)/sh_modules.h 
 kern_head.o: $(srcsrc)/kern_head.c Makefile config.h $(srcinc)/kern_head.h $(srcinc)/kern_head.h 
-sh_string.o: $(srcsrc)/sh_string.c Makefile $(srcinc)/sh_string.h $(srcinc)/sh_mem.h $(srcinc)/CuTest.h 
+sh_string.o: $(srcsrc)/sh_string.c Makefile config_xor.h $(srcinc)/sh_string.h $(srcinc)/sh_mem.h $(srcinc)/CuTest.h 
Index: /trunk/depend.sum
===================================================================
--- /trunk/depend.sum	(revision 168)
+++ /trunk/depend.sum	(revision 169)
@@ -1,1 +1,1 @@
-2783424603
+3805394239
Index: /trunk/docs/Changelog
===================================================================
--- /trunk/docs/Changelog	(revision 168)
+++ /trunk/docs/Changelog	(revision 169)
@@ -1,14 +1,38 @@
 2.4.4:
-	* fill content if MODI_TXT, base64 encode and add as link_path 
-	  in sh_unix.c; add to report in sh_hash.c; needs listing option
+	* configure.ac: warn, but accept nonexistent tmp dir
+          (Problem reported by Brian)
+	* sh_unix.c: undef P_ALL, P_PID, P_PGID before including 
+          sys/wait.h (compile problem reported by Reputation)
+	* syslog function tested ok with Syslog Fuzzer v0.1 
+          by Jaime Blasco (c) 2008
+	* slib.c: call fflush when writing trace to file
+	* sh_readconf.c: don't set OnlyStderr to false if gpg (problem
+	  reported by Irene Reed)
+	* fix unconditional removal of pid file in atexit handler (bug
+	  reported by Brian)
+	* fix invalid free() in sh_unix_checksum_size()
+	* sh_processcheck.c: workaround for stupid OpenBSD bug (returns
+	  ENODEV instead of EAGAIN, because fgetc does 
+          fcntl(0,F_SETFL,O_NONBLOCK) [ENODEV] internally), problem
+	  reported by Roman R.
+	* fix buf that cause incomplete reporting of modified symlink if
+	  symlink has changed and both old and new paths are >48 bytes
+	* fix bug that prevented mount check from running in one-shot mode
+	* enable mount check for openbsd
+	* fix processcheck default options and test script for openbsd
+	* option --list-file to list content of file (if saved)
+	* sh_tools.c: use strcasecmp in reverse lookup since DNS is case
+	  insensitive (bug reported by Phil)
+	* fill content if MODI_TXT, zlib compress, base64 encode and add 
+	  as link_path in sh_unix.c; add to report in sh_hash.c
 	* testsuite: add test for gpg fingerprint option
 	* sh_extern.c: add 'CloseCommand' for syntactic sugar, 
 	  add in testsuite
 
-2.4.3a:
-	* fix compile error cause by open() with O_CREAT and no third argument
+2.4.3a (12-02-2008):
+	* fix compile error caused by open() with O_CREAT and no third argument
 	  (reported by J.-S. Eon)
 
-2.4.3:
+2.4.3 (31-01-2008):
 	* sh_kern.c: don't require asm/segment.h for kernel check module
 	* use global var with pid of initial thread instead of getpid(),
Index: /trunk/docs/TODO
===================================================================
--- /trunk/docs/TODO	(revision 168)
+++ /trunk/docs/TODO	(revision 169)
@@ -1,6 +1,3 @@
 
 
-check thread_safe sh_entropy
-check thread_safe rng
-
-
+check reported problem with rpmbuild (samhain_setpwd)
Index: /trunk/include/sh_cat.h
===================================================================
--- /trunk/include/sh_cat.h	(revision 168)
+++ /trunk/include/sh_cat.h	(revision 169)
@@ -142,5 +142,8 @@
 
 #ifdef SH_USE_PORTCHECK
- MSG_PORT_REPORT,
+ MSG_PORT_MISS,
+ MSG_PORT_NEW,
+ MSG_PORT_RESTART,
+ MSG_PORT_NEWPORT,
 #endif
 
Index: /trunk/include/sh_extern.h
===================================================================
--- /trunk/include/sh_extern.h	(revision 168)
+++ /trunk/include/sh_extern.h	(revision 169)
@@ -10,6 +10,7 @@
   char   *  envv[32];
   char      checksum[KEY_LEN + 1];
-
+#if 0
   uid_t     trusted_users[32];
+#endif
   uid_t     run_user_uid;
   gid_t     run_user_gid;
Index: /trunk/include/sh_hash.h
===================================================================
--- /trunk/include/sh_hash.h	(revision 168)
+++ /trunk/include/sh_hash.h	(revision 169)
@@ -47,4 +47,8 @@
  */
 int sh_hash_list_db (const char * db_file);
+
+/* List database content for a single file
+ */
+int set_list_file (const char * c);
 
 /* List database content with full detail
Index: /trunk/include/sh_string.h
===================================================================
--- /trunk/include/sh_string.h	(revision 169)
+++ /trunk/include/sh_string.h	(revision 169)
@@ -0,0 +1,47 @@
+#ifndef SH_STRING_H
+#define SH_STRING_H
+
+
+/* String definition and utility functions.
+ */
+typedef struct sh_str_struct
+{
+  char * str; /* always NULL terminated               */
+  size_t len; /* without terminating \0               */
+  size_t siz; /* size of allocated buffer             */
+} sh_string;
+
+sh_string * sh_string_new(size_t size);
+void sh_string_destroy(sh_string ** s);
+#define sh_string_str(a) ((a)->str)
+#define sh_string_len(a) ((a)->len)
+
+/* concat string to sh_string
+ */
+sh_string * sh_string_cat_lchar(sh_string * s, char * str, size_t len);
+
+/* set sh_string from string
+ */
+sh_string * sh_string_set_from_char(sh_string * s, char * str);
+
+/* create new sh_string from array of given length
+ */
+sh_string * sh_string_new_from_lchar(char * str, size_t len);
+
+#define sh_string_copy(a) ((a) ? sh_string_new_from_lchar(((a)->str), ((a)->len)) : NULL)
+
+/* create new sh_string from three arrays of given length
+ */
+sh_string * sh_string_new_from_lchar3(char * str1, size_t len1,
+                                      char * str2, size_t len2,
+                                      char * str3, size_t len3);
+
+/* Truncate to desired length.
+ */
+sh_string * sh_string_truncate(sh_string * s, size_t len);
+
+/* If requested increase is zero, increase by default amount. 
+ */
+sh_string * sh_string_grow(sh_string * s, size_t increase);
+
+#endif
Index: /trunk/include/sh_unix.h
===================================================================
--- /trunk/include/sh_unix.h	(revision 168)
+++ /trunk/include/sh_unix.h	(revision 169)
@@ -91,5 +91,5 @@
 #define MODI_PREL (1 << 13)
 /* get content     */
-#define MODI_TXT (1 << 14)
+#define MODI_TXT ((1 << 14)|MODI_CHK)
 
 #define SH_TXT_MAX 9200
Index: /trunk/include/slib.h
===================================================================
--- /trunk/include/slib.h	(revision 168)
+++ /trunk/include/slib.h	(revision 169)
@@ -213,4 +213,9 @@
 
   /*
+   * robust strcasecmp replacement
+   */
+  int sl_strcasecmp(const char * one, const char * two);
+
+  /*
    * robust strlen replacement
    */
Index: /trunk/man/samhain.8
===================================================================
--- /trunk/man/samhain.8	(revision 168)
+++ /trunk/man/samhain.8	(revision 169)
@@ -262,8 +262,14 @@
 [\-a | \-\-full\-detail]
 List all informations for each file, not only those you would get
-with ls \-l.
+with ls \-l. Must precede the \-d option.
 .TP
 [\-\-delimited]
 List all informations for each file, in a comma-separated format.
+Must precede the \-d option.
+.TP
+.RI [\-\-list\-file= file ]
+List the literal content of the given file as stored in the database.
+Content is not stored by default, must be enabled in the runtime
+configuration file. Must precede the \-d option.
 
 .SS "OPTIONS TO VERIFY AN AUDIT TRAIL"
Index: /trunk/man/samhainrc.5
===================================================================
--- /trunk/man/samhainrc.5	(revision 168)
+++ /trunk/man/samhainrc.5	(revision 169)
@@ -648,5 +648,5 @@
 .BI RedefReadOnly= +/-XXX,+/-YYY,...
 Add or subtract tests XXX from the ReadOnly policy.
-Tests are: CHK (checksum), LNK (link), 
+Tests are: CHK (checksum), TXT (store literal content), LNK (link), 
 HLN (hardlink), INO (inode), USR (user), GRP (group), MTM (mtime),
 ATM (atime), CTM (ctime), SIZ (size), RDEV (device numbers) 
Index: /trunk/src/cutest_slib.c
===================================================================
--- /trunk/src/cutest_slib.c	(revision 168)
+++ /trunk/src/cutest_slib.c	(revision 169)
@@ -24,3 +24,57 @@
 }
 
+void Test_sl_strcasecmp (CuTest *tc) {
+  char one[64], two[64];
+  int  res;
 
+  strcpy(one, "foo");
+  strcpy(two, "foo");
+  res = sl_strcasecmp(one, two);
+  CuAssertIntEquals(tc, 0, res);
+
+  strcpy(one, "fo");
+  strcpy(two, "foo");
+  res = sl_strcasecmp(one, two);
+  CuAssertIntEquals(tc, -1, res);
+
+  strcpy(one, "foo");
+  strcpy(two, "fo");
+  res = sl_strcasecmp(one, two);
+  CuAssertIntEquals(tc, 1, res);
+
+  strcpy(one, "1234");
+  strcpy(two, "2345");
+  res = sl_strcasecmp(one, two);
+  CuAssertIntEquals(tc, -1, res);
+
+  strcpy(one, "234");
+  strcpy(two, "123");
+  res = sl_strcasecmp(one, two);
+  CuAssertIntEquals(tc, 1, res);
+
+  strcpy(one, "");
+  strcpy(two, "123");
+  res = sl_strcasecmp(one, two);
+  CuAssertIntEquals(tc, -1, res);
+
+  strcpy(one, "234");
+  strcpy(two, "");
+  res = sl_strcasecmp(one, two);
+  CuAssertIntEquals(tc, 1, res);
+
+  strcpy(one, "");
+  strcpy(two, "");
+  res = sl_strcasecmp(one, two);
+  CuAssertTrue(tc, res == 0);
+
+#ifndef SL_FAIL_ON_ERROR
+  res = sl_strcasecmp(NULL, two);
+  CuAssertIntEquals(tc, -1, res);
+
+  res = sl_strcasecmp(one, NULL);
+  CuAssertIntEquals(tc, 1, res);
+
+  res = sl_strcasecmp(NULL, NULL);
+  CuAssertTrue(tc, res != 0);
+#endif
+}
Index: /trunk/src/samhain.c
===================================================================
--- /trunk/src/samhain.c	(revision 168)
+++ /trunk/src/samhain.c	(revision 169)
@@ -775,5 +775,6 @@
    */
   (void) sh_unix_rm_lock_file (sh.srvlog.name);
-  (void) sh_unix_rm_pid_file ();
+  if (sh.flag.isdaemon == S_TRUE)
+    (void) sh_unix_rm_pid_file ();
   if (skey != NULL)
     memset (skey, (int) '\0', sizeof(sh_key_t));
Index: /trunk/src/sh_calls.c
===================================================================
--- /trunk/src/sh_calls.c	(revision 168)
+++ /trunk/src/sh_calls.c	(revision 169)
@@ -341,5 +341,5 @@
 #endif
 
-  SL_ENTER(_("retry_fcntl"));
+  SL_ENTER(_("retry_msleep"));
 
   errno  = 0;
Index: /trunk/src/sh_cat.c
===================================================================
--- /trunk/src/sh_cat.c	(revision 168)
+++ /trunk/src/sh_cat.c	(revision 169)
@@ -134,5 +134,8 @@
 
 #ifdef SH_USE_PORTCHECK
-  { MSG_PORT_REPORT, SH_ERR_SEVERE,  EVENT, N_("msg=\"%s\"")},
+  { MSG_PORT_MISS,   SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [ServiceMissing] %s\"")},
+  { MSG_PORT_NEW,    SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [ServiceNew] %s\"")},
+  { MSG_PORT_RESTART,SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [ServiceRestarted] %s\"")},
+  { MSG_PORT_NEWPORT,SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [ServicePortSwitch] %s\"")},
 #endif
 
@@ -449,5 +452,8 @@
 
 #ifdef SH_USE_PORTCHECK
-  { MSG_PORT_REPORT, SH_ERR_SEVERE,  EVENT, N_("msg=<%s>")},
+  { MSG_PORT_MISS,   SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [ServiceMissing] %s>")},
+  { MSG_PORT_NEW,    SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [ServiceNew] %s>")},
+  { MSG_PORT_RESTART,SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [ServiceRestarted] %s>")},
+  { MSG_PORT_NEWPORT,SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [ServicePortSwitch] %s>")},
 #endif
 
Index: /trunk/src/sh_extern.c
===================================================================
--- /trunk/src/sh_extern.c	(revision 168)
+++ /trunk/src/sh_extern.c	(revision 169)
@@ -143,8 +143,11 @@
    * --  check whether path is trustworthy
    */
+  status = sl_trustfile(task->command, NULL, NULL);
+#if 0
   if ((uid_t) -1 != task->trusted_users[0])
     {
       status = sl_trustfile(task->command, task->trusted_users, NULL);
     }
+#endif
 
   PDBG_OPEN;
@@ -647,5 +650,7 @@
       tas->argv[i]          = NULL;
       tas->envv[i]          = NULL;
+#if 0
       tas->trusted_users[i] = (uid_t) -1;
+#endif
     }
 
@@ -826,6 +831,8 @@
 
   (void) sl_get_euid(&ff_euid);
+#if 0
   ext_com->tas.trusted_users[0] = (uid_t) 0;
   ext_com->tas.trusted_users[1] = (uid_t) (ff_euid);
+#endif
 
   /* ------------------------------------------------- */
Index: /trunk/src/sh_getopt.c
===================================================================
--- /trunk/src/sh_getopt.c	(revision 168)
+++ /trunk/src/sh_getopt.c	(revision 169)
@@ -225,4 +225,9 @@
     HAS_ARG_NO, 
     sh_getopt_forever},
+  { N_("list-file"),  
+    '-', 
+    N_("Modify -d to list content of a single file"),  
+    HAS_ARG_YES, 
+    set_list_file},
   { N_("full-detail"),  
     'a', 
@@ -417,5 +422,5 @@
 #if defined(SL_DEBUG)
   if (num > 0) fputc ('\n', stdout);
-  fputs (_(" debug build"), stdout); ++num;
+  fputs (_(" debug build (don't use for production)"), stdout); ++num;
 #endif
 #if defined(SCREW_IT_UP)
@@ -433,4 +438,8 @@
 
 #if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)
+#if defined(HAVE_LIBZ)
+  if (num > 0) fputc ('\n', stdout);
+  fputs (_(" optionally store full text for files"), stdout); ++num;
+#endif
 #if defined(USE_XATTR)
   if (num > 0) fputc ('\n', stdout);
Index: /trunk/src/sh_hash.c
===================================================================
--- /trunk/src/sh_hash.c	(revision 168)
+++ /trunk/src/sh_hash.c	(revision 169)
@@ -2497,5 +2497,6 @@
   sl_strlcat(msg, tmp, SH_BUFSIZE); 
 
-  if (theFile->c_mode[0] == 'l' || theFile->link_path != NULL)
+  if (theFile->c_mode[0] == 'l' || 
+      (theFile->link_path != NULL && theFile->link_path[0] != '-'))
     {
       tmp_lnk     = sh_util_safe_name(theFile->link_path);
@@ -2663,5 +2664,6 @@
   sl_strlcat(msg, tmp, SH_BUFSIZE); 
 
-  if (theFile->c_mode[0] == 'l' || theFile->link_path != NULL)
+  if (theFile->c_mode[0] == 'l' || 
+      (theFile->link_path != NULL && theFile->link_path[0] != '-'))
     {
       tmp_lnk     = sh_util_safe_name(theFile->link_path);
@@ -2739,4 +2741,5 @@
   char timstr2m[32];
   char linkHash[KEY_LEN+1];
+  char * linkComp;
   int  maxcomp;
 
@@ -2869,28 +2872,39 @@
   if (p->theFile.c_mode[0] == 'l') 
     {
-      if (sl_strlen(theFile->link_path) >= MAX_PATH_STORE) 
-	{
-	  sl_strlcpy(linkHash, 
-		     sh_tiger_hash(theFile->link_path, 
-				   TIGER_DATA,
-				   sl_strlen(theFile->link_path),
-				   hashbuf, sizeof(hashbuf)), 
-		     MAX_PATH_STORE+1);
-	  maxcomp = MAX_PATH_STORE;
-	} 
-      else 
-	{
-	  sl_strlcpy(linkHash, theFile->link_path, KEY_LEN + 1);
-	  maxcomp = KEY_LEN;
-	}
-      
-
-      if ( sl_strncmp (linkHash, p->linkpath, maxcomp) != 0 &&
-	   (theFile->check_mask & MODI_LNK) != 0)
-	{
+      if (!(theFile->link_path) &&
+	  (theFile->check_mask & MODI_LNK) != 0)
+	{
+	  linkComp = NULL;
 	  modi_mask |= MODI_LNK;
 	  change_code[1] = 'L';
 	  TPT ((0, FIL__, __LINE__, _("mod=<link>")));
-	} 
+	}
+      else
+	{
+	  if (sl_strlen(theFile->link_path) >= MAX_PATH_STORE) 
+	    {
+	      sl_strlcpy(linkHash, 
+			 sh_tiger_hash(theFile->link_path, 
+				       TIGER_DATA,
+				       sl_strlen(theFile->link_path),
+				       hashbuf, sizeof(hashbuf)), 
+			 MAX_PATH_STORE+1);
+	      linkComp = linkHash;
+	      maxcomp  = KEY_LEN;
+	    } 
+	  else 
+	    {
+	      linkComp = theFile->link_path;
+	      maxcomp  = MAX_PATH_STORE;
+	    }
+	  
+	  if ( sl_strncmp (linkComp, p->linkpath, maxcomp) != 0 &&
+	       (theFile->check_mask & MODI_LNK) != 0)
+	    {
+	      modi_mask |= MODI_LNK;
+	      change_code[1] = 'L';
+	      TPT ((0, FIL__, __LINE__, _("mod=<link>")));
+	    } 
+	}
     }
 
@@ -3349,11 +3363,21 @@
 	    }
 #endif
-	}
-
-
-      if ((modi_mask & MODI_LNK) != 0 && theFile->c_mode[0] == 'l')
-	{
-	  tmp_lnk     = sh_util_safe_name(theFile->link_path);
-	  tmp_lnk_old = sh_util_safe_name(p->linkpath);
+	  /* FIXME is this correct? */
+	  if (theFile->c_mode[0] != 'l' && theFile->link_path &&
+	      strlen(theFile->link_path) > 2)
+	    modi_mask |= MODI_LNK;
+	}
+
+
+      if ((modi_mask & MODI_LNK) != 0 /* && theFile->c_mode[0] == 'l' */)
+	{
+	  if (theFile->link_path)
+	    tmp_lnk     = sh_util_safe_name(theFile->link_path);
+	  else
+	    tmp_lnk     = sh_util_strdup("-");
+	  if (p->linkpath)
+	    tmp_lnk_old = sh_util_safe_name(p->linkpath);
+	  else
+	    tmp_lnk_old = sh_util_strdup("-");
 #ifdef SH_USE_XML
 	  sl_snprintf(tmp, SH_BUFSIZE, _("link_old=\"%s\" link_new=\"%s\" "),
@@ -3599,6 +3623,13 @@
 #endif
 
-static int ListFullDetail = S_FALSE;
+static int ListFullDetail    = S_FALSE;
 static int ListWithDelimiter = S_FALSE;
+static char * ListFile       = NULL;
+
+int set_list_file (const char * c)
+{
+  ListFile = sh_util_strdup(c);
+  return 0;
+}
 
 int set_full_detail (const char * c)
@@ -3858,4 +3889,46 @@
 }
 
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif    
+
+int sh_hash_printcontent(char * linkpath)
+{
+#ifdef HAVE_LIBZ
+  unsigned char * decoded;
+  unsigned char * decompressed = NULL;
+  size_t dlen;
+  unsigned long clen;
+  unsigned long clen_o;
+  int    res;
+
+  if (linkpath && *linkpath != '-')
+    {
+      dlen = sh_util_base64_dec_alloc (&decoded, 
+				       (unsigned char *)linkpath, 
+				       strlen(linkpath));
+      clen = dlen * 2 + 1;
+      do {
+	if (decompressed)
+	  SH_FREE(decompressed);
+	clen += dlen; clen_o = clen;
+	decompressed = SH_ALLOC(clen);
+	res = uncompress(decompressed, &clen, decoded, dlen);
+	if (res == Z_MEM_ERROR)
+	  { fprintf(stderr, _("Error: Not enough memory\n")); return -1; }
+	if (res == Z_DATA_ERROR)
+	  { fprintf(stderr, _("Error: Data corrupt or incomplete\n")); return -1; }
+      } while (res == Z_BUF_ERROR || clen == clen_o);
+      decompressed[clen] = '\0';
+      fputs( (char*) decompressed, stdout);
+      return 0;
+    }
+#else
+  (void) linkpath;
+#endif
+  fprintf(stderr, _("Error: No data available\n")); 
+  return -1;
+}
+
 int sh_hash_list_db (const char * db_file)
 {
@@ -3863,4 +3936,5 @@
   SL_TICKET fd;
   char * line;
+  int  flag = 0;
 
   if (!db_file)
@@ -3900,10 +3974,37 @@
     {
       p = sh_hash_getdataent (fd, line, MAX_PATH_STORE);
-      if ((p != NULL) && (p->fullpath[0] != 'K'))
-	{
-	  if (ListFullDetail == S_FALSE)
-	    sh_hash_list_db_entry (p); 
+      if ((p != NULL) && (p->fullpath[0] == '/'))
+	{
+	  if (!ListFile)
+	    {
+	      flag = 1;
+	      if (ListFullDetail == S_FALSE)
+		sh_hash_list_db_entry (p); 
+	      else
+		sh_hash_list_db_entry_full_detail (p);
+	    }
 	  else
-	    sh_hash_list_db_entry_full_detail (p); 
+	    {
+	      if (0 != sl_strcmp(ListFile, p->fullpath))
+		{
+		  continue;
+		}
+	      flag = 1;
+	      if ('l' != p->theFile.c_mode[0])
+		{
+		  if (sh_hash_printcontent(p->linkpath) < 0)
+		    {
+		      _exit(EXIT_FAILURE);
+		      return -1;
+		    }
+		}
+	      else
+		{
+		  fprintf(stderr, _("File is a link\n"));
+		  _exit(EXIT_FAILURE);
+		  return -1;
+		}
+	      break;
+	    }
 	}
       else if (p == NULL)
@@ -3919,4 +4020,9 @@
   fflush(NULL);
 
+  if (flag == 0)
+    {
+      fprintf(stderr, _("No file found\n"));
+      _exit(EXIT_FAILURE);
+    }
   _exit(EXIT_SUCCESS);
   return 0; 
Index: /trunk/src/sh_mounts.c
===================================================================
--- /trunk/src/sh_mounts.c	(revision 168)
+++ /trunk/src/sh_mounts.c	(revision 169)
@@ -180,6 +180,4 @@
   }
 
-  lastcheck = time(NULL);
-
   SL_RETURN(0, _("sh_mounts_init"));
 }
@@ -197,5 +195,5 @@
     SL_RETURN(-1, _("sh_mounts_timer"));
   }
-  
+ 
   SL_RETURN(0, _("sh_mounts_timer"));
 }
@@ -400,5 +398,5 @@
 
 /* FreeBSD includes */
-#ifdef HOST_IS_FREEBSD
+#if defined(HOST_IS_FREEBSD) || defined(HOST_IS_OPENBSD) 
 #include <sys/param.h>
 #include <sys/ucred.h>
@@ -577,5 +575,5 @@
 #endif
 
-#if defined(HOST_IS_FREEBSD)
+#if defined(HOST_IS_FREEBSD) || defined(HOST_IS_OPENBSD)
 
 /* FreeBSD returns flags instead of strings as mount options, so we'll convert
@@ -595,4 +593,7 @@
 		{"local",	MNT_LOCAL},
 		{"quota",	MNT_QUOTA},
+#ifdef MNT_NOATIME
+		{"noatime",	MNT_NOATIME},
+#endif
 		{"bound",	-1}
 	};
@@ -665,6 +666,6 @@
 	list = m;
 
-/* The FreeBSD way */
-#ifdef HOST_IS_FREEBSD
+/* The Open/FreeBSD way */
+#if defined(HOST_IS_FREEBSD) || defined(HOST_IS_OPENBSD)
 {
 	struct statfs *fsp;
Index: /trunk/src/sh_portcheck.c
===================================================================
--- /trunk/src/sh_portcheck.c	(revision 168)
+++ /trunk/src/sh_portcheck.c	(revision 169)
@@ -384,5 +384,5 @@
 	  if (ptr->flag != SH_PORT_OPT && ptr->flag != SH_PORT_IGN)
 	    {
-	      snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceMissing] port %s:%d/%s (%s)"), 
+	      snprintf (errbuf, sizeof(errbuf), _("port: %s:%d/%s (%s)"), 
 			ptr->interface, ptr->port, SH_PROTO_STR(proto), 
 			ptr->service ? ptr->service : check_services(ptr->port, proto));
@@ -395,5 +395,5 @@
 		  SH_MUTEX_LOCK(mutex_thread_nolog);
 		  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
-				  MSG_PORT_REPORT, errbuf);
+				  MSG_PORT_MISS, errbuf);
 		  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 		}
@@ -497,5 +497,5 @@
       if (!portent)
 	{
-	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"), 
+	  snprintf (errbuf, sizeof(errbuf), _("port: %s:%d/%s (%s)"), 
 		    inet_ntoa(haddr), port, SH_PROTO_STR(proto), service);
 #ifdef TEST_ONLY
@@ -505,5 +505,5 @@
 	  SH_MUTEX_LOCK(mutex_thread_nolog);
 	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
-			  MSG_PORT_REPORT, errbuf);
+			  MSG_PORT_NEW, errbuf);
 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 #endif
@@ -515,6 +515,6 @@
       else if (portent->status == SH_PORT_MISS && portent->flag != SH_PORT_IGN)
 	{
-	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s to %d/%s (%s)"), 
-		    inet_ntoa(haddr), portent->port, SH_PROTO_STR(proto), port, SH_PROTO_STR(proto), service);
+	  snprintf (errbuf, sizeof(errbuf), _("port: %s:%d/%s (%s), was %d/%s"), 
+		    inet_ntoa(haddr), port, SH_PROTO_STR(proto), service, portent->port, SH_PROTO_STR(proto));
 #ifdef TEST_ONLY
 	  fprintf(stderr, _("service: %s\n"), errbuf);
@@ -522,5 +522,5 @@
 	  SH_MUTEX_LOCK(mutex_thread_nolog);
 	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
-			  MSG_PORT_REPORT, errbuf);
+			  MSG_PORT_RESTART, errbuf);
 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 #endif
@@ -530,6 +530,6 @@
       else if (port != portent->port && (-1) != portent->port)
 	{
-	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServicePortSwitch] port %s:%d/%s to %d/%s (%s)"), 
-		    inet_ntoa(haddr), portent->port, SH_PROTO_STR(proto), port, SH_PROTO_STR(proto), service);
+	  snprintf (errbuf, sizeof(errbuf), _("port: %s:%d/%s (%s), was %d/%s"), 
+		    inet_ntoa(haddr), port, SH_PROTO_STR(proto), service, portent->port, SH_PROTO_STR(proto));
 #ifdef TEST_ONLY
 	  fprintf(stderr, _("service: %s\n"), errbuf);
@@ -537,5 +537,5 @@
 	  SH_MUTEX_LOCK(mutex_thread_nolog);
 	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
-			  MSG_PORT_REPORT, errbuf);
+			  MSG_PORT_NEWPORT, errbuf);
 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 #endif
@@ -552,5 +552,5 @@
       if (!portent)
 	{
-	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"), 
+	  snprintf (errbuf, sizeof(errbuf), _("port: %s:%d/%s (%s)"), 
 		    inet_ntoa(haddr), port, SH_PROTO_STR(proto), check_services(port, proto));
 #ifdef TEST_ONLY
@@ -560,5 +560,5 @@
 	  SH_MUTEX_LOCK(mutex_thread_nolog);
 	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
-			  MSG_PORT_REPORT, errbuf);
+			  MSG_PORT_NEW, errbuf);
 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 #endif
@@ -570,5 +570,5 @@
       else if (portent->status == SH_PORT_MISS && portent->flag != SH_PORT_IGN)
 	{
-	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s (%s)"), 
+	  snprintf (errbuf, sizeof(errbuf), _("port: %s:%d/%s (%s)"), 
 		    inet_ntoa(haddr), port, SH_PROTO_STR(proto), check_services(port, proto));
 #ifdef TEST_ONLY
@@ -577,5 +577,5 @@
 	  SH_MUTEX_LOCK(mutex_thread_nolog);
 	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
-			  MSG_PORT_REPORT, errbuf);
+			  MSG_PORT_RESTART, errbuf);
 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 #endif
Index: /trunk/src/sh_prelude.c
===================================================================
--- /trunk/src/sh_prelude.c	(revision 168)
+++ /trunk/src/sh_prelude.c	(revision 169)
@@ -85,5 +85,5 @@
 #define SH_NEED_PWD_GRP 1
 #include "sh_static.h"
-
+char * sh_util_strdup (const char * str) SH_GNUC_MALLOC;
 /*
  * When SH_USE_XML is set, value are formated using name="value".
@@ -651,4 +651,11 @@
 #endif
 
+#ifdef SH_USE_PORTCHECK
+		{ MSG_PORT_MISS, N_("Service closed"), IDMEF_IMPACT_TYPE_OTHER },
+		{ MSG_PORT_NEW, N_("Service opened"), IDMEF_IMPACT_TYPE_OTHER },
+		{ MSG_PORT_RESTART, N_("Service restarted"), IDMEF_IMPACT_TYPE_OTHER },
+		{ MSG_PORT_NEWPORT, N_("Service restarted"), IDMEF_IMPACT_TYPE_OTHER },
+#endif
+
 #ifdef SH_USE_SUIDCHK
                 { MSG_SUID_POLICY, N_("SUID/SGID File Detected"), IDMEF_IMPACT_TYPE_FILE },
@@ -684,4 +691,158 @@
 
 
+#ifdef SH_USE_PORTCHECK
+static int get_service_info(char *msg, idmef_alert_t *alert)
+{
+        int ret;
+	long port;
+	char *ptr, *new, *tmp, *ip, *srv, *end;
+        prelude_string_t *str;
+        idmef_address_t *address;
+        idmef_node_t *node;
+        idmef_service_t *service;
+        idmef_source_t *source = idmef_alert_get_next_source(alert, NULL);
+
+        new = sh_util_strdup(msg);
+ 
+        ptr = strstr(new, _("port: "));
+        if ( ! ptr ) {
+                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
+                                _("malformed Samhain port check message"), _("get_service_info"));
+		SH_FREE( new );
+                return -1;
+        }
+
+        ptr += 6; /* skip 'port: ', position on first byte of interface */
+        tmp = strchr(ptr, ':');
+        if ( ! tmp ) {
+                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
+                                _("malformed Samhain port check message (no port)"), _("get_service_info"));
+		SH_FREE( new );
+                return -1;
+        }
+	*tmp = '\0';
+
+	ip = strdup(ptr);
+        if ( ip ) {
+                if ( ! source ) {
+                        ret = idmef_alert_new_source(alert, &source, IDMEF_LIST_APPEND);
+                        if ( ret < 0 ) {
+                                free(ip);
+				SH_FREE( new );
+                                return ret;
+                        }
+                }
+
+                ret = idmef_source_new_node(source, &node);
+                if ( ret < 0 ) {
+                        free(ip);
+			SH_FREE( new );
+                        return ret;
+                }
+                
+                ret = idmef_node_new_address(node, &address, IDMEF_LIST_APPEND);
+                if ( ret < 0 ) {
+                        free(ip);
+			SH_FREE( new );
+                        return ret;
+                }
+                
+                ret = idmef_address_new_address(address, &str);
+                if ( ret < 0 ) {
+                        free(ip);
+			SH_FREE( new );
+                        return ret;
+                }
+                
+                prelude_string_set_nodup(str, ip);
+        }
+
+	ptr = tmp;
+	++ptr;
+        tmp = strchr(ptr, '/');
+        if ( ! tmp ) {
+                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
+                                _("malformed Samhain port check message (no protocol)"), _("get_service_info"));
+		SH_FREE( new );
+                return -1;
+        }
+	*tmp = '\0';
+
+	port = strtol(ptr, &end, 0);
+        if ( *ptr && *end == '\0' && port >= 0 && port < 65536) {
+
+                if ( ! source ) {
+                        ret = idmef_alert_new_source(alert, &source, IDMEF_LIST_APPEND);
+                        if ( ret < 0 ) {
+                                free(srv);
+				SH_FREE( new );
+                                return ret;
+                        }
+                }
+
+                ret = idmef_source_new_service(source, &service);
+                if ( ret < 0 ) {
+                        free(srv);
+			SH_FREE( new );
+                        return ret;
+                }
+
+		idmef_service_set_port(service, port);
+	}
+
+	ptr = tmp;
+	++ptr;
+        ptr = strchr(ptr, '(');
+        if ( ! ptr ) {
+                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
+                                _("malformed Samhain port check message (no service)"), _("get_service_info"));
+		SH_FREE( new );
+                return -1;
+        }
+	++ptr;
+        tmp = strchr(ptr, ')');
+        if ( ! tmp ) {
+                sh_error_handle((-1), FIL__, __LINE__, -1, MSG_E_SUBGEN,
+                                _("malformed Samhain port check message (service not closed)"), _("get_service_info"));
+		SH_FREE( new );
+                return -1;
+        }
+	*tmp = '\0';
+
+	srv = strdup(ptr);
+        if ( srv ) {
+                if ( ! source ) {
+                        ret = idmef_alert_new_source(alert, &source, IDMEF_LIST_APPEND);
+                        if ( ret < 0 ) {
+                                free(srv);
+				SH_FREE( new );
+                                return ret;
+                        }
+                }
+
+		if ( ! service ) {
+                        ret = idmef_source_new_service(source, &service);
+			if ( ret < 0 ) {
+                                free(srv);
+				SH_FREE( new );
+				return ret;
+			}
+                }
+
+                ret = idmef_service_new_ident(service, &str);
+                if ( ret < 0 ) {
+                        free(srv);
+			SH_FREE( new );
+                        return ret;
+                }
+                
+                prelude_string_set_nodup(str, srv);
+        }
+
+	SH_FREE( new );
+
+	return 0;
+}
+#endif
 
 static int get_login_info(char *msg, idmef_alert_t *alert)
@@ -902,4 +1063,12 @@
                 goto err;
         
+#ifdef SH_USE_PORTCHECK
+	if (msgid == MSG_PORT_MISS || msgid == MSG_PORT_NEW || msgid == MSG_PORT_RESTART || msgid == MSG_PORT_NEWPORT) {
+	        ret = get_service_info(message, alert);
+		if ( ret < 0 )
+		        goto err;
+        }
+#endif
+   
         map_policy_to_class(message, msgid, impact, str);
 
Index: /trunk/src/sh_processcheck.c
===================================================================
--- /trunk/src/sh_processcheck.c	(revision 168)
+++ /trunk/src/sh_processcheck.c	(revision 169)
@@ -837,6 +837,14 @@
 	  else if (errno == EAGAIN)
 	    {
+	      clearerr(in);
 	      continue;
 	    }
+#ifdef HOST_IS_OPENBSD
+	  else if (errno == ENODEV)
+	    {
+	      clearerr(in);
+	      continue;
+	    }
+#endif
 	  else
 	    {
@@ -1049,4 +1057,5 @@
   size_t i, j;
   char  tests[512];
+  int   retval;
 
   pid_t this_pid;
@@ -1060,5 +1069,5 @@
       SH_MUTEX_LOCK(mutex_thread_nolog);
       sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
-		      _("Internal error: NULL argument"), 
+		      _("Internal error: NULL argument, switching off"), 
 		      _("sh_process_check_int"));
       SH_MUTEX_UNLOCK(mutex_thread_nolog);
@@ -1067,5 +1076,5 @@
 
   SH_MUTEX_LOCK(mutex_thread_nolog);
-  sh_processes_runps (res, NULL, 0, SH_PR_PS, 0);
+  retval = sh_processes_runps (res, NULL, 0, SH_PR_PS, 0);
   SH_MUTEX_UNLOCK(mutex_thread_nolog);
   for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
@@ -1075,6 +1084,16 @@
     }
   SH_MUTEX_LOCK(mutex_thread_nolog);
-  sh_processes_runps (res, NULL, 0, SH_PR_PS2, 0);
+  retval += sh_processes_runps (res, NULL, 0, SH_PR_PS2, 0);
   SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+  if (retval != 0)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+		      _("Failed to run ps, switching off"), 
+		      _("sh_process_check_int"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      SL_RETURN ((-1), _("sh_process_check_int"));
+    }
 
   /* Evaluate results
Index: /trunk/src/sh_readconf.c
===================================================================
--- /trunk/src/sh_readconf.c	(revision 168)
+++ /trunk/src/sh_readconf.c	(revision 169)
@@ -573,5 +573,4 @@
    */
   sl_rewind (fd);
-  sh_error_only_stderr (S_FALSE);
   if (0 != sh_gpg_check_sign (fd, 0, 1))
     aud_exit (FIL__, __LINE__, EXIT_FAILURE);
Index: /trunk/src/sh_string.c
===================================================================
--- /trunk/src/sh_string.c	(revision 169)
+++ /trunk/src/sh_string.c	(revision 169)
@@ -0,0 +1,825 @@
+
+#include "config_xor.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include "sh_string.h"
+#include "sh_mem.h"
+
+#undef  FIL__
+#define FIL__  _("sh_string.c")
+
+extern int sl_ok_adds (size_t a, size_t b);
+#define SL_TRUE  1
+#define SL_FALSE 0
+
+#include <ctype.h>
+/* Split array at delim in at most nfields fields. 
+ * Empty fields are returned as empty (zero-length) strings. 
+ * Leading and trailing WS are removed from token. 
+ * The number of fields is returned in 'nfields', their
+ * lengths in 'lengths'.
+ * A single delimiter will return two empty fields.
+ */
+char ** split_array(char *line, unsigned int * nfields, 
+                    char delim, size_t * lengths)
+{
+  char *a, *e, *s;
+  unsigned int i = 0;
+  int flag = 0;
+  char **arr;
+  unsigned int maxfields = (*nfields);
+
+  arr = SH_ALLOC((maxfields+1) * sizeof (char*));
+
+  e = line;
+
+  do
+    {
+      /* skip leading WS 
+       */
+      for (s=e; *s && isspace(*s); ++s) /* nothing */;
+
+      if (*s) 
+        {
+          a = s;
+
+          /* move a to next delim
+           */
+          for (a=s; *a && *a != delim; ++a) /* nothing */;
+          
+          /* set e to next after delim
+           */
+          if (*a == delim)
+            {
+              e    = a+1;
+              flag = 1;
+            }
+          else /* (!*a) */
+            {
+              e    = a;
+              flag = 0;
+            }
+
+          if (a != line)
+            {
+              /* chop off trailing WS 
+               */
+              for (a--; isspace(*a) && a > s; a--) /* do nothing */;
+              
+              /* terminate string
+               */
+              ++a; *a = '\0';
+            }
+          else
+            {
+              *a = '\0';
+            }
+        }
+      else /* (!*s) */
+        {
+          a = s;
+	  /* (i == 0) handles the special case of splitting the empty string */
+          if (flag || i == 0) 
+            {
+              flag = 0;
+              goto setnext;
+            }
+          break;
+        }
+
+    setnext:
+      lengths[i] = (a-s);
+      arr[i] = s;
+      ++i;
+
+    } while (i < maxfields);
+
+  *nfields = i;
+  arr[i]   = NULL;
+
+  return arr;
+}
+
+/* Split array at whitespace in at most nfields fields.
+ * Multiple whitespaces are collapsed. 
+ * Empty fields are returned as empty (zero-length) strings.
+ * The number of fields is returned in nfields.
+ * An empty string will return zero fields.
+ * If nfields < actual fields, last string will be remainder.
+ */
+char ** split_array_ws(char *line, unsigned int * nfields, size_t * lengths)
+{
+  char *a, *e, *s;
+  unsigned int i = 0;
+  char **arr;
+  unsigned int maxfields = (*nfields);
+
+  arr = SH_ALLOC((maxfields+1) * sizeof (char*));
+
+  e = line;
+
+  do
+    {
+      s = e;
+
+      /* skip leading WS 
+       */
+      if ( *s && isspace(*s) )
+        {
+          do {
+            ++s;
+          } while ( *s && isspace(*s) );
+        }
+
+      if (*s)
+        {
+
+          /* s is at non-ws, move a to next ws
+           */
+          a = s;
+          do {
+            a++;
+          } while ( *a && (!isspace(*a)) );
+
+          /* next token, *a is either ws or '\0' 
+           */
+          e = ( (*a) ? a+1 : a);
+          
+          /* terminate and set arr[i]
+           */
+          if (i < (maxfields-1))
+	    {
+              *a = '\0'; 
+	    }
+	  else
+	    {
+	      /* If nfields < actual fields, last 
+	       * string will be remainder. Therefore
+	       * skip to end.
+	       */
+	      if ( *a )
+		{
+		  do {
+		    a++;
+		  } while ( *a );
+		}
+	    }
+          lengths[i] = (a-s);
+          arr[i]     = s; 
+          ++i;
+        }
+      else /* if (!*s) */
+        {
+          break;
+        }
+
+    } while (i < maxfields);
+
+  *nfields = i;
+  arr[i]   = NULL;
+
+  return arr;
+}
+
+
+#define SH_STRING_PARCEL 120
+
+size_t sh_string_read(sh_string * s, FILE * fp, size_t maxlen)
+{
+
+  /* case 1) EOF or error 
+   */
+  if (fgets(s->str, s->siz, fp) == NULL)
+    {
+      sh_string_truncate(s, 0);
+      if (ferror(fp))
+        return -1;
+      return 0;
+    }
+
+  /* case 2) end of line reached. strlen should always be > 0
+   *         because of the '\n', but we check.
+   */
+  s->len = strlen(s->str);
+  if (s->len > 0 && (s->str)[s->len-1] == '\n') 
+    {
+      (s->str)[s->len-1] = '\0';
+      --(s->len);
+      return (s->len + 1);
+    }
+      
+  /* case 3) incomplete string
+   */
+  for (;;) {
+    
+    if (maxlen > 0 && (s->siz+SH_STRING_PARCEL) > maxlen)
+      {
+        if (s->siz < maxlen)
+          sh_string_grow(s, (maxlen-s->siz));
+        else
+          return -2;
+      }
+    else
+      {
+        sh_string_grow(s, 0);
+      }
+    
+    if (fgets(&(s->str[s->len]), (s->siz - s->len), fp) == NULL) 
+      {
+        if (ferror(fp))
+          {
+            sh_string_truncate(s, 0);
+            return -1;
+          }
+        return s->len;
+      }
+    
+    s->len += strlen( &(s->str[s->len]) );
+    if (s->len > 0 && s->str[s->len-1] == '\n')
+      {
+        (s->str)[s->len-1] = '\0';
+        --(s->len);
+        return (s->len + 1);
+      }
+  }
+
+  /* notreached */
+}
+
+sh_string * sh_string_cat_lchar(sh_string * s, char * str, size_t len)
+{
+  if (sl_ok_adds(len, s->siz) == SL_TRUE)
+    {
+      if ((len + 1 + s->len) > s->siz)
+	{
+	  sh_string_grow(s, ((len+1+s->len) - s->siz) );
+	}
+      memcpy(&(s->str[s->len]), str, len);
+      s->len += len;
+      s->str[s->len] = '\0';
+      return s;
+    }
+
+  return NULL;
+}
+
+sh_string * sh_string_set_from_char(sh_string * s, char * str)
+{
+  size_t len = strlen(str);
+
+  if ((len+1) > s->siz)
+    {
+      sh_string_grow(s, ((len+1) - s->siz) );
+    }
+  memcpy(s->str, str, (len+1));
+  return s;
+}
+
+sh_string * sh_string_new_from_lchar(char * str, size_t len)
+{
+  sh_string * s;
+  s      = SH_ALLOC(sizeof(sh_string));
+  s->str = SH_ALLOC(len+1);
+  memcpy(s->str, str, len);
+  s->str[len] = '\0';
+  s->siz = len+1;
+  s->len = len;
+  return s;
+}
+
+sh_string * sh_string_new_from_lchar3(char * str1, size_t len1,
+                                      char * str2, size_t len2,
+                                      char * str3, size_t len3)
+{
+  sh_string * s;
+  size_t len = 0;
+
+  if (sl_ok_adds(len1, len2) == SL_TRUE)
+    len    = len1 + len2;
+  else
+    return NULL;
+  if (sl_ok_adds( len, len3) == SL_TRUE)
+    len    = len  + len3;
+  else
+    return NULL;
+
+  s      = SH_ALLOC(sizeof(sh_string));
+  s->str = SH_ALLOC(len+1);
+
+  memcpy(s->str, str1, len1);
+  memcpy(&s->str[len1], str2, len2);
+  memcpy(&s->str[len1+len2], str3, len3);
+
+  s->str[len] = '\0';
+  s->siz = len+1;
+  s->len = len;
+  return s;
+}
+
+sh_string * sh_string_grow(sh_string * s, size_t increase)
+{
+  char * new;
+
+  if (increase == 0)
+    increase = SH_STRING_PARCEL;
+  
+  if (s && sl_ok_adds(s->siz, increase) == SL_TRUE)
+    {
+      new = SH_ALLOC(s->siz + increase);
+
+      if (s->str)
+        {
+          memcpy(new, s->str, s->len+1);
+          SH_FREE(s->str);
+        }
+      else
+        {
+          new[0] = '\0';
+        }
+      s->str  = new;
+      s->siz += increase;
+      return s;
+    }
+  return NULL;
+}
+
+sh_string * sh_string_truncate(sh_string * s, size_t len)
+{
+  if (s)
+    {
+      if (s->str && (s->len > len) )
+        {
+          s->len            = len;
+          (s->str)[len]     = '\0';
+        }
+      return s;
+    }
+  return NULL;
+}
+
+void sh_string_destroy(sh_string ** s)
+{
+  if (s)
+    {
+      if ((*s) && (*s)->str)
+        SH_FREE ((*s)->str);
+      SH_FREE(*s);
+      *s = NULL;
+    }
+  return;
+}
+
+sh_string * sh_string_new(size_t size)
+{
+  sh_string * s;
+  s      = SH_ALLOC (sizeof(sh_string));
+  if (size == 0)
+    size = SH_STRING_PARCEL;
+  s->str = SH_ALLOC (size);
+  s->str[0] = '\0';
+  s->siz = size;
+  s->len = 0;
+  return s;
+}
+
+/* Replaces fields in s with 'replacement'. Fields are given
+ * in the ordered array ovector, comprising ovecnum pairs 
+ * ovector[i], ovector[i+1] which list offset of first char
+ * of field, offset of first char after field (this is how
+ * the pcre library does it).
+ */  
+sh_string * sh_string_replace(const sh_string * s, 
+                              const int * ovector, int ovecnum, 
+                              const char * replacement, size_t rlen)
+{
+  sh_string * r = NULL;
+  char * p;
+  size_t len;
+  int    end    = 0;
+  int    start  = 0;
+  size_t oldlen = 0;
+  size_t newlen = 0;
+  long   diff;
+  int    i;
+
+
+  for (i = 0; i < ovecnum; ++i)
+    {
+      start = ovector[2*i];       /* offset of first char of substring       */
+      if (start >= end)
+        {
+          end   = ovector[2*i+1]; /* offset of first char after substring end*/
+
+          if (end > start && (unsigned int)end <= (s->len + 1))
+            {
+              oldlen += (end - start);
+              newlen += rlen;
+            }
+          else                    /* inconsistency detected                  */
+            {
+              return NULL;
+            }
+        }
+      else                        /* overlap detected                        */
+        {
+          return NULL;
+        }
+    }
+
+  diff = newlen - oldlen;
+
+  if ((diff > 0) && ((s->len + 1 + diff) > s->siz))
+    {
+      r = sh_string_new_from_lchar(sh_string_str(s), 
+                                   sh_string_len(s));
+      r = sh_string_grow(r, diff);
+    }
+  else
+    {
+      r = sh_string_new_from_lchar(sh_string_str(s), 
+                                   sh_string_len(s));
+    }
+
+  if (r && ovecnum > 0)
+    {
+      r->len = 0; r->str[0] = '\0'; p = r->str;
+
+      /* First part, until start of first replacement 
+       */
+      memcpy(p, s->str,      ovector[0]); p += ovector[0];
+      memcpy(p, replacement, rlen);       p += rlen;
+      *p = '\0'; r->len += (ovector[0] + rlen);
+
+      for (i = 1; i < ovecnum; ++i)
+        {
+          /* From end of last replacement to start of this */
+          len = ovector[2*i] - ovector[2*i -1];
+          memcpy(p, &(s->str[ovector[2*i -1]]), len);
+          p += len;
+
+          /* The replacement */
+          memcpy(p, replacement, rlen);       
+          p += rlen;
+
+          /* null terminate */
+          *p = '\0'; r->len += (len + rlen);
+        }
+
+      /* Last part, after last replacement; includes terminating null 
+       */
+      len = (s->len + 1) - ovector[2*i -1];
+      memcpy(p, &(s->str[ovector[2*i -1]]), len);
+      p += len; *p = '\0'; r->len += (len - 1);
+    }
+  return r;
+}
+
+
+#ifdef SH_CUTEST
+#include <stdlib.h>
+#include "CuTest.h"
+
+void Test_string (CuTest *tc) {
+  int status, i, max = 120;
+  FILE * fp;
+  sh_string * s = NULL;
+  sh_string * t;
+  static char template[] = "/tmp/xtest.XXXXXX";
+  char ** array;
+  char test[128];
+  size_t lengths[16];
+  unsigned int iarr;
+  int ovector[16];
+  int ovecnum;
+
+  s = sh_string_new(0);
+  CuAssertPtrNotNull(tc, s);
+  sh_string_destroy(&s);
+  CuAssertTrue(tc, s == NULL);
+
+  s = sh_string_new(0);
+  CuAssertPtrNotNull(tc, s);
+
+  status = mkstemp(template);
+  CuAssertTrue(tc, status >= 0);
+
+  fp = fdopen(status, "r+");
+  CuAssertPtrNotNull(tc, fp);
+
+  for (i = 0; i <  80; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 0
+  for (i = 0; i < 118; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 1
+  for (i = 0; i < 119; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 2
+  for (i = 0; i < 120; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 3
+  for (i = 0; i < 121; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 4
+  for (i = 0; i < 238; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+  for (i = 0; i < 239; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+  for (i = 0; i < 240; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+  for (i = 0; i < 241; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+
+  rewind(fp);
+
+  for (i = 0; i < 9; ++i)
+    {
+      status = sh_string_read(s, fp, max);
+
+      switch (i) {
+      case 0:
+	CuAssertTrue(tc, s->len ==  80);
+	CuAssertTrue(tc, s->siz == 120);
+	CuAssertTrue(tc, status ==  81);
+	break;
+      case 1:
+	CuAssertTrue(tc, s->len == 118);
+	CuAssertTrue(tc, s->siz == 120);
+	CuAssertTrue(tc, status == 119);
+	break;
+      case 2:
+	CuAssertTrue(tc, s->len == 119);
+	CuAssertTrue(tc, s->siz == 120);
+	CuAssertTrue(tc, status ==  -2); /* no terminating '\n', truncated */
+	break;
+      case 3:
+	CuAssertTrue(tc, s->len == 120);
+	CuAssertTrue(tc, s->siz == 240);
+	CuAssertTrue(tc, status == 121);
+	break;
+      case 4:
+	CuAssertTrue(tc, s->len == 121);
+	CuAssertTrue(tc, s->siz == 240);
+	CuAssertTrue(tc, status == 122);
+	break;
+      case 5:
+	CuAssertTrue(tc, s->len == 238);
+	CuAssertTrue(tc, s->siz == 240);
+	CuAssertTrue(tc, status == 239);
+	break;
+      case 6:
+	CuAssertTrue(tc, s->len == 239);
+	CuAssertTrue(tc, s->siz == 240);
+	CuAssertTrue(tc, status ==  -2); /* no terminating '\n', truncated */
+	break;
+      default:
+	CuAssertTrue(tc, s->len == 239);
+	CuAssertTrue(tc, s->siz == 240);
+	CuAssertTrue(tc, status ==  -2);
+      }
+      if (status == -2) /* read in rest of string */
+        { max = 240; sh_string_read(s, fp, max); }
+    }
+
+  rewind(fp);
+
+  sh_string_truncate(s, 0);
+  CuAssertTrue(tc, s->len == 0);
+
+  for (i = 0; i < 9; ++i)
+    {
+      status = sh_string_read(s, fp, 240);
+      if (status == -2)
+        sh_string_read(s, fp, 240);
+      else
+        {
+          for (status = 0; status < (int)s->len; ++status)
+            {
+              if (s->str[status] != 'a')
+                {
+                  CuFail(tc, "unexpected character");
+                }
+            }
+        }
+    }
+
+  status = fclose(fp); 
+  CuAssertTrue(tc, status == 0);
+  status = remove(template);
+  CuAssertTrue(tc, status == 0);
+
+  iarr = 10; strcpy(test, "|a1|| a2| |a3 |a4|a5|");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,9,(int)iarr);
+  CuAssertStrEquals(tc,"",   array[0]);
+  CuAssertStrEquals(tc,"a1", array[1]);
+  CuAssertStrEquals(tc,"",   array[2]);
+  CuAssertStrEquals(tc,"a2", array[3]);
+  CuAssertStrEquals(tc,"",   array[4]);
+  CuAssertStrEquals(tc,"a3", array[5]);
+  CuAssertStrEquals(tc,"a4", array[6]);
+  CuAssertStrEquals(tc,"a5", array[7]);
+  CuAssertStrEquals(tc,"",   array[8]);
+
+  CuAssertIntEquals(tc, 0, (int)lengths[0]);
+  CuAssertIntEquals(tc, 2, (int)lengths[1]);
+  CuAssertIntEquals(tc, 0, (int)lengths[2]);
+  CuAssertIntEquals(tc, 2, (int)lengths[3]);
+  CuAssertIntEquals(tc, 0, (int)lengths[4]);
+  CuAssertIntEquals(tc, 2, (int)lengths[5]);
+  CuAssertIntEquals(tc, 2, (int)lengths[6]);
+  CuAssertIntEquals(tc, 2, (int)lengths[7]);
+  CuAssertIntEquals(tc, 0, (int)lengths[8]);
+
+  iarr = 10; strcpy(test, "a1|| a2| |a3 |a4|a5|");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,8,(int)iarr);
+  CuAssertStrEquals(tc,"a1", array[0]);
+  CuAssertStrEquals(tc,"",   array[1]);
+  CuAssertStrEquals(tc,"a2", array[2]);
+  CuAssertStrEquals(tc,"",   array[3]);
+  CuAssertStrEquals(tc,"a3", array[4]);
+  CuAssertStrEquals(tc,"a4", array[5]);
+  CuAssertStrEquals(tc,"a5", array[6]);
+  CuAssertStrEquals(tc,"",   array[7]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+  CuAssertIntEquals(tc, 0, (int)lengths[1]);
+  CuAssertIntEquals(tc, 2, (int)lengths[2]);
+  CuAssertIntEquals(tc, 0, (int)lengths[3]);
+  CuAssertIntEquals(tc, 2, (int)lengths[4]);
+  CuAssertIntEquals(tc, 2, (int)lengths[5]);
+  CuAssertIntEquals(tc, 2, (int)lengths[6]);
+  CuAssertIntEquals(tc, 0, (int)lengths[7]);
+
+  iarr = 10; strcpy(test, "  a1|| a2  | |a3 |a4|a5");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,7,(int)iarr);
+  CuAssertStrEquals(tc,"a1", array[0]);
+  CuAssertStrEquals(tc,"",   array[1]);
+  CuAssertStrEquals(tc,"a2", array[2]);
+  CuAssertStrEquals(tc,"",   array[3]);
+  CuAssertStrEquals(tc,"a3", array[4]);
+  CuAssertStrEquals(tc,"a4", array[5]);
+  CuAssertStrEquals(tc,"a5", array[6]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+  CuAssertIntEquals(tc, 0, (int)lengths[1]);
+  CuAssertIntEquals(tc, 2, (int)lengths[2]);
+  CuAssertIntEquals(tc, 0, (int)lengths[3]);
+  CuAssertIntEquals(tc, 2, (int)lengths[4]);
+  CuAssertIntEquals(tc, 2, (int)lengths[5]);
+  CuAssertIntEquals(tc, 2, (int)lengths[6]);
+
+  iarr = 10; strcpy(test, "a1|| a2  | |a3 |a4|a5  ");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,7,(int)iarr);
+  CuAssertStrEquals(tc,"a1", array[0]);
+  CuAssertStrEquals(tc,"",   array[1]);
+  CuAssertStrEquals(tc,"a2", array[2]);
+  CuAssertStrEquals(tc,"",   array[3]);
+  CuAssertStrEquals(tc,"a3", array[4]);
+  CuAssertStrEquals(tc,"a4", array[5]);
+  CuAssertStrEquals(tc,"a5", array[6]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+  CuAssertIntEquals(tc, 0, (int)lengths[1]);
+  CuAssertIntEquals(tc, 2, (int)lengths[2]);
+  CuAssertIntEquals(tc, 0, (int)lengths[3]);
+  CuAssertIntEquals(tc, 2, (int)lengths[4]);
+  CuAssertIntEquals(tc, 2, (int)lengths[5]);
+  CuAssertIntEquals(tc, 2, (int)lengths[6]);
+
+  iarr = 10; strcpy(test, "|");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,2,(int)iarr);
+  CuAssertStrEquals(tc,"",   array[0]);
+  CuAssertStrEquals(tc,"",   array[1]);
+
+  CuAssertIntEquals(tc, 0, (int)lengths[0]);
+  CuAssertIntEquals(tc, 0, (int)lengths[1]);
+
+  iarr = 10; strcpy(test, "|||");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,4,(int)iarr);
+  CuAssertStrEquals(tc,"",   array[0]);
+  CuAssertStrEquals(tc,"",   array[1]);
+  CuAssertStrEquals(tc,"",   array[2]);
+  CuAssertStrEquals(tc,"",   array[3]);
+
+  CuAssertIntEquals(tc, 0, (int)lengths[0]);
+  CuAssertIntEquals(tc, 0, (int)lengths[1]);
+  CuAssertIntEquals(tc, 0, (int)lengths[2]);
+  CuAssertIntEquals(tc, 0, (int)lengths[3]);
+
+  iarr = 10; strcpy(test, " a1 ");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,1,(int)iarr);
+  CuAssertStrEquals(tc,"a1",   array[0]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+
+  iarr = 10; strcpy(test, "");
+  array  = split_array(test, &iarr, '|', lengths);
+  CuAssertIntEquals(tc,1,(int)iarr);
+  CuAssertStrEquals(tc,"",   array[0]);
+
+  CuAssertIntEquals(tc, 0, (int)lengths[0]);
+
+  /* WS separated */
+
+  iarr = 10; strcpy(test, "a1");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,1,(int)iarr);
+  CuAssertStrEquals(tc,"a1",   array[0]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+
+  iarr = 10; strcpy(test, " a1");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,1,(int)iarr);
+  CuAssertStrEquals(tc,"a1",   array[0]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+
+  iarr = 10; strcpy(test, " a1 ");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,1,(int)iarr);
+  CuAssertStrEquals(tc,"a1",   array[0]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+
+  iarr = 10; strcpy(test, "   ");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,0,(int)iarr);
+  CuAssertTrue(tc, array[0] == NULL);
+
+  iarr = 10; strcpy(test, " a1 a2");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,2,(int)iarr);
+  CuAssertStrEquals(tc,"a1",   array[0]);
+  CuAssertStrEquals(tc,"a2",   array[1]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+  CuAssertIntEquals(tc, 2, (int)lengths[1]);
+
+  iarr = 10; strcpy(test, " a1  a2  ");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,2,(int)iarr);
+  CuAssertStrEquals(tc,"a1",   array[0]);
+  CuAssertStrEquals(tc,"a2",   array[1]);
+
+  CuAssertIntEquals(tc, 2, (int)lengths[0]);
+  CuAssertIntEquals(tc, 2, (int)lengths[1]);
+
+  iarr = 10; strcpy(test, "");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,0,(int)iarr);
+  CuAssertTrue(tc, array[0] == NULL);
+
+  iarr = 3; strcpy(test, " this is a test for remainder");
+  array  = split_array_ws (test, &iarr, lengths);
+  CuAssertIntEquals(tc,3,(int)iarr);
+  CuAssertStrEquals(tc,"this",   array[0]);
+  CuAssertStrEquals(tc,"is",     array[1]);
+  CuAssertStrEquals(tc,"a test for remainder",     array[2]);
+  for (i = 0; i < 3; ++i)
+    {
+      CuAssertIntEquals(tc, (int)strlen(array[i]), lengths[i] );
+    }
+
+  /* string replace */
+  s = sh_string_new_from_lchar3 ("abc ", 4, "def ", 4, "ghi ", 4);
+  ovecnum = 2;
+  ovector[0] = 0;  ovector[1] = 2;
+  ovector[2] = 4;  ovector[3] = 11;
+
+  t = sh_string_replace(s, ovector, ovecnum, 
+                        "___", 3);
+  CuAssertStrEquals(tc, "___c ___ ",   t->str);
+  CuAssertIntEquals(tc, 9, (int)t->len);
+
+  ovector[0] = 0;  ovector[1] = 2;
+  ovector[2] = 4;  ovector[3] = 12; 
+  t = sh_string_replace(s, ovector, ovecnum, 
+                        "___", 3);
+  CuAssertStrEquals(tc, "___c ___",   t->str);
+  CuAssertIntEquals(tc, 8, (int)t->len);
+
+  ovector[0] = 0;  ovector[1] = 0;
+  ovector[2] = 0;  ovector[3] = 0; 
+  t = sh_string_replace(s, ovector, ovecnum, 
+                        "___", 3);
+  CuAssertTrue(tc, t == NULL);
+
+  ovector[0] = 0;  ovector[1] = 3;
+  ovector[2] = 3;  ovector[3] = 6; 
+  t = sh_string_replace(s, ovector, ovecnum, 
+                        "___", 3);
+  
+  CuAssertStrEquals(tc, "______f ghi ",   t->str);
+  CuAssertIntEquals(tc, 12, (int)t->len);
+
+  ovector[0] = 4;  ovector[1] = 5;
+  ovector[2] = 11;  ovector[3] = 12; 
+  t = sh_string_replace(s, ovector, ovecnum, 
+                        "___", 3);
+  CuAssertStrEquals(tc, "abc ___ef ghi___",   t->str);
+  CuAssertIntEquals(tc, 16, (int)t->len);
+
+  t = sh_string_replace(s, ovector, 0, 
+                        "___", 3);
+  CuAssertStrEquals(tc, s->str,   t->str);
+  CuAssertIntEquals(tc, (int)s->len, (int)t->len);
+
+}
+
+#endif
Index: /trunk/src/sh_tools.c
===================================================================
--- /trunk/src/sh_tools.c	(revision 168)
+++ /trunk/src/sh_tools.c	(revision 169)
@@ -582,5 +582,5 @@
 		      *errnum = 0;
 		      if (sl_strlen(host_entry->h_name) == 0 || 
-			  (*errnum = sl_strcmp(host_name,host_entry->h_name)) != 0)
+			  (*errnum = sl_strcasecmp(host_name,host_entry->h_name)) != 0)
 			{ 
 			  if (*errnum)
@@ -1663,4 +1663,6 @@
 	SH_FREE (file);
 	SH_FREE(my_tmp_dir);
+	sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN, 
+			_("Error (lstat) while opening temporary file"), _("open_tmp"));
 	TPT(( 0, FIL__, __LINE__, _("msg=<Unexpected error %d>\n"), error));
 	SL_RETURN((-1), _("open_tmp"));
Index: /trunk/src/sh_unix.c
===================================================================
--- /trunk/src/sh_unix.c	(revision 168)
+++ /trunk/src/sh_unix.c	(revision 169)
@@ -43,4 +43,10 @@
 #include <fcntl.h>
 #include <unistd.h>
+/* need to undef these, since the #define's may be picked up from
+ * linux/wait.h, and will clash with a typedef in sys/wait.h
+ */
+#undef P_ALL
+#undef P_PID
+#undef P_PGID
 #include <sys/wait.h>
 
@@ -2993,5 +2999,4 @@
 	      someval *= 1000;  /* milliseconds in a second */
 	      sometime = (unsigned long) someval;
-	      /* fprintf(stderr, "FIXME PAUSE %ld\n", sometime); */
 	      retry_msleep(0, sometime);
 	    }
@@ -2999,5 +3004,4 @@
 	    {
 	      sometime = (unsigned long) someval;
-	      /* fprintf(stderr, "FIXME PAUSE %ld sec\n", sometime); */
 	      retry_msleep (sometime, 0);
 	    }
@@ -3037,4 +3041,6 @@
 
   SL_ENTER(_("sh_unix_checksum_size"));
+
+  tmpFile.link_path = NULL;
 
   if (sh.flag.checkSum != SH_CHECK_INIT)
@@ -3256,5 +3262,8 @@
 }
 #endif
-    
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif    
 
 int sh_unix_getinfo (int level, char * filename, file_type * theFile, 
@@ -3402,5 +3411,5 @@
 	      UINT64 length_nolim = TIGER_NOLIM;
 
-	      if ((theFile->check_mask & MODI_TXT) != 0 && fbuf.st_size < SH_TXT_MAX)
+	      if ((theFile->check_mask & MODI_TXT) != 0 && fbuf.st_size < (10 * SH_TXT_MAX))
 		{
 		  sl_init_content (rval_open, fbuf.st_size);
@@ -3415,4 +3424,5 @@
 
 	      content = sl_get_content(rval_open);
+	      content = sh_string_copy(content);
 
 	      if ((theFile->check_mask & MODI_SGROW) != 0)
@@ -3454,5 +3464,5 @@
 	      UINT64 length_nolim = TIGER_NOLIM;
 
-	      if ((theFile->check_mask & MODI_TXT) != 0 && fbuf.st_size < SH_TXT_MAX)
+	      if ((theFile->check_mask & MODI_TXT) != 0 && fbuf.st_size < (10 * SH_TXT_MAX))
 		{
 		  sl_init_content (rval_open, fbuf.st_size);
@@ -3467,4 +3477,5 @@
 
 	      content = sl_get_content(rval_open);
+	      content = sh_string_copy(content);
 
 	      if ((theFile->check_mask & MODI_SGROW) != 0) 
@@ -3759,7 +3770,32 @@
       if (content)
 	{
-	  sh_util_base64_enc_alloc (&(theFile->link_path), 
-				    sh_string_str(content), 
-				    sh_string_len(content));
+#ifdef HAVE_LIBZ
+	  unsigned long   clen = compressBound(sh_string_len(content));
+	  unsigned char * compressed = SH_ALLOC(clen);
+	  if (Z_OK == compress(compressed, &clen, 
+			       (unsigned char *) sh_string_str(content), 
+			       sh_string_len(content)))
+	      {
+		if (clen < SH_TXT_MAX)
+		  {
+		    sh_util_base64_enc_alloc (&(theFile->link_path), 
+					      (char *) compressed, clen);
+		  }
+		else
+		  {
+		    char tmsg[128];
+		    char * tpath = sh_util_safe_name (theFile->fullpath);
+		    sl_snprintf(tmsg, sizeof(tmsg), 
+				_("compressed file too large (%lu bytes)"),
+				clen);
+		    sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, -1, 
+				     MSG_E_SUBGPATH, tmsg, 
+				     _("sh_unix_getinfo"), tpath);
+		    SH_FREE(tpath);
+		  }
+	      }
+	  SH_FREE(compressed);
+#endif
+	  sh_string_destroy(&content);
 	}
     } 
@@ -3770,6 +3806,81 @@
 #endif
 
-int sh_unix_unlock (char * lockfile, char * flag);
-int sh_unix_lock   (char * lockfile, char * flag);
+int sh_unix_unlock(char * lockfile, char * flag)
+{
+  int         error = 0;
+  
+  SL_ENTER(_("sh_unix_unlock"));
+
+  if (sh.flag.isdaemon == S_FALSE && flag == NULL)
+    SL_RETURN((0),_("sh_unix_unlock"));
+
+  /* --- Logfile is not locked to us. ---
+   */
+  if (sh.flag.islocked == BAD && flag != NULL) 
+    SL_RETURN((-1),_("sh_unix_unlock"));
+
+  /* --- Check whether the directory is secure. ---
+   */
+  if (0 != tf_trust_check (lockfile, SL_YESPRIV))
+    SL_RETURN((-1),_("sh_unix_unlock"));
+
+  /* --- Delete the lock file. --- 
+   */
+  error = retry_aud_unlink (FIL__, __LINE__, lockfile);
+  
+  if (error == 0)
+    {
+      if (flag != NULL)
+	sh.flag.islocked = BAD; /* not locked anymore */
+    }
+  else if (flag != NULL)
+    {
+      char errbuf[SH_ERRBUF_SIZE];
+      error = errno;
+      sh_error_handle ((-1), FIL__, __LINE__, error, MSG_E_UNLNK,
+		       sh_error_message(error, errbuf, sizeof(errbuf)), 
+		       lockfile);
+      SL_RETURN((-1),_("sh_unix_unlock"));
+    }
+  SL_RETURN((0),_("sh_unix_unlock"));
+}
+
+int sh_unix_lock (char * lockfile, char * flag)
+{
+  int filed;
+  int errnum;
+  char myPid[64];
+  SL_TICKET  fd;
+  extern int get_the_fd (SL_TICKET ticket);
+
+  SL_ENTER(_("sh_unix_lock"));
+
+  sprintf (myPid, "%ld\n", (long) sh.pid);             /* known to fit  */
+
+  fd = sl_open_safe_rdwr (lockfile, SL_YESPRIV);       /* fails if file exists */
+
+  if (!SL_ISERROR(fd))
+    {
+      errnum = sl_write (fd, myPid, sl_strlen(myPid));
+      filed = get_the_fd(fd);
+      fchmod (filed, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+      sl_close (fd);
+
+      if (!SL_ISERROR(errnum))
+	{
+	  if (flag != NULL)
+	    sh.flag.islocked = GOOD;
+	  SL_RETURN((0),_("sh_unix_lock"));
+	}
+    }
+
+  TPT((0, FIL__, __LINE__, _("msg=<open pid file failed>\n")));
+  if (flag != NULL)
+    sh.flag.islocked       = BAD;
+  SL_RETURN((-1),_("sh_unix_lock"));
+
+  /* notreached */
+}
+
 
 /* check whether file is locked
@@ -3964,41 +4075,4 @@
 }
 
-int sh_unix_unlock(char * lockfile, char * flag)
-{
-  int         error = 0;
-  
-  SL_ENTER(_("sh_unix_unlock"));
-
-  /* --- Logfile is not locked to us. ---
-   */
-  if (sh.flag.islocked == BAD && flag != NULL) 
-    SL_RETURN((-1),_("sh_unix_unlock"));
-
-  /* --- Check whether the directory is secure. ---
-   */
-  if (0 != tf_trust_check (lockfile, SL_YESPRIV))
-    SL_RETURN((-1),_("sh_unix_unlock"));
-
-  /* --- Delete the lock file. --- 
-   */
-  error = retry_aud_unlink (FIL__, __LINE__, lockfile);
-  
-  if (error == 0)
-    {
-      if (flag != NULL)
-	sh.flag.islocked = BAD; /* not locked anymore */
-    }
-  else if (flag != NULL)
-    {
-      char errbuf[SH_ERRBUF_SIZE];
-      error = errno;
-      sh_error_handle ((-1), FIL__, __LINE__, error, MSG_E_UNLNK,
-		       sh_error_message(error, errbuf, sizeof(errbuf)), 
-		       lockfile);
-      SL_RETURN((-1),_("sh_unix_unlock"));
-    }
-  SL_RETURN((0),_("sh_unix_unlock"));
-}
-
 /* rm lock for filename
  */
@@ -4029,41 +4103,4 @@
 {
   return sh_unix_unlock(sh.srvlog.alt, NULL);
-}
-
-int sh_unix_lock (char * lockfile, char * flag)
-{
-  int filed;
-  int errnum;
-  char myPid[64];
-  SL_TICKET  fd;
-  extern int get_the_fd (SL_TICKET ticket);
-
-  SL_ENTER(_("sh_unix_lock"));
-
-  sprintf (myPid, "%ld\n", (long) sh.pid);             /* known to fit  */
-
-  fd = sl_open_safe_rdwr (lockfile, SL_YESPRIV);       /* fails if file exists */
-
-  if (!SL_ISERROR(fd))
-    {
-      errnum = sl_write (fd, myPid, sl_strlen(myPid));
-      filed = get_the_fd(fd);
-      fchmod (filed, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
-      sl_close (fd);
-
-      if (!SL_ISERROR(errnum))
-	{
-	  if (flag != NULL)
-	    sh.flag.islocked = GOOD;
-	  SL_RETURN((0),_("sh_unix_lock"));
-	}
-    }
-
-  TPT((0, FIL__, __LINE__, _("msg=<open pid file failed>\n")));
-  if (flag != NULL)
-    sh.flag.islocked       = BAD;
-  SL_RETURN((-1),_("sh_unix_lock"));
-
-  /* notreached */
 }
 
Index: /trunk/src/slib.c
===================================================================
--- /trunk/src/slib.c	(revision 168)
+++ /trunk/src/slib.c	(revision 169)
@@ -108,8 +108,6 @@
 int  sl_trace_use (const char * dummy)
 {
-  if (dummy)
-    slib_do_trace = 1;
-  else
-    slib_do_trace = 1;
+  (void) dummy;
+  slib_do_trace = 1;
   return 0;
 }
@@ -156,4 +154,5 @@
 	  fprintf(trace_fp, "[%2d] %s \t - File %c%s%c at line %d\n", 
 		 trace_level, str, 0x22, file, 0x22, line);
+	  fflush(trace_fp);
 	}
       else
@@ -191,4 +190,5 @@
 	  fprintf(trace_fp, _("[%2d] %s \t - File %c%s%c at line %d\n"), 
 		 trace_level, str, 0x22, file, 0x22, line);
+	  fflush(trace_fp);
 	}
       else
@@ -1007,4 +1007,41 @@
 }
 
+#include <ctype.h>
+int sl_strcasecmp(const char * one, const char * two)
+{
+#ifdef SL_FAIL_ON_ERROR
+  SL_REQUIRE (one != NULL, _("one != NULL"));
+  SL_REQUIRE (two != NULL, _("two != NULL"));
+#endif
+
+  if (one && two)
+    {
+      do {
+	if (*one && *two)
+	  {
+	    if (tolower(*one) == tolower(*two))
+	      {
+		++one; ++two;
+	      }
+	    else if (tolower(*one) < tolower(*two))
+	      return -1;
+	    else
+	      return 1;
+	  }
+	else if (*one == '\0' && *two == '\0')
+	  return 0;
+	else if (*one == '\0')
+	  return -1;
+	else
+	  return 1;
+      } while (1 == 1);
+    }
+  else if (one == NULL && two != NULL)
+    return -1;
+  else if (one != NULL && two == NULL)
+    return 1;
+  else
+    return -7; /* default to not equal */
+}
 
 int sl_strcmp(const char * a, const char * b)
@@ -1022,5 +1059,5 @@
     return (1);
   else
-    return (-7); /* arbitrary */
+    return (-7); /* default to not equal */
 }
 
@@ -1040,5 +1077,5 @@
     return (1);
   else
-    return (-7); /* arbitrary */
+    return (-7); /* default to not equal */
 }
 
@@ -2232,4 +2269,5 @@
 		  if (is_nonblocking == SL_FALSE)
 		      retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags);
+		  TPT(( 0, FIL__, __LINE__, _("msg=<read error>")));
 		  return (SL_EREAD);
 		}
@@ -2247,4 +2285,5 @@
 	  if (is_nonblocking == SL_FALSE)
 	      retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags);
+	  TPT(( 0, FIL__, __LINE__, _("msg=<timeout>")));
 	  return (SL_TIMEOUT);
 	}
@@ -2253,4 +2292,5 @@
 	  if (is_nonblocking == SL_FALSE)
 	      retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags);
+	  TPT(( 0, FIL__, __LINE__, _("msg=<timeout>")));
 	  return (SL_EREAD);
 	}
@@ -2260,4 +2300,5 @@
 	  if (is_nonblocking == SL_FALSE)
 	      retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags);
+	  TPT(( 0, FIL__, __LINE__, _("msg=<terminated>")));
 	  return (SL_EREAD);
 	}
@@ -2270,4 +2311,5 @@
 	  if (is_nonblocking == SL_FALSE)
 	      retry_fcntl(FIL__, __LINE__, fd, F_SETFL, sflags);
+	  TPT(( 0, FIL__, __LINE__, _("msg=<timeout>")));
 	  return (SL_TIMEOUT);
 	}
@@ -2282,6 +2324,8 @@
 		     int timeout, int is_nonblocking)
 {
-  int    fd;
+  int    fd, retval;
  
+  SL_ENTER(_("sl_read_timeout"));
+
   if (buf_in == NULL || SL_ISERROR(fd = get_the_fd(ticket)))
     {
@@ -2289,14 +2333,15 @@
 	{
 	  TPT(( 0, FIL__, __LINE__, _("msg=<null buffer>")));
-	  return (SL_ENULL);
+	  SL_IRETURN((SL_ENULL), _("sl_read_timeout"));
 	}
       if (SL_ISERROR(fd = get_the_fd(ticket)))
 	{
 	  TPT(( 0, FIL__, __LINE__, _("msg=<ticket error> errno=<%d>"), fd));
-	  return (fd);
+	  SL_IRETURN((fd),  _("sl_read_timeout"));
 	}
     }
 
-  return sl_read_timeout_fd (fd, buf_in, count, timeout, is_nonblocking);
+  retval = sl_read_timeout_fd (fd, buf_in, count, timeout, is_nonblocking);
+  SL_IRETURN((retval), _("sl_read_timeout"));
 }
 
@@ -2310,13 +2355,15 @@
   char * buf;
 
+  SL_ENTER(_("sl_read"));
+
   if (count < 1)
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<range error>")));
-      return(SL_ERANGE);
+      SL_IRETURN((SL_ERANGE), _("sl_read"));
     }
   if (buf_in == NULL)
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<null buffer>")));
-      return (SL_ENULL);
+      SL_IRETURN((SL_ENULL), _("sl_read"));
     }
 
@@ -2324,5 +2371,5 @@
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<ticket error> errno=<%d>"), fd));
-      return (fd);
+      SL_IRETURN((fd), _("sl_read"));
     }
 
@@ -2345,7 +2392,7 @@
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<read error> errno=<%d>\n"), errno));
-      return (SL_EREAD);
-    }
-  return (bytes);
+      SL_IRETURN((SL_EREAD), _("sl_read"));
+    }
+  SL_IRETURN((bytes), _("sl_read"));
 }
 
@@ -2357,13 +2404,15 @@
   char * buf;
 
+  SL_ENTER(_("sl_read_fast"));
+
   if (count < 1)
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<range error>")));
-      return(SL_ERANGE);
+      SL_IRETURN((SL_ERANGE), _("sl_read_fast"));
     }
   if (buf_in == NULL)
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<null buffer>")));
-      return (SL_ENULL);
+      SL_IRETURN((SL_ENULL), _("sl_read_fast"));
     }
 
@@ -2371,5 +2420,5 @@
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<ticket error> errno=<%d>"), fd));
-      return (fd);
+      SL_IRETURN((fd), _("sl_read_fast"));
     }
 
@@ -2381,5 +2430,5 @@
       if (byteread >= 0) 
 	{
-	  return (byteread);
+	  SL_IRETURN((byteread), _("sl_read_fast"));
 	}  
     } while ( byteread == -1 && (errno == EINTR || errno == EAGAIN));
@@ -2389,7 +2438,7 @@
     {
       TPT(( 0, FIL__, __LINE__, _("msg=<read error> errno=<%d>\n"), errno));
-      return (SL_EREAD);
-    }
-  return (0);
+      SL_IRETURN((SL_EREAD), _("sl_read_fast"));
+    }
+  SL_IRETURN((0), _("sl_read_fast"));
 }
 
Index: /trunk/test/testrun_1.sh
===================================================================
--- /trunk/test/testrun_1.sh	(revision 168)
+++ /trunk/test/testrun_1.sh	(revision 169)
@@ -41,5 +41,5 @@
 testrun1_setup=0
 
-MAXTEST=13; export MAXTEST
+MAXTEST=15; export MAXTEST
 
 test_dirs () {
@@ -65,4 +65,33 @@
 	fi
     done
+}
+
+
+TESTPOLICY_15="
+[Misc]
+DigestAlgo=SHA1
+RedefReadOnly = +TXT
+[ReadOnly]
+dir=${BASE}
+"
+mod_testdata_15 () {
+    mod_testdata_1
+}
+chk_testdata_15 () {
+    chk_testdata_1
+}
+
+TESTPOLICY_14="
+[Misc]
+DigestAlgo=MD5
+RedefReadOnly = +TXT
+[ReadOnly]
+dir=${BASE}
+"
+mod_testdata_14 () {
+    mod_testdata_1
+}
+chk_testdata_14 () {
+    chk_testdata_1
 }
 
@@ -455,7 +484,10 @@
 mod_testdata_5 () {
     mod_testdata_4
-    echo "This is a xxxx file"  > "${BASE}/a/a/b/x"    # GrowingLogFiles
-    echo "This is a test file" > "${BASE}/a/a/b/y"     # GrowingLogFiles
-    echo "This is a xxxx file bad" > "${BASE}/a/a/b/z" # GrowingLogFiles
+    echo "1 This is a xxxx file" > "${BASE}/a/a/b/x"     # GrowingLogFiles
+    echo "1 This is a test file" > "${BASE}/a/a/b/y"     # GrowingLogFiles
+    echo "2 This is a test file" >> "${BASE}/a/a/b/y"    # GrowingLogFiles
+    echo "1 This is a xxxx file bad" > "${BASE}/a/a/b/z" # GrowingLogFiles
+    echo "2 This is a xxxx file bad" >>"${BASE}/a/a/b/z" # GrowingLogFiles
+    echo "3 This is a xxxx file bad" >>"${BASE}/a/a/b/z" # GrowingLogFiles
 }
 
@@ -716,5 +748,16 @@
 "
 mod_testdata_2 () {
-    mod_testdata_1;
+    # mod_testdata_1;
+    one_sec_sleep
+    touch "${BASE}/a/a/x"
+    chmod 0555 "${BASE}/a/a/y"
+    mv "${BASE}/a/b/y" "${BASE}/a/b/yy"; 
+    echo "1 This is a test file" >  "${BASE}/a/b/y";
+    echo "2 This is a test file" >> "${BASE}/a/b/y";
+    echo "4 This is a test file" >> "${BASE}/a/b/z";
+    rm "${BASE}/a/b/yy"; # mv/rm to force new inode
+    rm "${BASE}/a/b/l_y";
+    ln -s "${BASE}/a/b/x" "${BASE}/a/b/l_y";
+    echo "foobar" > "${BASE}/a/c/y"
     rm "${BASE}/a/a/c/y"
     echo "foobar" > "${BASE}/a/a/c/foo"
@@ -762,5 +805,5 @@
     fi
     tmp=`grep CRIT $LOGFILE | wc -l`
-    if [ $tmp -ne 8 ]; then
+    if [ $tmp -ne 10 ]; then
 	[ -z "$verbose" ] || log_msg_fail "policy count";
 	return 1
@@ -769,4 +812,6 @@
 
 TESTPOLICY_1="
+[Misc]
+RedefReadOnly = +TXT
 [ReadOnly]
 dir=${BASE}
@@ -777,6 +822,22 @@
     touch "${BASE}/a/a/x"
     chmod 0555 "${BASE}/a/a/y"
-    mv "${BASE}/a/b/y"  "${BASE}/a/b/yy"; echo "This is a test file" > "${BASE}/a/b/y"; rm "${BASE}/a/b/yy"
+    mv "${BASE}/a/b/y" "${BASE}/a/b/yy"; 
+    echo "1 This is a test file" >  "${BASE}/a/b/y";
+    echo "2 This is a test file" >> "${BASE}/a/b/y";
+    echo "4 This is a test file" >> "${BASE}/a/b/z";
+    rm "${BASE}/a/b/yy"; # mv/rm to force new inode
+    rm "${BASE}/a/b/l_y";
+    ln -s "${BASE}/a/b/x" "${BASE}/a/b/l_y";
     echo "foobar" > "${BASE}/a/c/y"
+    #
+    mv "${BASE}/b/x" "${BASE}/b/xx"; # mv/rm to force new inode 
+    mkdir "${BASE}/b/x"
+    rm "${BASE}/b/xx";
+    #
+    mv "${BASE}/b/y" "${BASE}/b/yy"; # mv/rm to force new inode
+    ln -s  "${BASE}/b/z" "${BASE}/b/y"
+    rm "${BASE}/b/yy";
+    #
+    rm "${BASE}/b/l_x";  echo "1 This is a test file" >  "${BASE}/b/l_x"; 
 }
 
@@ -792,4 +853,5 @@
 	if [ $tmp -ne 1 ]; then
 	    [ -z "$verbose" ] || log_msg_fail "${BASE}/${ff} (multiple)";
+	    return 1
 	fi
 	#
@@ -797,13 +859,44 @@
 	    egrep "Checksum.*${BASE}/${ff}/${gg}" $LOGFILE >/dev/null 2>&1
 	    if [ $? -ne 0 ]; then
-		[ -z "$verbose" ] || log_msg_fail "${BASE}/${ff}/${gg} (checking)";
-		return 1
+		if [ "${BASE}/${ff}" != "${BASE}/b" ]; then
+		    [ -z "$verbose" ] || log_msg_fail "${BASE}/${ff}/${gg} (checksum)";
+		    return 1
+		fi
 	    fi
 	    tmp=`egrep "Checksum.*${BASE}/${ff}/${gg}" $LOGFILE 2>/dev/null | wc -l`
 	    if [ $tmp -ne 1 ]; then
-		[ -z "$verbose" ] || log_msg_fail "${BASE}/${ff}/${gg} (multiple)";
+		if [ "${BASE}/${ff}" != "${BASE}/b" ]; then
+		    [ -z "$verbose" ] || log_msg_fail "${BASE}/${ff}/${gg} (multiple)";
+		    return 1
+		fi
 	    fi
 	done
     done
+    #
+    #
+    #
+    egrep "CRIT.*POLICY \[ReadOnly\] ----H---T-.*${BASE}/b" $LOGFILE >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+	[ -z "$verbose" ] || log_msg_fail "${BASE}/b";
+	return 1
+    fi
+    egrep "CRIT.*POLICY \[ReadOnly\] CL-I-M--TS.*${BASE}/b/y" $LOGFILE >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+	[ -z "$verbose" ] || log_msg_fail "${BASE}/b/y";
+	return 1
+    fi
+    egrep "CRIT.*POLICY \[ReadOnly\] CL---M--TS.*${BASE}/b/l_x" $LOGFILE >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+	[ -z "$verbose" ] || log_msg_fail "${BASE}/b/l_x";
+	return 1
+    fi
+    egrep "CRIT.*POLICY \[ReadOnly\] C--IHM--TS.*${BASE}/b/x" $LOGFILE >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+	[ -z "$verbose" ] || log_msg_fail "${BASE}/b/x";
+	return 1
+    fi
+    #
+    #
+    #
     egrep "CRIT.*POLICY \[ReadOnly\] --------T-.*${BASE}/a/a/x" $LOGFILE >/dev/null 2>&1
     if [ $? -ne 0 ]; then
@@ -821,4 +914,9 @@
 	return 1
     fi
+    egrep "CRIT.*POLICY \[ReadOnly\] -L-I----T-.*${BASE}/a/b/l_y" $LOGFILE >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+	[ -z "$verbose" ] || log_msg_fail "${BASE}/a/b/l_y";
+	return 1
+    fi
     egrep "CRIT.*POLICY \[ReadOnly\] --------T-.*${BASE}/a/b" $LOGFILE >/dev/null 2>&1
     if [ $? -ne 0 ]; then
@@ -826,4 +924,9 @@
 	return 1
     fi
+    egrep "CRIT.*POLICY \[ReadOnly\] C-------TS.*${BASE}/a/b/z" $LOGFILE >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+	[ -z "$verbose" ] || log_msg_fail "${BASE}/a/b/z";
+	return 1
+    fi
     egrep "CRIT.*POLICY \[ReadOnly\] C-------TS.*${BASE}/a/c/y" $LOGFILE >/dev/null 2>&1
     if [ $? -ne 0 ]; then
@@ -832,8 +935,17 @@
     fi
     tmp=`grep CRIT $LOGFILE | wc -l`
-    if [ $tmp -ne 5 ]; then
+    if [ $tmp -ne 11 ]; then
 	[ -z "$verbose" ] || log_msg_fail "policy count";
 	return 1
     fi
+    for ff in x y z; do
+	./samhain --list-file "${BASE}/a/a/${ff}" -d "$PW_DIR/.samhain_file" > "$PW_DIR/.samhain_tmp"
+	diff "$PW_DIR/.samhain_tmp" "${BASE}/a/a/${ff}" >/dev/null
+	if [ $? -ne 0 ]; then
+	    [ -z "$verbose" ] || log_msg_fail "diff $PW_DIR/.samhain_tmp ${BASE}/a/a/${ff}"
+	    return 1
+	fi
+    done
+
     return 0
 }
@@ -1028,7 +1140,11 @@
 	chmod 0755 "${BASE}/${ff}"
 	for gg in $TFILES; do
-	    echo "This is a test file" > "${BASE}/${ff}/${gg}"
+	    echo "1 This is a test file" > "${BASE}/${ff}/${gg}"
 	    chmod 0644 "${BASE}/${ff}/${gg}"
+	    ln -s "${BASE}/${ff}/${gg}" "${BASE}/${ff}/l_${gg}"
 	done
+	echo "2 This is a test file" >> "${BASE}/${ff}/y"
+	echo "2 This is a test file" >> "${BASE}/${ff}/z"
+	echo "3 This is a test file" >> "${BASE}/${ff}/z"
     done
 }
Index: /trunk/test/testrun_1b.sh
===================================================================
--- /trunk/test/testrun_1b.sh	(revision 168)
+++ /trunk/test/testrun_1b.sh	(revision 169)
@@ -20,5 +20,5 @@
 #
 
-MAXTEST=5; export MAXTEST
+MAXTEST=6; export MAXTEST
 LOGFILE="$PW_DIR/.samhain_log"; export LOGFILE
 RCFILE="$PW_DIR/testrc_1.dyn";  export RCFILE
@@ -136,4 +136,51 @@
 
 	mv $PW_DIR/.samhain_file.asc $PW_DIR/.samhain_file
+}
+
+testrun1b_nogpg ()
+{
+	BUILDOPTS="$1"
+	#
+	# test standalone compilation
+	#
+	[ -z "$verbose" ] || { echo; echo "${S}Building standalone agent${E}"; echo; }
+	#
+	if test -r "Makefile"; then
+		$MAKE distclean >/dev/null >&1
+	fi
+
+	${TOP_SRCDIR}/configure ${BUILDOPTS} 2>/dev/null 
+        #
+	#
+	if test x$? = x0; then
+		[ -z "$verbose" ] ||     log_msg_ok "configure..."; 
+		$MAKE  > /dev/null 2>&1
+		if test x$? = x0; then
+		    [ -z "$verbose" ] || log_msg_ok "make..."; 
+		else
+		    [ -z "$quiet" ] &&   log_msg_fail "make..."; 
+		    return 1
+		fi
+
+	else
+		[ -z "$quiet" ] &&       log_msg_fail "configure...";
+		return 1
+	fi
+
+	rm -f ./.samhain_file
+	rm -f ./.samhain_log
+	rm -f ./.samhain_lock
+
+	cp "${SCRIPTDIR}/testrc_1" "${RCFILE}"
+
+	./samhain -t init -p none -l info
+
+	if test x$? = x0; then
+	    [ -z "$verbose" ] || log_msg_ok    "init...";
+	else
+	    [ -z "$quiet" ]   && log_msg_fail  "init...";
+	    return 1
+	fi
+
 }
 
@@ -316,4 +363,30 @@
 		fi
 	    fi
+
+	    #
+	    # -------------  sixth test -------------
+	    #
+	    if test -f /usr/local/lib/libprelude.so
+	    then
+		    LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
+		    export LD_LIBRARY_PATH
+	    fi
+	    #
+	    PM=`find_path prelude-manager`
+	    if [ -z "$PM" ]; then
+		log_skip 6 $MAXTEST 'prelude-manager not found in $PATH'
+	    elif [ -z "$doall" ]; then
+		log_skip 6 $MAXTEST 'logging to prelude (or use --really-all)'
+	    else
+		BUILDOPTS="--quiet $TRUST --with-prelude --enable-login-watch --enable-mounts-check --enable-process-check --enable-port-check --enable-suidcheck --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$RCFILE  --with-log-file=$PW_DIR/.samhain_log --with-pid-file=$PW_DIR/.samhain_lock --with-data-file=$PW_DIR/.samhain_file"
+		testrun1b_nogpg "${BUILDOPTS} CFLAGS=-DSH_NOFAILOVER=1"
+		do_test_1b_2
+		if [ $? -eq 0 ]; then
+		    log_ok   6 $MAXTEST 'logging to prelude'
+		else
+		    log_fail 6 $MAXTEST 'logging to prelude'
+		fi
+	    fi
+
 	fi
     fi
Index: /trunk/test/testrun_1d.sh
===================================================================
--- /trunk/test/testrun_1d.sh	(revision 168)
+++ /trunk/test/testrun_1d.sh	(revision 169)
@@ -197,8 +197,10 @@
 proc_psarg ()
 {
-    OS=`uname -o`
+    OS=`uname -s`
     case $OS in
 	*Linux*|*linux*)
 	    PSARG="-eT";;
+        *OpenBSD*)
+            PSARG="akx";;
 	*)
 	    PS=`proc_pspath`
