Subject: PATCH: if - elsif - else - endif syntax

From: Scott Smedley <[email-masked]>
Date: Thu, 29 Apr 2004 21:42:39 +1000

Hi all,

I had a go at attempting to implement if-elsif-else-endif syntax for
FVWM so I can do things like this in my .fvwm2rc file:

(Much more intuitive/maintainable than using PipeRead.)

if xdpyinfo | /bin/grep -q XINERAMA
	Echo "dual head system!"
	Xinerama on
	XineramaPrimaryScreen 0
	DeskTopSize 4x1
else
	Echo "NOT a dual head system."
	DeskTopSize 6x1
endif

if [[ $(hostname) = "hamlet.denmark.org" ]]
	Echo "This is SCoTT's home system"
elsif hostname | grep -q aao
	Echo "This is SCoTT's work system"
endif

if [ -f someSlackwareFile ]
	Echo "This is a Slackware system."
elsif [ -f /etc/someDebianFile ]
	Echo "This is a Debian system."
elsif [ -f /etc/redhat-release ]
	Echo "This is a RedHat system."
else
	Echo "No idea what system this is."
endif

if [ $(cat /proc/meminfo | grep ^MemTotal | tr -s ' ' | cut -f2 -d' ') -gt 500000 ]
	Echo "You have more than 0.5GB of RAM"
else
	Echo "You need to upgrade."
endif

Unfortunately I don't know of a way to make this work with FvwmConsole.

Does anyone have any comments, suggestions, criticism, encouragement?

SCoTT. :)
Index: fvwm/read.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/read.c,v
retrieving revision 1.60
diff -u -r1.60 read.c
--- fvwm/read.c	29 Jun 2003 19:53:23 -0000	1.60
+++ fvwm/read.c	29 Apr 2004 10:44:04 -0000
@@ -114,25 +114,16 @@
 	return curr_read_dir;
 }
 
-
-/*
- * Read and execute each line from stream.
- */
-void run_command_stream(
-	cond_rc_t *cond_rc, FILE *f, const exec_context_t *exc)
+/* __run_command_stream() - utility function for run_command_stream(). */
+void __run_command_stream(
+	cond_rc_t *cond_rc, FILE *f, const exec_context_t *exc,
+	Bool bInIf, Bool bIfTrue)
 {
-	char *tline;
+	char *tline, *tmp, *func = "__run_command_stream", *cond;
 	char line[1024];
+	Bool bIgnoreSection = !bIfTrue;
 
-	/* Set close-on-exec flag */
-	fcntl(fileno(f), F_SETFD, 1);
-
-	/* Update window decorations in case we were called from a menu that
-	 * has now popped down. */
-	handle_all_expose();
-
-	tline = fgets(line, (sizeof line) - 1, f);
-	while (tline)
+	while (NULL != (tline = fgets(line, (sizeof line) - 1, f)))
 	{
 		int l;
 		while (tline && (l = strlen(line)) < sizeof(line) && l >= 2 &&
@@ -150,6 +141,63 @@
 		{
 			tline[l - 1] = '\0';
 		}
+		/* remove leading whitespace */
+		tline = SkipSpaces(tline, NULL, 0);
+		/* ignore blank lines & comments. */
+		if (tline == NULL || tline[0] == '\0' || tline[0] == '#')
+			continue;
+
+		tmp = tline;
+		tmp = PeekToken(tmp, &cond);
+		if (strcasecmp(tmp, "if") == 0)
+		{
+			// fvwm_msg(DBG, func, "if-cmd=<%s>", cond);
+			bIfTrue = !system(cond);
+			// use recursion to allow nested if-statements.
+			__run_command_stream(cond_rc, f, exc, True, bIfTrue);
+			// at this point the complete if-statement has been handled.
+			// ie. we've parsed the file all the way through to the
+			// matching "endif".
+			continue;
+		}
+		else if (strcasecmp(tmp, "elsif") == 0)
+		{
+			if (!bInIf)
+				fvwm_msg(ERR, func, "\"elsif\" not within if-statement");
+			if (bIfTrue)
+				bIgnoreSection = True;
+			else
+			{
+				// fvwm_msg(DBG, func, "elsif-cmd=<%s>", cond);
+				bIfTrue = !system(cond);
+				bIgnoreSection = !bIfTrue;
+			}
+			continue;
+		}
+		else if (strcasecmp(tmp, "else") == 0)
+		{
+			if (!bInIf)
+				fvwm_msg(ERR, func, "\"else\" not within if-statement");
+			if (bIfTrue)
+				bIgnoreSection = True;
+			else
+			{
+				bIfTrue = True;
+				bIgnoreSection = False;
+			}
+			continue;
+		}
+		else if (strcasecmp(tmp, "endif") == 0)
+		{
+			if (!bInIf)
+				fvwm_msg(ERR, func, "\"endif\" not within if-statement");
+			else
+				return;
+		}
+
+		if (bInIf && bIgnoreSection)
+			continue;
+
 		if (debugging)
 		{
 			fvwm_msg(
@@ -158,12 +206,26 @@
 			exc->m.modnum, tline);
 		}
 		execute_function(cond_rc, exc, tline, 0);
-		tline = fgets(line, (sizeof line) - 1, f);
 	}
 
 	return;
 }
 
+/*
+ * Read and execute each line from stream.
+ */
+void run_command_stream(
+	cond_rc_t *cond_rc, FILE *f, const exec_context_t *exc)
+{
+	/* Set close-on-exec flag */
+	fcntl(fileno(f), F_SETFD, 1);
+
+	/* Update window decorations in case we were called from a menu that
+	 * has now popped down. */
+	handle_all_expose();
+
+	__run_command_stream(cond_rc, f, exc, False, False);
+}
 
 /**
  * Parse the action string.  We expect a filename, and optionally,