Index: trunk/src/sh_readconf.c
===================================================================
--- trunk/src/sh_readconf.c	(revision 196)
+++ trunk/src/sh_readconf.c	(revision 197)
@@ -43,4 +43,5 @@
 #include "sh_prelink.h"
 #include "sh_extern.h"
+#include "sh_tools.h"
 
 #ifdef WITH_DATABASE
@@ -135,4 +136,151 @@
 };
 
+enum {
+  SH_RC_ANY        = 0, 
+  SH_RC_HOST       = 1, 
+  SH_RC_SYSTEM     = 2,
+  SH_RC_FILE       = 3,
+  SH_RC_IFACE      = 4,
+  SH_RC_CMD        = 5
+};
+
+
+static int sh_readconf_cond_match(char * str, int line)
+{
+  int    match  = 0;
+  int    negate = 1;
+  int    cond_type = SH_RC_ANY;
+  char   myident[3*SH_MINIBUF+3];
+  struct stat buf;
+
+  char * p = str;
+
+  if (*p == '!') { negate = 0; ++p; }
+  if (*p == '$') { 
+    cond_type = SH_RC_SYSTEM; ++p; /* [!]$system */ 
+  }
+  else { /* *p == '@' */
+
+    ++p; while (isspace((int)*p)) ++p;
+
+    if (0 != strncmp(p, _("if "),   3)) {
+      cond_type = SH_RC_HOST; /* [!]$host */
+    }
+
+    else {
+
+      p += 3; while (isspace((int)*p)) ++p; /* skip the 'if\s+' */
+
+      if (0 == strncmp(p, _("not "), 4))
+	{
+	  p += 4; while (isspace((int)*p)) ++p;
+	  negate = 0;
+	}
+      else if (0 == strncmp(p, _("!"), 1))
+	{
+	  ++p; while (isspace((int)*p)) ++p;
+	  negate = 0;
+	}
+  
+      if (0 == strncmp(p, _("file_exists "), 12))
+	{
+	  p += 12; cond_type = SH_RC_FILE;
+	}
+      else if (0 == strncmp(p, _("interface_exists "), 17))
+	{
+	  p += 17; cond_type = SH_RC_IFACE;
+	}
+      else if (0 == strncmp(p, _("hostname_matches "), 17))
+	{
+	  p += 17; cond_type = SH_RC_HOST;
+	}
+      else if (0 == strncmp(p, _("system_matches "), 15))
+	{
+	  p += 15; cond_type = SH_RC_SYSTEM;
+	}
+      else if (0 == strncmp(p, _("command_succeeds "), 17))
+	{
+	  p += 17; cond_type = SH_RC_CMD;
+	}
+      else
+	{
+	  char errbuf[SH_ERRBUF_SIZE];
+	  sl_snprintf(errbuf, sizeof(errbuf), 
+		      _("Unsupported test at line %d of configuration file"),
+		      line);
+	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+			  errbuf,
+			  _("sh_readconf_cond_match"));
+	  return 0;
+	}
+    }
+  }
+
+  while (isspace((int)*p)) ++p;
+
+  switch (cond_type)
+    {
+    case SH_RC_HOST:
+      if  (sl_strncmp (p,  sh.host.name, strlen(sh.host.name)) == 0
+#ifdef HAVE_REGEX_H
+	   || sh_util_regcmp (p, sh.host.name) == 0
+#endif
+	   )
+	match = negate;
+      break;
+    case SH_RC_SYSTEM:
+      /*
+       * The system type, release, and machine.
+       */
+      sl_snprintf(myident, sizeof(myident), _("%s:%s:%s"),  
+		  sh.host.system, /* flawfinder: ignore */ 
+		  sh.host.release, sh.host.machine);
+      
+      if  (sl_strncmp (p,  myident, sl_strlen(myident)) == 0
+#ifdef HAVE_REGEX_H
+	   || sh_util_regcmp (p, myident) == 0
+#endif
+	   )
+	match = negate;
+      break;
+    case SH_RC_FILE:
+      if (0 == retry_lstat(FIL__, __LINE__, p, &buf))
+	match = negate;
+      break;
+    case SH_RC_IFACE:
+      if (sh_tools_iface_is_present(p))
+	match = negate;
+      break;
+    case SH_RC_CMD:
+      if (0 == sh_unix_run_command(p))
+	match = negate;
+      break;
+    default:
+      match = 0;
+    }
+  return match;
+}
+
+static int sh_readconf_is_end (char * str)
+{
+  int retval = 0;
+
+  if (str[0] == '@' || str[0] == '$')
+    {
+      char * p = str;
+      ++p; while (isspace((int)*p)) ++p;
+      if (0 == strcmp (p, _("end")     ) ||
+	  0 == strncmp(p, _("end "),  4) || /* Comment to follow */
+	  0 == strcmp (p, _("endif")   ) ||
+	  0 == strncmp(p, _("endif "),6) ||
+	  0 == strcmp (p, _("fi")      ) ||
+	  0 == strncmp(p, _("fi "),   3)
+	  )
+	{
+	  return 1;
+	}
+    }
+  return retval;
+}
    
 static int sh_readconfig_line (char * line);
@@ -160,24 +308,13 @@
 #endif
   char * tmp;
-  char * lptr;
 
   char   line_in[512+2];
   char * line;
-  int    line_int;
-
-  char   myident[3*SH_MINIBUF+3];
 
   /* This is for nested conditionals.
    */
-  int    some_other_host[16]   = { 0 };
-  int    some_other_system[16] = { 0 };
-  int    seen_host   = 0;
-  int    seen_system = 0;
-  int    host_int    = 0;
-  int    sys_int     = 0;
-
-  int    invert = 0;
-  int    length = sl_strlen(sh.host.name);
-
+  int    cond_depth  = 0;
+  int    cond_excl   = 0;
+  
   int    local_file = 1;
   char   local_flag = 'R';
@@ -288,10 +425,4 @@
   sl_rewind (fd);
 #endif
-
-  /* The system type, release, and machine.
-   */
-  sl_snprintf(myident, sizeof(myident), _("%s:%s:%s"),  
-	      sh.host.system, /* flawfinder: ignore */ 
-	      sh.host.release, sh.host.machine);
 
 
@@ -345,11 +476,6 @@
     /* Skip leading white space.
      */
-    while (*line)
-      {
-	line_int = *line;
-	if (!isspace(line_int))
-	  break;
-	++line;
-      }
+    while (isspace((int)*line)) ++line;
+
 
     /* Skip header etc. 
@@ -365,17 +491,55 @@
 
 
+    /* ---  an @host/@if/$system directive -------------- */
+
+    if (line[0] == '@' || (line[0] == '!' && line[1] == '@') || 
+	line[0] == '$' || (line[0] == '!' && line[1] == '$'))
+      {
+	if (sh_readconf_is_end(line))
+	  {
+	    if (0 == cond_depth) {
+	      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_EINVALD,
+			       _("config file"), 
+			       (long) conf_line);
+	    }
+	    else {
+	      if (cond_excl == cond_depth)
+		cond_excl = 0;
+	      --cond_depth;
+	    }
+	  }
+	else
+	  {
+	    if (sh_readconf_cond_match(line, conf_line)) {
+	      ++cond_depth;
+	    }
+	    else {
+	      ++cond_depth;
+	      if (cond_excl == 0)
+		cond_excl = cond_depth;
+	    }
+	  }
+	continue;
+      }
+
+    /****************************************************
+     *
+     * Only carry on if this section is intended for us
+     *
+     ****************************************************/
+    
+    if (cond_excl != 0) {
+      continue;
+    }
+
     /* -------  starts a section  ------------  */
     
-    if (line[0] == '['                       && 
-	some_other_host[seen_host] == 0      &&
-	some_other_system[seen_system] == 0) 
+    else if (line[0] == '[')
       { 
 	read_mode = SH_SECTION_NONE;
 
-	if (sl_strncmp (line,  _("[EOF]"), 
-			  5) == 0)
-	  {
-	    goto nopel;
-	  }
+	if (0 == sl_strncmp (line,  _("[EOF]"), 5)) {
+	  goto nopel;
+	}
 
 	i = 0;
@@ -410,70 +574,4 @@
       } 
 
-    /* ---  an @host directive -------------- */
-
-    else if (line[0] == '@' || (line[0] == '!' && line[1] == '@')) 
-      {
-	if (line[0] == '!')
-	  {
-	    lptr   = &line[2];
-	    invert = 1;
-	  }
-	else
-	  {
-	    lptr   = &line[1];
-	    invert = 0;
-	  }
-
-	if (sl_strncmp (lptr, _("end"), 3) == 0)
-	  {
-	    if (0 == seen_host)
-	      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_EINVALD,
-			       _("config file"), 
-			       (long) conf_line);
-
-	    else if (host_int == 0)
-	      {
-		/* end  of an @host directive 
-		 */
-		some_other_host[seen_host] = 0;
-		--seen_host;
-		seen_host = (seen_host < 0) ? 0 : seen_host;
-	      }
-
-	    else 
-	      {
-		--host_int;
-		host_int = (host_int < 0) ? 0 : host_int;
-	      }
-	  }
-	else if (some_other_host[seen_host] == 0      &&
-		 some_other_system[seen_system] == 0  && 
-		 seen_host < 15)
-	  {
-	    if  (sl_strncmp (lptr,  sh.host.name, length) == 0
-#ifdef HAVE_REGEX_H
-		 || sh_util_regcmp (lptr, sh.host.name) == 0
-#endif
-		 )
-	      {
-		/* if match and '@',  set some_other_host = 0;
-		 * if match and '!@', set some_other_host = 1;
-		 */
-		++seen_host;
-		some_other_host[seen_host] = invert; 
-	      }
-	    else
-	      {
-		/* if no match and '@',  set some_other_host = 1;
-		 * if no match and '!@', set some_other_host = 0;
-		 */
-		++seen_host;
-		some_other_host[seen_host] = (invert == 0) ? 1 : 0;
-	      }
-	  }
-	else
-	  ++host_int;
-      } 
-
     /* ---  an %schedule directive ------------ */
 
@@ -489,70 +587,9 @@
 #endif
       }
-	  
-    /* ---  an $system directive -------------- */
-
-    else if (line[0] == '$' || (line[0] == '!' && line[1] == '$')) 
-      {
-	if (line[0] == '!')
-	  {
-	    lptr   = &line[2];
-	    invert = 1;
-	  }
-	else
-	  {
-	    lptr   = &line[1];
-	    invert = 0;
-	  }
-
-	if (sl_strncmp (lptr, _("end"), 3) == 0)
-	  {
-	    if (0 == seen_system)
-	      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_EINVALD,
-			       _("config file"),
-			       (long) conf_line);
-
-	    else if (sys_int == 0)
-	      {
-		/* end  of an $system directive 
-		 */
-		some_other_system[seen_system] = 0;
-		--seen_system;
-		seen_system = (seen_system < 0) ? 0 : seen_system;
-	      }
-	    else 
-	      {
-		--sys_int;
-		sys_int = (sys_int < 0) ? 0 : sys_int;
-	      }
-	  }
-	else if (some_other_host[seen_host] == 0      &&
-		 some_other_system[seen_system] == 0  && 
-		 seen_system < 15)
-	  {
-	    if  (sl_strncmp (lptr,  myident, sl_strlen(myident)) == 0
-#ifdef HAVE_REGEX_H
-		 || sh_util_regcmp (lptr, myident) == 0
-#endif
-		 )
-	      {
-		++seen_system;
-		some_other_system[seen_system] = invert;
-	      }
-	    else
-	      {
-		++seen_system;
-		some_other_system[seen_system] = (invert == 0) ? 1 : 0;
-	      }
-	  }
-	else
-	  ++sys_int;
-      }
 
     /* ------  no new section -------------- */
 
 
-    else if (some_other_host[seen_host] == 0          && 
-	     some_other_system[seen_system] == 0      && 
-	     read_mode != SH_SECTION_NONE) 
+    else if (read_mode != SH_SECTION_NONE)
       { 
 	if (0 != sh_readconfig_line (line))
@@ -562,10 +599,9 @@
 	  }
       }
-
-  }
+  } /* while getline() */
 
  nopel:
 	   
-  if (0 != seen_host || 0 != seen_system)
+  if (0 != cond_depth)
     sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_EINVALDD,
 		     _("config file"), 
Index: trunk/src/sh_tools.c
===================================================================
--- trunk/src/sh_tools.c	(revision 196)
+++ trunk/src/sh_tools.c	(revision 197)
@@ -122,4 +122,42 @@
 
 #endif
+
+/* --- check for an interface ---
+ */
+int sh_tools_iface_is_present(char *str)
+{
+  struct sockaddr_in sin;
+  int sd;
+
+  memset(&sin, '\0', sizeof(sin));
+  sin.sin_family = AF_INET;
+  if (inet_aton(str, &(sin.sin_addr)))
+    {
+      sin.sin_port = htons(2543);
+
+      if (-1 == (sd = socket(AF_INET, SOCK_STREAM, 0)))
+	{
+	  return 0;
+	}
+
+      if (-1 == bind(sd, (struct sockaddr *)&sin, sizeof(sin)))
+	{
+	  int retval = 0;
+
+	  /* bind() fails for access reasons, iface exists
+	   */
+	  if (errno == EACCES || errno == EADDRINUSE)
+	    retval = 1;
+	  close (sd);
+	  return retval;
+	}
+
+      /* bind() succeeds, iface exists
+       */
+      close(sd);
+      return 1;
+    }
+  return 0;
+}
 
 /* --- recode all \blah escapes to '=XX' format, and also code all
Index: trunk/src/sh_unix.c
===================================================================
--- trunk/src/sh_unix.c	(revision 196)
+++ trunk/src/sh_unix.c	(revision 197)
@@ -2027,4 +2027,72 @@
 
   SL_RETURN((0),_("sh_unix_init"));
+}
+
+/* --- run a command, securely --- */
+
+int sh_unix_run_command (const char * str)
+{
+  pid_t  pid;
+  char * arg[4];
+  char * env[5];
+  char * path = sh_util_strdup(_("/bin/sh"));
+
+  int  status = -1;
+
+  arg[0] = sh_util_strdup(_("/bin/sh"));
+  arg[1] = sh_util_strdup(_("-c"));
+  arg[2] = sh_util_strdup(str);
+  arg[3] = NULL;
+
+  env[0] = sh_util_strdup(_("PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/ucb"));
+  env[1] = sh_util_strdup(_("SHELL=/bin/sh"));
+  env[2] = sh_util_strdup(_("IFS= \t\n"));
+  if (getenv("TZ")) {
+    char * tz = sh_util_strdup(getenv("TZ"));
+    if (SL_TRUE == sl_ok_adds (4, strlen(tz))) {
+	env[3] = SH_ALLOC(4+strlen(tz));
+	sl_strlcpy(env[3], "TZ=", 4);
+	sl_strlcat(env[3], tz   , 4+strlen(tz));
+    } else {
+      env[3] = NULL;
+    }
+  } else {
+    env[3] = NULL;
+  }
+  env[4] = NULL;
+
+  pid = fork();
+
+  if (pid == (pid_t)(-1))
+    {
+      return -1;
+    }
+
+  else if (pid == 0) /* child */
+    {
+      memset(skey, 0, sizeof(sh_key_t));
+      (void) umask(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+      sh_unix_closeall (3, -1, SL_TRUE); /* in child process */
+      execve(path, arg, env);
+      _exit(EXIT_FAILURE);
+    }
+
+  else /* parent */
+    {
+      int r;
+
+      while((r = waitpid(pid, &status, WCONTINUED|WUNTRACED)) != pid && r != -1) ;
+
+      if (r == -1 || !WIFEXITED(status)) 
+	{
+	  status = -1;
+	}
+      else
+	{
+	  status = WEXITSTATUS(status);
+	}
+     }
+
+  return status;
 }
 
