'k, the patched code didn't immediately crash, so here it is (two
patches).  Please test.

What this is about:

> The whole point in the completed part of the work was to make the
> command line parser pluggable.  As far as I remember, that part
> was done, but needed more testing by users.  Now that the rebase
> potentially contains some mistakes and may miss some things that
> have changed in between, it's even more important.
>
> There is a slight incompatibility of the new code with some thiung
> about quoting plus variable expansion.  I had started a thread on
> the mailing list and asked if that would annoy anybody and the
> people who replied said no.  Anyway (a) I cannot remember what it
> was exactly (we can dig it up from the mail archive) and (b) there
> won't be any progress if we make a new parser compatible with the
> crappy bugs of the old one.

The new code:

 * Rewrites functions.c to use hooks that are provided by a
   pluggable parser module (even at run time).
 * The module that replicates the old parser behaviour is in
   cmdparser_old.[ch].
 * The long term goal is to replace the _old parser with a new one
   that evauates the BNF definitions and does the parsing of
   function arguments before actually calling them.
 * To allow that, a "parsing context" structure is passed to the
   CMD_... functions.  This is already implemented but not used.
 * How the "parsing context" structure should look like is yet to
   be defined.
 * It shoul be entirely possible to convert command functions one
   by one to the new type of parser.  So that word does not need
   to be done in a single big step.

Possible pitfalls:

 * Watch out for changed whitespace behaviour.
 * A complex function name cannot start with a builtin name
   followed by an embedded space.
 * Possible to-dos in the code (look for the string '!!!').
 * Bad behaviour of the fvwm_debug function because of wrong
   parameters being passed.
 * The "repeat" command may cause crashes or leaks.
 * The general code in functions.c may as well.
 * Same in CMD_... functions and various parts of fvwm that call
   execute_function to do their work.
 * Debug code needt to be removed later.

Ciao

Dominik ^_^  ^_^

--

Dominik Vogt
From eddb78f108c0284fd7c670dd4d08811de59d2820 Mon Sep 17 00:00:00 2001
From: Dominik Vogt <dominik.v...@gmx.de>
Date: Wed, 17 Nov 2021 20:03:57 +0100
Subject: [PATCH 1/2] Rewrite parser framework.

---
 fvwm/Makefile.am         |   4 +-
 fvwm/add_window.c        |   9 +-
 fvwm/builtins.c          |   4 +-
 fvwm/cmdparser.h         |  65 ++++
 fvwm/cmdparser_hooks.h   |  74 ++++
 fvwm/cmdparser_old.c     | 424 +++++++++++++++++++++
 fvwm/cmdparser_old.h     |  11 +
 fvwm/colorset.c          |   2 +-
 fvwm/conditional.c       |  21 +-
 fvwm/conditional.h       |  42 +++
 fvwm/events.c            |  40 +-
 fvwm/ewmh.c              |   7 +-
 fvwm/ewmh_events.c       |  33 +-
 fvwm/expand.c            |  48 ++-
 fvwm/expand.h            |   2 +-
 fvwm/functable.c         |  66 +++-
 fvwm/functable.h         |  25 +-
 fvwm/functable_complex.c | 157 ++++++++
 fvwm/functable_complex.h |  55 +++
 fvwm/functions.c         | 771 ++++++++++++++-------------------------
 fvwm/functions.h         |  41 +--
 fvwm/fvwm.h              |  49 +--
 fvwm/fvwm3.c             |  12 +-
 fvwm/menucmd.c           |   3 +-
 fvwm/menus.c             |   4 +-
 fvwm/misc.c              |   1 +
 fvwm/module_interface.c  |   3 +-
 fvwm/move_resize.c       |  29 +-
 fvwm/placement.c         |   5 +-
 fvwm/read.c              |  14 +-
 fvwm/repeat.c            |   6 +-
 fvwm/schedule.c          |   3 +-
 fvwm/update.c            |   7 +-
 fvwm/virtual.c           |  24 +-
 fvwm/windowlist.c        |   3 +-
 35 files changed, 1373 insertions(+), 691 deletions(-)
 create mode 100644 fvwm/cmdparser.h
 create mode 100644 fvwm/cmdparser_hooks.h
 create mode 100644 fvwm/cmdparser_old.c
 create mode 100644 fvwm/cmdparser_old.h
 create mode 100644 fvwm/functable_complex.c
 create mode 100644 fvwm/functable_complex.h

diff --git a/fvwm/Makefile.am b/fvwm/Makefile.am
index a4bb6a0b..9ae456f9 100644
--- a/fvwm/Makefile.am
+++ b/fvwm/Makefile.am
@@ -19,6 +19,7 @@ fvwm3_SOURCES = \
 	placement.h read.h repeat.h execcontext.h schedule.h screen.h \
 	session.h stack.h style.h update.h virtual.h window_flags.h frame.h \
 	infostore.h \
+	cmdparser.h cmdparser_hooks.h cmdparser_old.h functable_complex.h \
 	\
 	menus.c style.c borders.c events.c move_resize.c builtins.c \
 	add_window.c icons.c fvwm3.c frame.c placement.c virtual.c \
@@ -28,7 +29,8 @@ fvwm3_SOURCES = \
 	menubindings.c decorations.c ewmh_icons.c update.c bindings.c misc.c \
 	cursor.c colormaps.c modconf.c  ewmh_conf.c read.c schedule.c \
 	menucmd.c ewmh_names.c icccm2.c windowshade.c focus_policy.c repeat.c \
-	execcontext.c menugeometry.c menudim.c condrc.c infostore.c
+	execcontext.c menugeometry.c menudim.c condrc.c infostore.c \
+	cmdparser_old.c functable_complex.c

 fvwm3_DEPENDENCIES = $(top_builddir)/libs/libfvwm3.a

diff --git a/fvwm/add_window.c b/fvwm/add_window.c
index dbd062e2..95a18600 100644
--- a/fvwm/add_window.c
+++ b/fvwm/add_window.c
@@ -2495,7 +2495,7 @@ FvwmWindow *AddWindow(
 		char *cmd;

 		xasprintf(&cmd, "GotoDesk %s 0 %d", tm->si->name, fw->Desk);
-		execute_function_override_window(NULL, NULL, cmd, 0, fw);
+		execute_function_override_window(NULL, NULL, cmd, NULL, 0, fw);
 		free(cmd);

 		/* read the requested absolute geometry */
@@ -2695,7 +2695,8 @@ FvwmWindow *AddWindow(
 			SET_STICKY_ACROSS_PAGES(fw, 0);
 			SET_STICKY_ACROSS_DESKS(fw, 0);
 			handle_stick(
-				NULL, exc2, "", stick_page, stick_desk, 1, 0);
+				NULL, exc2, "", NULL, stick_page, stick_desk,
+				1, 0);
 			exc_destroy_context(exc2);
 		}
 	}
@@ -2727,7 +2728,7 @@ FvwmWindow *AddWindow(
 		ecc.w.wcontext = C_WINDOW;
 		exc2 = exc_clone_context(
 			exc, &ecc, ECC_ETRIGGER | ECC_FW | ECC_WCONTEXT);
-		CMD_Resize(NULL, exc2, "");
+		CMD_Resize(NULL, exc2, "", NULL);
 		exc_destroy_context(exc2);
 	}

@@ -2796,7 +2797,7 @@ FvwmWindow *AddWindow(
 			     EWMH_STATE_HAS_HINT) ? 100 : 0;
 			sprintf(cmd,"Maximize on %i %i", h, v);
 			execute_function_override_window(
-				NULL, NULL, cmd, 0, fw);
+				NULL, NULL, cmd, NULL, 0, fw);
 		}
 	}
 	if (HAS_EWMH_INIT_FULLSCREEN_STATE(fw) == EWMH_STATE_HAS_HINT)
diff --git a/fvwm/builtins.c b/fvwm/builtins.c
index 4065051d..5c6e4eba 100644
--- a/fvwm/builtins.c
+++ b/fvwm/builtins.c
@@ -544,7 +544,7 @@ static void __remove_window_decors(F_CMD_ARGS, FvwmDecor *d)
 			exc2 = exc_clone_context(
 				exc, &ecc, ECC_FW | ECC_WCONTEXT);
 			execute_function(
-				cond_rc, exc2, "ChangeDecor Default", 0);
+				cond_rc, exc2, "ChangeDecor Default", pc, 0);
 			exc_destroy_context(exc2);
 		}
 	}
@@ -2166,7 +2166,7 @@ void AddToDecor(F_CMD_ARGS, FvwmDecor *decor)
 		return;
 	}
 	Scr.cur_decor = decor;
-	execute_function(cond_rc, exc, action, 0);
+	execute_function(F_PASS_ARGS, 0);
 	Scr.cur_decor = NULL;

 	return;
diff --git a/fvwm/cmdparser.h b/fvwm/cmdparser.h
new file mode 100644
index 00000000..67e6ae36
--- /dev/null
+++ b/fvwm/cmdparser.h
@@ -0,0 +1,65 @@
+/* -*-c-*- */
+
+#ifndef CMDPARSER_H
+#define CMDPARSER_H
+
+/* ---------------------------- included header files ---------------------- */
+
+/* ---------------------------- global definitions ------------------------- */
+
+/* $0 through to $9 */
+#define CMDPARSER_NUM_POS_ARGS 10
+
+/* ---------------------------- global macros ------------------------------ */
+
+/* ---------------------------- type definitions --------------------------- */
+
+/* Enum for all the possible prefixes. */
+typedef enum
+{
+	CP_PREFIX_NONE = 0,
+	CP_PREFIX_MINUS = 0x1,
+	CP_PREFIX_SILENT = 0x2,
+	CP_PREFIX_KEEPRC = 0x4
+} cmdparser_prefix_flags_t;
+
+/* Enum for types of things to execute. */
+typedef enum
+{
+	CP_EXECTYPE_UNKNOWN = 0,
+	CP_EXECTYPE_BUILTIN_FUNCTION,
+	CP_EXECTYPE_COMPLEX_FUNCTION,
+	CP_EXECTYPE_COMPLEX_FUNCTION_DOES_NOT_EXIST,
+	CP_EXECTYPE_MODULECONFIG
+} cmdparser_execute_type_t;
+
+/* move this to the implementation file and use void* instead??? */
+
+typedef struct
+{
+	/* !!!note: need to define which bits in here may be accessed by the
+	 * user and which are internal */
+	unsigned char is_created : 1;
+	/* the original command line */
+	char *line;
+	/* the current command line */
+	char *cline;
+	/* the expanded command line */
+	char *expline;
+	unsigned char do_free_expline : 1;
+	/* the command name */
+	char *command;
+	/* name of the complex function if present */
+	char *complex_function_name;
+	/* current function nesting depth */
+	int call_depth;
+	/* A string with all positional arguments of a complex function.  May
+	 * be NULL if not in the context of a complex function. */
+	char *all_pos_args_string;
+	/* An array of the first CMDPARSER_NUM_POS_ARGS positional arguments up
+	 * to and excluding the first NULL pointer. Must (not) all be NULL if
+	 * all_pos_args_string is (not) NULL. */
+	char *pos_arg_tokens[CMDPARSER_NUM_POS_ARGS];
+} cmdparser_context_t;
+
+#endif /* CMDPARSER_H */
diff --git a/fvwm/cmdparser_hooks.h b/fvwm/cmdparser_hooks.h
new file mode 100644
index 00000000..f05c98d3
--- /dev/null
+++ b/fvwm/cmdparser_hooks.h
@@ -0,0 +1,74 @@
+/* -*-c-*- */
+
+#ifndef CMDPARSER_HOOKS_H
+#define CMDPARSER_HOOKS_H
+
+/* ---------------------------- included header files ---------------------- */
+
+/* ---------------------------- global definitions ------------------------- */
+
+/* ---------------------------- global macros ------------------------------ */
+
+/* ---------------------------- type definitions --------------------------- */
+
+typedef struct
+{
+	/* in general, functions in this structure that return int return
+	 *    0: success
+	 *  > 0: unsuccessful but not an error condition
+	 *  < 0: unsuccessful, error
+	 */
+	int (*create_context)(
+		/* context structure to initialise */
+		cmdparser_context_t *dest_context,
+		/* context structure of the caller or NULL */
+		cmdparser_context_t *caller_context,
+		/* input command line */
+		char *line,
+		/* A string with all positional arguments of a complex
+		 * function.  May be NULL if not in the context of a complex
+		 * function. */
+		char *all_pos_args_string,
+		/* An array of the first CMDPARSER_NUM_POS_ARGS positional
+		 * arguments up to and excluding the first NULL pointer. Must
+		 * (not) all be NULL if all_pos_args_string is (not) NULL. */
+		char *pos_arg_tokens[]
+		);
+	int (*handle_line_start)(cmdparser_context_t *context);
+	/* Returns a set of or'ed flags of which prefixes are present on the
+	 * command line.  The prefixes are stripped.  */
+	cmdparser_prefix_flags_t (*handle_line_prefix)(
+		cmdparser_context_t *context);
+	/* parses and returns the command name or returns a NULL pointer if not
+	 * possible */
+	const char *(*parse_command_name)(
+		cmdparser_context_t *context, void *func_rc, const void *exc);
+	/* returns 1 if the stored command is a module configuration command
+	 * and 0 otherwise */
+	int (*is_module_config)(cmdparser_context_t *context);
+	/* expandeds the command line */
+	void (*expand_command_line)(
+		cmdparser_context_t *context, int is_addto, void *func_rc,
+		const void *exc);
+	/* Release the expline field from the context structure and return it.
+	 * It is then the responsibility of the caller to free() it. */
+	void (*release_expanded_line)(cmdparser_context_t *context);
+	/* Tries to find a builtin function, a complex function or a module
+	 * config line to execute and returns the type found or
+	 * CP_EXECTYPE_UNKNOWN if none of the above was identified.  For a
+	 * builtin or a complex funtion the pointer to the description is
+	 * returned in *ret_builtin or *ret_complex_function.  Consumes the
+	 * the "Module" or the "Function" command form the input.  Dos not
+	 * consider builtin functions if *ret_builtin is != NULL when the
+	 * function is called.  */
+	cmdparser_execute_type_t (*find_something_to_execute)(
+		cmdparser_context_t *context,
+		const func_t **ret_builtin,
+		FvwmFunction **ret_complex_function);
+	/* Release the context initialised with create_context(). */
+	void (*destroy_context)(cmdparser_context_t *context);
+	/* Print the contents of the context. */
+	void (*debug)(cmdparser_context_t *context, const char *msg);
+} cmdparser_hooks_t;
+
+#endif /* CMDPARSER_HOOKS_H */
diff --git a/fvwm/cmdparser_old.c b/fvwm/cmdparser_old.c
new file mode 100644
index 00000000..11636030
--- /dev/null
+++ b/fvwm/cmdparser_old.c
@@ -0,0 +1,424 @@
+/* -*-c-*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* ---------------------------- included header files ---------------------- */
+
+#include "config.h"
+#include "libs/defaults.h"
+#include "libs/Parse.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "fvwm.h"
+#include "execcontext.h"
+#include "conditional.h"
+#include "functable.h"
+#include "commands.h"
+#include "functable_complex.h"
+#include "cmdparser.h"
+#include "cmdparser_hooks.h"
+#include "cmdparser_old.h"
+#include "misc.h"
+#include "expand.h"
+
+/* ---------------------------- local definitions -------------------------- */
+
+#if 0 /*!!!*/
+#define OCP_DEBUG 1
+#else
+#define OCP_DEBUG 0
+#endif
+
+/* ---------------------------- local macros ------------------------------- */
+
+/* ---------------------------- imports ------------------------------------ */
+
+/* ---------------------------- included code files ------------------------ */
+
+/* ---------------------------- local types -------------------------------- */
+
+/* ---------------------------- forward declarations ----------------------- */
+
+/* ---------------------------- local variables ---------------------------- */
+
+/* ---------------------------- exported variables (globals) --------------- */
+
+/* ---------------------------- local functions ---------------------------- */
+
+static void ocp_debug(cmdparser_context_t *c, const char *msg)
+{
+	int i;
+
+	if (!OCP_DEBUG)
+	{
+		return;
+	}
+	fprintf(stderr, "%s: %p: %s\n", __func__, c, (msg != NULL) ? msg : "");
+	if (!c->is_created)
+	{
+		fprintf(stderr, "  not created\n");
+		return;
+	}
+	fprintf(stderr, "  depth    : %d\n", c->call_depth);
+	fprintf(stderr, "  orig line: '%s'\n", c->line ? c->line : "(nil)");
+	fprintf(stderr, "  curr line: '%s'\n", c->cline ? c->cline : "(nil)");
+	fprintf(
+		stderr, "  exp  line: (do_free = %d) '%s'\n",
+		c->do_free_expline, c->expline ? c->expline : "(nil)");
+	fprintf(
+		stderr, "  command  : '%s'\n",
+		c->command ? c->command : "(nil)");
+	fprintf(
+		stderr, "  complexf : '%s'\n",
+		c->complex_function_name ? c->complex_function_name : "(nil)");
+	if (c->all_pos_args_string == NULL)
+	{
+		return;
+	}
+	fprintf(
+		stderr, "  all args : '%s'\n",
+		c->all_pos_args_string ? c->all_pos_args_string: "(nil)");
+	fprintf(stderr, "  pos args :");
+	for (
+		i = 0; i < CMDPARSER_NUM_POS_ARGS &&
+			c->pos_arg_tokens[i] != NULL; i++)
+	{
+		fprintf(stderr, " %d:'%s'", i,
+			c->pos_arg_tokens[i] ? c->pos_arg_tokens[i]: "(nil)");
+	}
+	fprintf(stderr, "\n");
+
+	return;
+}
+
+static int ocp_create_context(
+	cmdparser_context_t *dest_c, cmdparser_context_t *caller_c, char *line,
+	char *all_pos_args_string, char *pos_arg_tokens[])
+{
+	/* allocate the necessary resources */
+	memset(dest_c, 0, sizeof(*dest_c));
+	dest_c->line = line;
+	/*!!!should this be in handle_line_start instead???*/
+	if (caller_c != NULL)
+	{
+		if (dest_c->call_depth > MAX_FUNCTION_DEPTH)
+		{
+			fvwm_debug(
+				__func__,
+				"called with a depth of %i, "
+				"stopping function execution!",
+				dest_c->call_depth);
+
+			return -1;
+		}
+	}
+	else
+	{
+		dest_c->call_depth = 1;
+	}
+	if (all_pos_args_string != NULL)
+	{
+		int i;
+
+		dest_c->all_pos_args_string = all_pos_args_string;
+		for (
+			i = 0; i < CMDPARSER_NUM_POS_ARGS &&
+				pos_arg_tokens[i] != NULL; i++)
+		{
+			dest_c->pos_arg_tokens[i] = pos_arg_tokens[i];
+		}
+	}
+	/*!!!allocate stuff*/
+	dest_c->is_created = 1;
+
+	return 0;
+}
+
+static void ocp_destroy_context(cmdparser_context_t *c)
+{
+	/* free the resources allocated in the create function */
+	if (!c->is_created)
+	{
+		return;
+	}
+	if (c->command != NULL)
+	{
+		free(c->command);
+	}
+	if (c->expline != NULL && c->do_free_expline)
+	{
+		free(c->expline);
+	}
+	c->is_created = 0;
+
+	return;
+}
+
+static int ocp_handle_line_start(cmdparser_context_t *c)
+{
+	/*!!! remove assertion later*/
+	assert(c->cline == NULL);
+	if (!c->line)
+	{
+		/* nothing to do */
+		return 1;
+	}
+	/* ignore whitespace at the beginning of all config lines */
+	c->cline = SkipSpaces(c->line, NULL, 0);
+	if (!c->cline || c->cline[0] == 0)
+	{
+		/* impossibly short command */
+		return -1;
+	}
+	if (c->cline[0] == '#')
+	{
+		/* a comment */
+		return 1;
+	}
+
+	return 0;
+}
+
+static cmdparser_prefix_flags_t ocp_handle_line_prefix(cmdparser_context_t *c)
+{
+	cmdparser_prefix_flags_t flags;
+	char *token;
+	char *restofline;
+
+	if (OCP_DEBUG)
+	{
+		fprintf(stderr, "%s: '%s'\n", __func__, c->cline);
+	}
+	flags = CP_PREFIX_NONE;
+	if (c->cline[0] == '-')
+	{
+		c->cline++;
+		flags |= CP_PREFIX_MINUS;
+		if (OCP_DEBUG)
+		{
+			fprintf(stderr, "minus -> '%s'\n", c->cline);
+		}
+	}
+	token = PeekToken(c->cline, &restofline);
+	if (OCP_DEBUG)
+	{
+		fprintf(
+			stderr, "cl '%s', tok '%s', rl '%s'\n", c->cline,
+			token, restofline);
+	}
+	while (token != NULL)
+	{
+		if (OCP_DEBUG)
+		{
+			fprintf(stderr, "loop: token '%s'\n", token);
+		}
+		if (StrEquals(token, PRE_SILENT))
+		{
+			flags |= CP_PREFIX_SILENT;
+			c->cline = restofline;
+			token = PeekToken(c->cline, &restofline);
+			if (OCP_DEBUG)
+			{
+				fprintf(
+					stderr,
+					"-> silent: cl '%s', tok '%s'\n",
+					c->cline, token);
+			}
+		}
+		else if (StrEquals(token, PRE_KEEPRC))
+		{
+			flags |= CP_PREFIX_KEEPRC;
+			c->cline = restofline;
+			token = PeekToken(c->cline, &restofline);
+			if (OCP_DEBUG)
+			{
+				fprintf(
+					stderr,
+					"-> keeprc: cl '%s', tok '%s'\n",
+					c->cline, token);
+			}
+		}
+		else
+		{
+			break;
+		}
+	}
+	if (OCP_DEBUG)
+	{
+		fprintf(stderr, "done: flags 0x%x\n", flags);
+	}
+
+	return flags;
+}
+
+static int ocp_is_module_config(cmdparser_context_t *c)
+{
+	return (c->command != NULL && *c->command == '*') ? 1 : 0;
+}
+
+static const char *ocp_parse_command_name(
+	cmdparser_context_t *c, void *func_rc, const void *exc)
+{
+	GetNextToken(c->cline, &c->command);
+	if (c->command != NULL)
+	{
+		char *tmp = c->command;
+
+		c->command = expand_vars(
+			c->command, c, False, False, func_rc, exc);
+		free(tmp);
+	}
+	if (c->command && !ocp_is_module_config(c))
+	{
+#if 1
+		/* DV: with this piece of code it is impossible to have a
+		 * complex function with embedded whitespace that begins with a
+		 * builtin function name, e.g. a function "echo hello". */
+		/* DV: ... and without it some of the complex functions will
+		 * fail */
+		char *tmp = c->command;
+
+		while (*tmp && !isspace(*tmp))
+		{
+			tmp++;
+		}
+		*tmp = 0;
+#endif
+		return c->command;
+	}
+	else
+	{
+		return NULL;
+	}
+}
+
+static void ocp_expand_command_line(
+	cmdparser_context_t *c, int is_addto, void *func_rc, const void *exc)
+{
+	c->expline = expand_vars(
+		c->cline, c, is_addto, ocp_is_module_config(c), func_rc, exc);
+	c->cline = c->expline;
+	c->do_free_expline = 1;
+
+	return;
+}
+
+static void ocp_release_expanded_line(cmdparser_context_t *c)
+{
+	c->do_free_expline = 0;
+
+	return;
+}
+
+static cmdparser_execute_type_t ocp_find_something_to_execute(
+	cmdparser_context_t *c, const func_t **ret_builtin,
+	FvwmFunction **ret_complex_function)
+{
+	int is_function_builtin;
+
+	*ret_complex_function = NULL;
+	/* Note: the module config command, "*" can not be handled by the
+	 * regular command table because there is no required white space after
+	 * the asterisk. */
+	if (ocp_is_module_config(c))
+	{
+		/*!!!strip asterisk??? */
+		return CP_EXECTYPE_MODULECONFIG;
+	}
+	if (*ret_builtin == NULL)
+	{
+		/* in a new parser we would look for a builtin function here,
+		 * but in the old parser this has already been done */
+	}
+	is_function_builtin = 0;
+	if (*ret_builtin != NULL)
+	{
+		if ((*ret_builtin)->func_c == F_FUNCTION)
+		{
+			c->cline = SkipNTokens(c->cline, 1);
+			if (OCP_DEBUG)
+			{
+				fprintf(stderr, "func cline '%s'\n", c->cline);
+			}
+			free(c->command);
+			c->command = NULL;
+			is_function_builtin = 1;
+			*ret_builtin = NULL;
+		}
+		else
+		{
+			c->cline = SkipNTokens(c->cline, 1);
+			return CP_EXECTYPE_BUILTIN_FUNCTION;
+		}
+	}
+#if 1 /*!!!*/
+	assert(*ret_builtin == NULL);
+#endif
+	do
+	{
+		char *complex_function_name;
+		char *rest_of_line;
+
+		/* find_complex_function expects a token, not just a quoted
+		 * string */
+		complex_function_name = PeekToken(c->cline, &rest_of_line);
+		if (complex_function_name == NULL)
+		{
+			break;
+		}
+		*ret_complex_function =
+			find_complex_function(complex_function_name);
+		if (*ret_complex_function != NULL)
+		{
+			c->cline = rest_of_line;
+			c->complex_function_name = complex_function_name;
+			return CP_EXECTYPE_COMPLEX_FUNCTION;
+		}
+		if (is_function_builtin)
+		{
+			c->cline = rest_of_line;
+			c->complex_function_name = complex_function_name;
+			return CP_EXECTYPE_COMPLEX_FUNCTION_DOES_NOT_EXIST;
+		}
+	} while (0);
+
+	return CP_EXECTYPE_UNKNOWN;
+}
+
+/* ---------------------------- local variables ---------------------------- */
+
+static cmdparser_hooks_t old_parser_hooks =
+{
+	ocp_create_context,
+	ocp_handle_line_start,
+	ocp_handle_line_prefix,
+	ocp_parse_command_name,
+	ocp_is_module_config,
+	ocp_expand_command_line,
+	ocp_release_expanded_line,
+	ocp_find_something_to_execute,
+	ocp_destroy_context,
+	ocp_debug
+};
+
+/* ---------------------------- interface functions ------------------------ */
+
+/* return the hooks structure of the old parser */
+const cmdparser_hooks_t *cmdparser_old_get_hooks(void)
+{
+	return &old_parser_hooks;
+}
diff --git a/fvwm/cmdparser_old.h b/fvwm/cmdparser_old.h
new file mode 100644
index 00000000..4ce61670
--- /dev/null
+++ b/fvwm/cmdparser_old.h
@@ -0,0 +1,11 @@
+/* -*-c-*- */
+
+#ifndef CMDPARSER_OLD_H
+#define CMDPARSER_OLD_H
+
+/* ---------------------------- interface functions ------------------------ */
+
+/* return the hooks structure of the old parser */
+const cmdparser_hooks_t *cmdparser_old_get_hooks(void);
+
+#endif /* CMDPARSER_OLD_H */
diff --git a/fvwm/colorset.c b/fvwm/colorset.c
index 48f59c8c..5aa9bc8d 100644
--- a/fvwm/colorset.c
+++ b/fvwm/colorset.c
@@ -250,7 +250,7 @@ static void add_to_junk(Pixmap pixmap)
 		const exec_context_t *exc;

 		exc = exc_create_null_context();
-		CMD_Schedule(NULL, exc, "3000 CleanupColorsets");
+		CMD_Schedule(NULL, exc, "3000 CleanupColorsets", NULL);
 		exc_destroy_context(exc);
 		cleanup_scheduled = True;
 	}
diff --git a/fvwm/conditional.c b/fvwm/conditional.c
index e64cabf6..30bf14a3 100644
--- a/fvwm/conditional.c
+++ b/fvwm/conditional.c
@@ -28,6 +28,7 @@
 #include "libs/wcontext.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "execcontext.h"
 #include "functions.h"
 #include "conditional.h"
@@ -201,7 +202,7 @@ static void circulate_cmd(
 		ecc.w.wcontext = new_context;
 		exc2 = exc_clone_context(
 			exc, &ecc, ECC_FW | ECC_W | ECC_WCONTEXT);
-		execute_function(cond_rc, exc2, restofline, flags);
+		execute_function(cond_rc, exc2, restofline, pc, flags);
 		exc_destroy_context(exc2);
 	}

@@ -240,7 +241,7 @@ static void select_cmd(F_CMD_ARGS)
 			cond_rc->rc = COND_RC_OK;
 		}
 		execute_function_override_wcontext(
-			cond_rc, exc, restofline, 0, C_WINDOW);
+			cond_rc, exc, restofline, pc, 0, C_WINDOW);
 	}
 	else if (cond_rc != NULL)
 	{
@@ -1423,7 +1424,7 @@ static void direction_cmd(F_CMD_ARGS, Bool is_scan)
 			cond_rc->rc = COND_RC_OK;
 		}
 		execute_function_override_window(
-			cond_rc, exc, restofline, 0, fw_best);
+			cond_rc, exc, restofline, pc, 0, fw_best);
 	}
 	else if (cond_rc != NULL)
 	{
@@ -1664,7 +1665,7 @@ void CMD_All(F_CMD_ARGS)
 		for (i = num-1; i >= 0; i--)
 		{
 			execute_function_override_window(
-				cond_rc, exc, restofline, 0, g[i]);
+				cond_rc, exc, restofline, pc, 0, g[i]);
 		}
 	}
 	else
@@ -1672,7 +1673,7 @@ void CMD_All(F_CMD_ARGS)
 		for (i = 0; i < num; i++)
 		{
 			execute_function_override_window(
-				cond_rc, exc, restofline, 0, g[i]);
+				cond_rc, exc, restofline, pc, 0, g[i]);
 		}
 	}
 	if (cond_rc != NULL && cond_rc->rc != COND_RC_BREAK)
@@ -1792,7 +1793,7 @@ void CMD_WindowId(F_CMD_ARGS)
 					cond_rc->rc = COND_RC_OK;
 				}
 				execute_function_override_window(
-					cond_rc, exc, action, 0, t);
+					cond_rc, exc, action, pc, 0, t);
 			}
 			else if (cond_rc != NULL)
 			{
@@ -1836,7 +1837,7 @@ void CMD_WindowId(F_CMD_ARGS)
 					ECC_FW | ECC_W | ECC_WCONTEXT);
 				execute_function(
 					cond_rc, exc2, action,
-					FUNC_IS_UNMANAGED);
+pc, 					FUNC_IS_UNMANAGED);
 				exc_destroy_context(exc2);
 			}
 		}
@@ -1873,7 +1874,7 @@ void CMD_TestRc(F_CMD_ARGS)
 	{
 		/* execute the command in root window context; overwrite the
 		 * return code with the return code of the command */
-		execute_function(cond_rc, exc, rest, 0);
+		execute_function(cond_rc, exc, rest, pc, 0);
 	}

 	return;
@@ -1899,7 +1900,7 @@ void CMD_Break(F_CMD_ARGS)

 void CMD_NoWindow(F_CMD_ARGS)
 {
-	execute_function_override_window(cond_rc, exc, action, 0, NULL);
+	execute_function_override_window(cond_rc, exc, action, pc, 0, NULL);

 	return;
 }
@@ -2321,7 +2322,7 @@ void CMD_Test(F_CMD_ARGS)
 	}
 	if (!error && match)
 	{
-		execute_function(cond_rc, exc, restofline, 0);
+		execute_function(cond_rc, exc, restofline, pc, 0);
 	}
 	if (cond_rc != NULL)
 	{
diff --git a/fvwm/conditional.h b/fvwm/conditional.h
index c993ae8b..c58f2030 100644
--- a/fvwm/conditional.h
+++ b/fvwm/conditional.h
@@ -12,6 +12,48 @@

 /* ---------------------------- type definitions --------------------------- */

+/* Window mask for Circulate and Direction functions */
+typedef struct WindowConditionMask
+{
+	struct
+	{
+		unsigned do_accept_focus : 1;
+		unsigned do_check_desk : 1;
+		unsigned do_check_screen : 1;
+		unsigned do_check_cond_desk : 1;
+		unsigned do_check_desk_and_global_page : 1;
+		unsigned do_check_desk_and_page : 1;
+		unsigned do_check_global_page : 1;
+		unsigned do_check_overlapped : 1;
+		unsigned do_check_page : 1;
+		unsigned do_not_check_screen : 1;
+		unsigned needs_current_desk : 1;
+		unsigned needs_current_desk_and_global_page : 1;
+		unsigned needs_current_desk_and_page : 1;
+		unsigned needs_current_global_page : 1;
+		unsigned needs_current_page : 1;
+#define NEEDS_ANY   0
+#define NEEDS_TRUE  1
+#define NEEDS_FALSE 2
+		unsigned needs_focus : 2;
+		unsigned needs_overlapped : 2;
+		unsigned needs_pointer : 2;
+		unsigned needs_same_layer : 1;
+		unsigned use_circulate_hit : 1;
+		unsigned use_circulate_hit_icon : 1;
+		unsigned use_circulate_hit_shaded : 1;
+		unsigned use_do_accept_focus : 1;
+	} my_flags;
+	window_flags flags;
+	window_flags flag_mask;
+	struct name_condition *name_condition;
+	int layer;
+	int desk;
+	struct monitor *screen;
+	int placed_by_button_mask;
+	int placed_by_button_set_mask;
+} WindowConditionMask;
+
 /* ---------------------------- forward declarations ----------------------- */

 /* ---------------------------- exported variables (globals) --------------- */
diff --git a/fvwm/events.c b/fvwm/events.c
index 01102968..b490b404 100644
--- a/fvwm/events.c
+++ b/fvwm/events.c
@@ -70,6 +70,7 @@
 #include "libs/FEvent.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "cursor.h"
 #include "functions.h"
 #include "commands.h"
@@ -1622,7 +1623,7 @@ static Bool __handle_bpress_action(
 		/* release the pointer since it can't do harm over an icon */
 		XAllowEvents(dpy, AsyncPointer, CurrentTime);
 	}
-	execute_function(NULL, exc, action, 0);
+	execute_function(NULL, exc, action, NULL, 0);
 	if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
 	{
 		WaitForButtonsUp(True);
@@ -1660,7 +1661,7 @@ static void __handle_bpress_on_root(const exec_context_t *exc)

 		ecc.w.wcontext = C_ROOT;
 		exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
-		execute_function(NULL, exc2, action, 0);
+		execute_function(NULL, exc2, action, NULL, 0);
 		exc_destroy_context(exc2);
 		WaitForButtonsUp(True);
 	}
@@ -1824,27 +1825,30 @@ monitor_emit_broadcast(void)

 	TAILQ_FOREACH (m, &monitor_q, entry) {
 		if (m->emit & MONITOR_CHANGED) {
-			BroadcastName(MX_MONITOR_CHANGED, -1, -1, -1, m->si->name);
+			BroadcastName(
+				MX_MONITOR_CHANGED, -1, -1, -1, m->si->name);
 			m->emit &= ~MONITOR_ALL;
 			m->flags &= ~MONITOR_CHANGED;

 			/* Run the RandRFunc in case a user has set it. */
-			execute_function_override_window(NULL, NULL, randrfunc,
-			    0, NULL);
+			execute_function_override_window(
+				NULL, NULL, randrfunc, NULL, 0, NULL);
 		}
 		if (m->emit & MONITOR_ENABLED) {
-			BroadcastName(MX_MONITOR_ENABLED, -1, -1, -1, m->si->name);
+			BroadcastName(
+				MX_MONITOR_ENABLED, -1, -1, -1, m->si->name);

 			/* Run the RandRFunc in case a user has set it. */
-			execute_function_override_window(NULL, NULL, randrfunc,
-			    0, NULL);
+			execute_function_override_window(
+				NULL, NULL, randrfunc, NULL, 0, NULL);
 		}
 		if (m->emit & MONITOR_DISABLED) {
-			BroadcastName(MX_MONITOR_DISABLED, -1, -1, -1, m->si->name);
+			BroadcastName(
+				MX_MONITOR_DISABLED, -1, -1, -1, m->si->name);

 			/* Run the RandRFunc in case a user has set it. */
-			execute_function_override_window(NULL, NULL, randrfunc,
-			    0, NULL);
+			execute_function_override_window(
+				NULL, NULL, randrfunc, NULL, 0, NULL);
 		}
 	}
 }
@@ -1895,7 +1899,7 @@ void HandleClientMessage(const evh_args_t *ea)

 		ecc.w.wcontext = C_WINDOW;
 		exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
-		execute_function(NULL, exc, "Iconify", 0);
+		execute_function(NULL, exc, "Iconify", NULL, 0);
 		exc_destroy_context(exc);
 		return;
 	}
@@ -2260,7 +2264,7 @@ void HandleEnterNotify(const evh_args_t *ea)
 		else if (edge_command)
 		{
 			fvwm_debug(__func__, "EC is: %s", edge_command);
-			execute_function(NULL, ea->exc, edge_command, 0);
+			execute_function(NULL, ea->exc, edge_command, NULL, 0);
 		}
 		else
 		{
@@ -2677,7 +2681,7 @@ void __handle_key(const evh_args_t *ea, Bool is_press)
 			exc = exc_clone_context(
 				ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
 		}
-		execute_function(NULL, exc, action, 0);
+		execute_function(NULL, exc, action, NULL, 0);
 		if (is_second_binding == False)
 		{
 			exc_destroy_context(exc);
@@ -2821,7 +2825,8 @@ void HandleLeaveNotify(const evh_args_t *ea)
 		}
 		else if (edge_command_leave)
 		{
-			execute_function(NULL, ea->exc, edge_command_leave, 0);
+			execute_function(
+				NULL, ea->exc, edge_command_leave, NULL, 0);
 		}
 	}

@@ -3192,7 +3197,8 @@ void HandleMapRequestKeepRaised(
 			{
 				execute_function_override_window(
 					NULL, ea->exc,
-					(char *)initial_map_command, 0, fw);
+					(char *)initial_map_command, NULL, 0,
+					fw);
 			}
 			MyXUngrabServer(dpy);

@@ -3635,7 +3641,7 @@ void HandlePropertyNotify(const evh_args_t *ea)
 			ecc.w.wcontext = C_WINDOW;
 			exc = exc_clone_context(
 				ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
-			execute_function(NULL, exc, urgency_action, 0);
+			execute_function(NULL, exc, urgency_action, NULL, 0);
 			exc_destroy_context(exc);
 		}
 		break;
diff --git a/fvwm/ewmh.c b/fvwm/ewmh.c
index e4e84800..3983ded4 100644
--- a/fvwm/ewmh.c
+++ b/fvwm/ewmh.c
@@ -58,6 +58,7 @@
 #include "libs/fvwm_x11.h"
 #include "libs/fvwmlib.h"
 #include "fvwm.h"
+#include "cmdparser.h"
 #include "execcontext.h"
 #include "functions.h"
 #include "commands.h"
@@ -2036,7 +2037,7 @@ void EWMH_fullscreen(FvwmWindow *fw)
 	{
 		fw->fullscreen.is_iconified = 1;
 		execute_function_override_window(
-			NULL, NULL, "Iconify off", 0, fw);
+			NULL, NULL, "Iconify off", NULL, 0, fw);
 	}
 	if (IS_SHADED(fw))
 	{
@@ -2045,7 +2046,7 @@ void EWMH_fullscreen(FvwmWindow *fw)
 		fw->fullscreen.is_shaded = 1;
 		fw->shade_anim_steps = 0;
 		execute_function_override_window(
-			NULL, NULL, "WindowShade off", 0, fw);
+			NULL, NULL, "WindowShade off", NULL, 0, fw);
 		fw->shade_anim_steps = sas;
 	}
 	SET_EWMH_FULLSCREEN(fw,True);
@@ -2079,7 +2080,7 @@ void EWMH_fullscreen(FvwmWindow *fw)
 	if (cmd[0] != 0)
 	{
 		SET_DISABLE_CONSTRAIN_SIZE_FULLSCREEN(fw, 1);
-		execute_function_override_window(NULL, NULL, cmd, 0, fw);
+		execute_function_override_window(NULL, NULL, cmd, NULL, 0, fw);
 		SET_DISABLE_CONSTRAIN_SIZE_FULLSCREEN(fw, 0);
 	}

diff --git a/fvwm/ewmh_events.c b/fvwm/ewmh_events.c
index 65b730c2..5989f40d 100644
--- a/fvwm/ewmh_events.c
+++ b/fvwm/ewmh_events.c
@@ -24,6 +24,7 @@
 #include "fvwm.h"
 #include "externs.h"
 #include "execcontext.h"
+#include "cmdparser.h"
 #include "functions.h"
 #include "misc.h"
 #include "screen.h"
@@ -92,7 +93,7 @@ int ewmh_DesktopGeometry(
 		return -1;
 	}
 	sprintf(action, "DesktopSize %ld %ld", width, height);
-	execute_function_override_window(NULL, NULL, action, 0, NULL);
+	execute_function_override_window(NULL, NULL, action, NULL, 0, NULL);

 	return -1;
 }
@@ -169,7 +170,7 @@ int ewmh_ActiveWindow(
 		return 0;
 	}
 	execute_function_override_window(
-		NULL, NULL, "EWMHActivateWindowFunc", 0, fw);
+		NULL, NULL, "EWMHActivateWindowFunc", NULL, 0, fw);

 	return 0;
 }
@@ -185,7 +186,7 @@ int ewmh_CloseWindow(
 	{
 		return 0;
 	}
-	execute_function_override_window(NULL, NULL, "Close", 0, fw);
+	execute_function_override_window(NULL, NULL, "Close", NULL, 0, fw);

 	return 0;
 }
@@ -290,7 +291,7 @@ int ewmh_WMDesktop(
 		if (d == (unsigned long)-2 || d == (unsigned long)-1)
 		{
 			execute_function_override_window(
-				NULL, NULL, "Stick on", 0, fw);
+				NULL, NULL, "Stick on", NULL, 0, fw);
 		}
 		else if (d >= 0)
 		{
@@ -298,7 +299,7 @@ int ewmh_WMDesktop(
 			    IS_STICKY_ACROSS_DESKS(fw))
 			{
 				execute_function_override_window(
-					NULL, NULL, "Stick off", 0, fw);
+					NULL, NULL, "Stick off", NULL, 0, fw);
 			}
 			if (fw->Desk != d)
 			{
@@ -449,18 +450,18 @@ int ewmh_MoveResize(
 	if (!move)
 	{
 		sprintf(cmd, "WarpToWindow %i %i",x_warp,y_warp);
-		execute_function_override_window(NULL, NULL, cmd, 0, fw);
+		execute_function_override_window(NULL, NULL, cmd, NULL, 0, fw);
 	}

 	if (move)
 	{
 		execute_function_override_window(
-			NULL, NULL, "Move", 0, fw);
+			NULL, NULL, "Move", NULL, 0, fw);
 	}
 	else
 	{
 		execute_function_override_window(
-			NULL, NULL, "Resize", 0, fw);
+			NULL, NULL, "Resize", NULL, 0, fw);
 	}

 	return 0;
@@ -558,7 +559,7 @@ int ewmh_WMState(
 			}
 			sprintf(cmd,"Maximize on %i %i", max_horiz, max_vert);
 		}
-		execute_function_override_window(NULL, NULL, cmd, 0, fw);
+		execute_function_override_window(NULL, NULL, cmd, NULL, 0, fw);
 	}
 	return 0;
 }
@@ -628,7 +629,7 @@ int ewmh_WMStateFullScreen(
 			/* unmaximize will restore is_ewmh_fullscreen,
 			 * layer and apply_decor_change */
 			execute_function_override_window(
-				NULL, NULL, "Maximize off", 0, fw);
+				NULL, NULL, "Maximize off", NULL, 0, fw);
 		}
 		if ((IS_EWMH_FULLSCREEN(fw) &&
 		     !DO_EWMH_USE_STACKING_HINTS(fw)) ||
@@ -638,7 +639,7 @@ int ewmh_WMStateFullScreen(
 			/* On: if not raised by a layer cmd raise
 			 * Off: if lowered by a layer cmd raise */
 			execute_function_override_window(
-				NULL, NULL, "Raise", 0, fw);
+				NULL, NULL, "Raise", NULL, 0, fw);
 		}
 	}

@@ -720,7 +721,7 @@ int ewmh_WMStateHidden(
 			/* deiconify */
 			sprintf(cmd, "Iconify off");
 		}
-		execute_function_override_window(NULL, NULL, cmd, 0, fw);
+		execute_function_override_window(NULL, NULL, cmd, NULL, 0, fw);
 	}
 	return 0;
 }
@@ -1042,7 +1043,7 @@ int ewmh_WMStateShaded(
 			 cmd_arg == NET_WM_STATE_ADD))
 		{
 			execute_function_override_window(
-				NULL, NULL, "Windowshade on", 0, fw);
+				NULL, NULL, "Windowshade on", NULL, 0, fw);
 		}
 		else if (
 			IS_SHADED(fw) &&
@@ -1050,7 +1051,7 @@ int ewmh_WMStateShaded(
 			 cmd_arg == NET_WM_STATE_REMOVE))
 		{
 			execute_function_override_window(
-				NULL, NULL, "Windowshade off", 0, fw);
+				NULL, NULL, "Windowshade off", NULL, 0, fw);
 		}
 	}
 	return 0;
@@ -1441,7 +1442,7 @@ int ewmh_WMStateSticky(
 		    bool_arg == NET_WM_STATE_ADD)
 		{
 			execute_function_override_window(
-				NULL, NULL, "Stick on", 0, fw);
+				NULL, NULL, "Stick on", NULL, 0, fw);
 		}
 		else if ((IS_STICKY_ACROSS_PAGES(fw) ||
 			  IS_STICKY_ACROSS_DESKS(fw)) &&
@@ -1449,7 +1450,7 @@ int ewmh_WMStateSticky(
 			  bool_arg == NET_WM_STATE_REMOVE))
 		{
 			execute_function_override_window(
-				NULL, NULL, "Stick off", 1, fw);
+				NULL, NULL, "Stick off", NULL, 1, fw);
 		}
 	}
 	return 0;
diff --git a/fvwm/expand.c b/fvwm/expand.c
index 1e8a87d4..73c7ebf6 100644
--- a/fvwm/expand.c
+++ b/fvwm/expand.c
@@ -28,7 +28,9 @@
 #include "fvwm.h"
 #include "externs.h"
 #include "cursor.h"
+#include "cmdparser.h"
 #include "functions.h"
+#include "expand.h"
 #include "misc.h"
 #include "move_resize.h"
 #include "screen.h"
@@ -1206,7 +1208,7 @@ GOT_STRING:
 /* ---------------------------- interface functions ------------------------ */

 char *expand_vars(
-	char *input, char *arguments[], Bool addto, Bool ismod,
+	char *input, cmdparser_context_t *pc, Bool addto, Bool ismod,
 	cond_rc_t *cond_rc, const exec_context_t *exc)
 {
 	int l, i, l2, n, k, j, m;
@@ -1280,12 +1282,12 @@ char *expand_vars(
 					if (name_has_dollar)
 					{
 						var = expand_vars(
-							var, arguments, addto,
-							ismod, cond_rc, exc);
+							var, pc, addto, ismod,
+							cond_rc, exc);
 					}
 					xlen = expand_args_extended(
-						var, arguments ? arguments[0] :
-						NULL, NULL);
+						var, pc->all_pos_args_string,
+						NULL);
 					if (xlen < 0)
 					{
 						xlen = expand_vars_extended(
@@ -1315,20 +1317,26 @@ char *expand_vars(
 			case '8':
 			case '9':
 			case '*':
+			{
+				char *s;
+
 				if (input[i + 1] == '*')
 				{
-					n = 0;
+					s = pc->all_pos_args_string;
 				}
 				else
 				{
-					n = input[i + 1] - '0' + 1;
+					n = input[i + 1] - '0';
+					s = pc->pos_arg_tokens[n];
 				}
-				if (arguments[n] != NULL)
+				n = input[i + 1] - '0';
+				if (s != NULL)
 				{
-					l2 += strlen(arguments[n]) - 2;
+					l2 += strlen(s) - 2;
 					i++;
 				}
 				break;
+			}
 			case '.':
 				string = get_current_read_dir();
 				break;
@@ -1459,12 +1467,11 @@ char *expand_vars(
 					if (name_has_dollar)
 					{
 						var = expand_vars(
-							var, arguments,	addto,
-							ismod, cond_rc,	exc);
+							var, pc, addto, ismod,
+							cond_rc, exc);
 					}
 					xlen = expand_args_extended(
-						var, arguments ?
-						arguments[0] : NULL,
+						var, pc->all_pos_args_string,
 						&out[j]);
 					if (xlen < 0)
 					{
@@ -1509,19 +1516,23 @@ char *expand_vars(
 			case '8':
 			case '9':
 			case '*':
+			{
+				char *s;
+
 				if (input[i + 1] == '*')
 				{
-					n = 0;
+					s = pc->all_pos_args_string;
 				}
 				else
 				{
-					n = input[i + 1] - '0' + 1;
+					n = input[i + 1] - '0';
+					s = pc->pos_arg_tokens[n];
 				}
-				if (arguments[n] != NULL)
+				if (s != NULL)
 				{
-					for (k = 0; arguments[n][k]; k++)
+					for (k = 0; s[k]; k++)
 					{
-						out[j++] = arguments[n][k];
+						out[j++] = s[k];
 					}
 					i++;
 				}
@@ -1534,6 +1545,7 @@ char *expand_vars(
 					i++;
 				}
 				break;
+			}
 			case '.':
 				string = get_current_read_dir();
 				is_string = True;
diff --git a/fvwm/expand.h b/fvwm/expand.h
index 087c3723..b1ec03fa 100644
--- a/fvwm/expand.h
+++ b/fvwm/expand.h
@@ -18,7 +18,7 @@
 /* ---------------------------- interface functions ------------------------ */

 char *expand_vars(
-	char *input, char *arguments[], Bool addto, Bool ismod,
+	char *input, cmdparser_context_t *pc, Bool addto, Bool ismod,
 	cond_rc_t *cond_rc, const exec_context_t *exc);

 #endif /* FVWM_EXPAND_H */
diff --git a/fvwm/functable.c b/fvwm/functable.c
index aa53a045..f003184b 100644
--- a/fvwm/functable.c
+++ b/fvwm/functable.c
@@ -84,7 +84,7 @@ const func_t func_table[] =
 	/* - Operate on all windows matching the given condition */

 	CMD_ENT("animatedmove", CMD_AnimatedMove, F_ANIMATED_MOVE,
-		FUNC_NEEDS_WINDOW, CRS_MOVE),
+		FUNC_NEEDS_WINDOW | FUNC_IS_MOVE_TYPE, CRS_MOVE),
 	/* - Like Move, but uses animation to move windows */

 	CMD_ENT("any", CMD_Any, F_ANY, 0, 0),
@@ -381,7 +381,7 @@ const func_t func_table[] =
 	/* - Bind or unbind a mouse button press to an fvwm action */

 	CMD_ENT("move", CMD_Move, F_MOVE,
-		FUNC_NEEDS_WINDOW, CRS_MOVE),
+		FUNC_NEEDS_WINDOW | FUNC_IS_MOVE_TYPE, CRS_MOVE),
 	/* - Move a window */

 	CMD_ENT("movethreshold", CMD_MoveThreshold, F_MOVE_THRESHOLD, 0, 0),
@@ -479,19 +479,20 @@ const func_t func_table[] =
 	/* - Repeat (very unreliably) the last command, don't use */

 	CMD_ENT("resize", CMD_Resize, F_RESIZE,
-		FUNC_NEEDS_WINDOW, CRS_RESIZE),
+		FUNC_NEEDS_WINDOW | FUNC_IS_MOVE_TYPE, CRS_RESIZE),
 	/* - Cause a window to be resized */

 	CMD_ENT("resizemaximize", CMD_ResizeMaximize, F_RESIZE_MAXIMIZE,
-		FUNC_NEEDS_WINDOW, CRS_RESIZE),
+		FUNC_NEEDS_WINDOW | FUNC_IS_MOVE_TYPE, CRS_RESIZE),
 	/* - Resize a window and mark window as maximized */

 	CMD_ENT("resizemove", CMD_ResizeMove, F_RESIZEMOVE,
-		FUNC_NEEDS_WINDOW, CRS_RESIZE),
+		FUNC_NEEDS_WINDOW | FUNC_IS_MOVE_TYPE, CRS_RESIZE),
 	/* - Resize and move in one operation */

 	CMD_ENT("resizemovemaximize", CMD_ResizeMoveMaximize,
-		F_RESIZEMOVE_MAXIMIZE, FUNC_NEEDS_WINDOW, CRS_RESIZE),
+		F_RESIZEMOVE_MAXIMIZE, FUNC_NEEDS_WINDOW | FUNC_IS_MOVE_TYPE,
+		CRS_RESIZE),
 	/* - Resize and move in one operation and mark maximized */

 	CMD_ENT("restacktransients", CMD_RestackTransients, F_RESTACKTRANSIENTS,
@@ -651,3 +652,56 @@ const func_t func_table[] =

 	{ "", 0, 0, 0, 0 }
 };
+
+/* ---------------------------- local functions ---------------------------- */
+
+/*
+ * do binary search on func list
+ */
+static int func_comp(const void *a, const void *b)
+{
+	return (strcmp((char *)a, ((func_t *)b)->keyword));
+}
+
+/* ---------------------------- interface functions ------------------------ */
+
+const func_t *find_builtin_function(const char *func)
+{
+	static int nfuncs = 0;
+	func_t *ret_func;
+	char *temp;
+	char *s;
+
+	if (!func || func[0] == 0)
+	{
+		return NULL;
+	}
+
+	/* since a lot of lines in a typical rc are probably menu/func
+	 * continues: */
+	if (func[0]=='+' || (func[0] == ' ' && func[1] == '+'))
+	{
+		return &(func_table[0]);
+	}
+
+	temp = fxstrdup(func);
+	for (s = temp; *s != 0; s++)
+	{
+		if (isupper(*s))
+		{
+			*s = tolower(*s);
+		}
+	}
+	if (nfuncs == 0)
+	{
+		for ( ; (func_table[nfuncs]).action != NULL; nfuncs++)
+		{
+			/* nothing to do here */
+		}
+	}
+	ret_func = (func_t *)bsearch(
+		temp, func_table, nfuncs, sizeof(func_t), func_comp);
+	free(temp);
+
+	return ret_func;
+}
diff --git a/fvwm/functable.h b/fvwm/functable.h
index 8f0ba7fb..c5bf6b9b 100644
--- a/fvwm/functable.h
+++ b/fvwm/functable.h
@@ -4,22 +4,39 @@
 #define FVWM_FUNCTABLE_H

 /* ---------------------------- included header files ---------------------- */
-#include "functions.h"

 /* ---------------------------- global definitions ------------------------- */

-#define PRE_KEEPRC       "keeprc"
-#define PRE_REPEAT       "repeat"
-#define PRE_SILENT       "silent"
+#define PRE_KEEPRC "keeprc"
+#define PRE_REPEAT "repeat"
+#define PRE_SILENT "silent"

 /* ---------------------------- global macros ------------------------------ */

 /* ---------------------------- type definitions --------------------------- */

+typedef unsigned int func_flags_t;
+
+/* used for parsing commands*/
+typedef struct
+{
+	char *keyword;
+#ifdef __STDC__
+	void (*action)(F_CMD_ARGS);
+#else
+	void (*action)();
+#endif
+	short func_c;
+	func_flags_t flags;
+	int cursor;
+} func_t;
+
 /* ---------------------------- exported variables (globals) --------------- */

 extern const func_t func_table[];

 /* ---------------------------- interface functions ------------------------ */

+const func_t *find_builtin_function(const char *func);
+
 #endif /* FVWM_FUNCTABLE_H */
diff --git a/fvwm/functable_complex.c b/fvwm/functable_complex.c
new file mode 100644
index 00000000..34539014
--- /dev/null
+++ b/fvwm/functable_complex.c
@@ -0,0 +1,157 @@
+/* -*-c-*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* ---------------------------- included header files ---------------------- */
+
+#include "config.h"
+
+#include "libs/fvwmlib.h"
+#include "libs/Picture.h"
+#include "libs/Strings.h"
+
+#include "fvwm.h"
+#include "cmdparser.h"
+#include "execcontext.h"
+#include "functable.h"
+#include "functable.h"
+#include "functable_complex.h"
+#include "misc.h"
+#include "screen.h"
+
+/* ---------------------------- local definitions -------------------------- */
+
+/* ---------------------------- local macros ------------------------------- */
+
+/* ---------------------------- imports ------------------------------------ */
+
+/* ---------------------------- included code files ------------------------ */
+
+/* ---------------------------- local types -------------------------------- */
+
+/* ---------------------------- local variables ---------------------------- */
+
+/* ---------------------------- exported variables (globals) --------------- */
+
+/* ---------------------------- local functions ---------------------------- */
+
+FvwmFunction *NewFvwmFunction(const char *name)
+{
+	FvwmFunction *tmp;
+
+	tmp = fxmalloc(sizeof *tmp);
+	tmp->next_func = Scr.functions;
+	tmp->first_item = NULL;
+	tmp->last_item = NULL;
+	tmp->name = stripcpy(name);
+	tmp->use_depth = 0;
+	Scr.functions = tmp;
+
+	return tmp;
+}
+
+void DestroyFunction(FvwmFunction *func)
+{
+	FunctionItem *fi,*tmp2;
+	FvwmFunction *tmp, *prev;
+
+	if (func == NULL)
+	{
+		return;
+	}
+
+	tmp = Scr.functions;
+	prev = NULL;
+	while (tmp && tmp != func)
+	{
+		prev = tmp;
+		tmp = tmp->next_func;
+	}
+	if (tmp != func)
+	{
+		return;
+	}
+
+	if (func->use_depth != 0)
+	{
+		fvwm_debug(
+			"Function %s is in use (depth %d)", func->name,
+			func->use_depth);
+		return;
+	}
+
+	if (prev == NULL)
+	{
+		Scr.functions = func->next_func;
+	}
+	else
+	{
+		prev->next_func = func->next_func;
+	}
+
+	free(func->name);
+
+	fi = func->first_item;
+	while (fi != NULL)
+	{
+		tmp2 = fi->next_item;
+		if (fi->action != NULL)
+		{
+			free(fi->action);
+		}
+		free(fi);
+		fi = tmp2;
+	}
+	free(func);
+
+	return;
+}
+
+/* ---------------------------- interface functions ------------------------ */
+
+/* find_complex_function expects a token as the input. Make sure you have used
+ * GetNextToken before passing a function name to remove quotes */
+FvwmFunction *find_complex_function(const char *function_name)
+{
+	FvwmFunction *func;
+
+	if (function_name == NULL || *function_name == 0)
+	{
+		return NULL;
+	}
+	func = Scr.functions;
+	while (func != NULL)
+	{
+		if (func->name != NULL)
+		{
+			if (strcasecmp(function_name, func->name) == 0)
+			{
+				return func;
+			}
+		}
+		func = func->next_func;
+	}
+
+	return NULL;
+}
+Bool functions_is_complex_function(const char *function_name)
+{
+	if (find_complex_function(function_name) != NULL)
+	{
+		return True;
+	}
+
+	return False;
+}
diff --git a/fvwm/functable_complex.h b/fvwm/functable_complex.h
new file mode 100644
index 00000000..f3d50483
--- /dev/null
+++ b/fvwm/functable_complex.h
@@ -0,0 +1,55 @@
+/* -*-c-*- */
+
+#ifndef FVWM_FUNCTABLE_COMPLEX_H
+#define FVWM_FUNCTABLE_COMPLEX_H
+
+/* ---------------------------- included header files ---------------------- */
+
+/* ---------------------------- global definitions ------------------------- */
+
+/* ---------------------------- global macros ------------------------------ */
+
+/* ---------------------------- type definitions --------------------------- */
+
+typedef struct FunctionItem
+{
+	struct FvwmFunction *func;       /* the function this item is in */
+	struct FunctionItem *next_item;  /* next function item */
+	char condition;                  /* the character string displayed on
+					  * left*/
+	char *action;                    /* action to be performed */
+	short type;                      /* type of built in function */
+	func_flags_t flags;
+} FunctionItem;
+
+typedef struct FvwmFunction
+{
+	struct FvwmFunction *next_func;  /* next in list of root menus */
+	FunctionItem *first_item;        /* first item in function */
+	FunctionItem *last_item;         /* last item in function */
+	char *name;                      /* function name */
+	int use_depth;
+} FvwmFunction;
+
+/* Types of events for the FUNCTION builtin */
+typedef enum
+{
+	CF_IMMEDIATE =      'i',
+	CF_LATE_IMMEDIATE = 'j',
+	CF_MOTION =         'm',
+	CF_HOLD =           'h',
+	CF_CLICK =          'c',
+	CF_DOUBLE_CLICK =   'd',
+	CF_TIMEOUT =        '-'
+} cfunc_action_t;
+
+/* ---------------------------- exported variables (globals) --------------- */
+
+/* ---------------------------- interface functions ------------------------ */
+
+FvwmFunction *NewFvwmFunction(const char *name);
+void DestroyFunction(FvwmFunction *func);
+FvwmFunction *find_complex_function(const char *function_name);
+Bool functions_is_complex_function(const char *function_name);
+
+#endif /* FVWM_FUNCTABLE_COMPLEX_H */
diff --git a/fvwm/functions.c b/fvwm/functions.c
index a40718cd..571251ec 100644
--- a/fvwm/functions.c
+++ b/fvwm/functions.c
@@ -22,6 +22,9 @@
 #include "config.h"

 #include <stdio.h>
+#if 1 /*!!!*/
+#include <assert.h>
+#endif

 #include "libs/fvwm_x11.h"
 #include "libs/fvwmlib.h"
@@ -36,9 +39,13 @@
 #include "externs.h"
 #include "cursor.h"
 #include "execcontext.h"
+#include "functable.h"
+#include "functable_complex.h"
+#include "cmdparser.h"
+#include "cmdparser_hooks.h"
+#include "cmdparser_old.h"
 #include "functions.h"
 #include "commands.h"
-#include "functable.h"
 #include "events.h"
 #include "modconf.h"
 #include "module_list.h"
@@ -58,45 +65,19 @@

 /* ---------------------------- local types -------------------------------- */

-typedef struct FunctionItem
-{
-	struct FvwmFunction *func;       /* the function this item is in */
-	struct FunctionItem *next_item;  /* next function item */
-	char condition;                  /* the character string displayed on
-					  * left*/
-	char *action;                    /* action to be performed */
-	short type;                      /* type of built in function */
-	FUNC_FLAGS_TYPE flags;
-} FunctionItem;
-
-typedef struct FvwmFunction
-{
-	struct FvwmFunction *next_func;  /* next in list of root menus */
-	FunctionItem *first_item;        /* first item in function */
-	FunctionItem *last_item;         /* last item in function */
-	char *name;                      /* function name */
-	int use_depth;
-} FvwmFunction;
-
-/* Types of events for the FUNCTION builtin */
-typedef enum
-{
-	CF_IMMEDIATE =      'i',
-	CF_LATE_IMMEDIATE = 'j',
-	CF_MOTION =         'm',
-	CF_HOLD =           'h',
-	CF_CLICK =          'c',
-	CF_DOUBLE_CLICK =   'd',
-	CF_TIMEOUT =        '-'
-} cfunc_action_t;
-
 /* ---------------------------- forward declarations ----------------------- */

 static void execute_complex_function(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	Bool *desperate, Bool has_ref_window_moved);
+	cond_rc_t *cond_rc, const exec_context_t *exc, cmdparser_context_t *pc,
+	FvwmFunction *func, Bool has_ref_window_moved);
+
+ /* ---------------------------- local variables ---------------------------- */

-/* ---------------------------- local variables ---------------------------- */
+/* Temporary instance of the hooks functions used in this file.  The goal is
+ * to remove all parsing and expansion from functions.c and move it into a
+ * separate, replaceable file.  */
+
+static const cmdparser_hooks_t *cmdparser_hooks = NULL;

 /* ---------------------------- exported variables (globals) --------------- */

@@ -294,120 +275,85 @@ static Bool DeferExecution(
 	return False;
 }

-/*
-** do binary search on func list
-*/
-static int func_comp(const void *a, const void *b)
-{
-	return (strcmp((char *)a, ((func_t *)b)->keyword));
-}
-
-static const func_t *find_builtin_function(char *func)
-{
-	static int nfuncs = 0;
-	func_t *ret_func;
-	char *temp;
-	char *s;
-
-	if (!func || func[0] == 0)
-	{
-		return NULL;
-	}
-
-	/* since a lot of lines in a typical rc are probably menu/func
-	 * continues: */
-	if (func[0]=='+' || (func[0] == ' ' && func[1] == '+'))
-	{
-		return &(func_table[0]);
-	}
-
-	temp = fxstrdup(func);
-	for (s = temp; *s != 0; s++)
-	{
-		if (isupper(*s))
-		{
-			*s = tolower(*s);
-		}
-	}
-	if (nfuncs == 0)
-	{
-		for ( ; (func_table[nfuncs]).action != NULL; nfuncs++)
-		{
-			/* nothing to do here */
-		}
-	}
-	ret_func = (func_t *)bsearch(
-		temp, func_table, nfuncs, sizeof(func_t), func_comp);
-	free(temp);
-
-	return ret_func;
-}
-
-static void __execute_function(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	FUNC_FLAGS_TYPE exec_flags, char *args[], Bool has_ref_window_moved)
+static void __execute_command_line(
+	cond_rc_t *cond_rc, const exec_context_t *exc, char *xaction,
+	cmdparser_context_t *caller_pc,
+	func_flags_t exec_flags, char *all_pos_args_string,
+	char *pos_arg_tokens[], Bool has_ref_window_moved)
 {
-	static int func_depth = 0;
+	cmdparser_context_t pc;
 	cond_rc_t *func_rc = NULL;
 	cond_rc_t dummy_rc;
 	Window w;
-	int j;
-	char *function;
-	char *taction;
-	char *trash;
-	char *trash2;
-	char *expaction = NULL;
-	char *arguments[11];
+	char *err_cline;
+	const char *err_func;
+	cmdparser_execute_type_t exec_type;
 	const func_t *bif;
-	Bool set_silent;
-	Bool must_free_function = False;
-	Bool must_free_expaction = False;
-	Bool do_keep_rc = False;
+	FvwmFunction *complex_function;
+	int set_silent;
+	int do_keep_rc = 0;
 	/* needed to be able to avoid resize to use moved windows for base */
 	extern Window PressedW;
 	Window dummy_w;
+	int rc;

-	if (!action)
-	{
-		return;
-	}
-	/* ignore whitespace at the beginning of all config lines */
-	action = SkipSpaces(action, NULL, 0);
-	if (!action || action[0] == 0)
+#if 1 /*!!!*/
+	fprintf(stderr, "%s: cpc %p, xaction '%s'\n", __func__, caller_pc, xaction);
+#endif
+	set_silent = 0;
+	/* generate a parsing context; this *must* be destroyed before
+	 * returning */
+	rc = cmdparser_hooks->create_context(
+		&pc, caller_pc, xaction, all_pos_args_string, pos_arg_tokens);
+	if (rc != 0)
 	{
-		/* impossibly short command */
-		return;
+		goto fn_exit;
 	}
-	if (action[0] == '#')
+cmdparser_hooks->debug(&pc, "!!!A");
+	rc = cmdparser_hooks->handle_line_start(&pc);
+	if (rc != 0)
 	{
-		/* a comment */
-		return;
+		goto fn_exit;
 	}
+cmdparser_hooks->debug(&pc, "!!!B");

-	func_depth++;
-	if (func_depth > MAX_FUNCTION_DEPTH)
-	{
-		fvwm_debug(__func__,
-			   "Function '%s' called with a depth of %i, "
-			   "stopping function execution!",
-			   action, func_depth);
-		func_depth--;
-		return;
-	}
-	if (args)
 	{
-		for (j = 0; j < 11; j++)
+		cmdparser_prefix_flags_t flags;
+
+		flags = cmdparser_hooks->handle_line_prefix(&pc);
+cmdparser_hooks->debug(&pc, "!!!BA");
+		if (flags & CP_PREFIX_MINUS)
 		{
-			arguments[j] = args[j];
+#if 1 /*!!!*/
+fprintf(stderr, "!!!do not expand\n");
+#endif
+			exec_flags |= FUNC_DONT_EXPAND_COMMAND;
 		}
-	}
-	else
-	{
-		for (j = 0; j < 11; j++)
+		if (flags & CP_PREFIX_SILENT)
 		{
-			arguments[j] = NULL;
+#if 1 /*!!!*/
+fprintf(stderr, "!!!is silent\n");
+#endif
+			if (Scr.flags.are_functions_silent == 0)
+			{
+				set_silent = 1;
+				Scr.flags.are_functions_silent = 1;
+			}
+		}
+		if (flags & CP_PREFIX_KEEPRC)
+		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!do keeprc\n");
+#endif
+			do_keep_rc = 1;
 		}
+		if (pc.cline == NULL)
+		{
+			goto fn_exit;
+		}
+		err_cline = pc.cline;
 	}
+cmdparser_hooks->debug(&pc, "!!!C");

 	if (exc->w.fw == NULL || IS_EWMH_DESKTOP(FW_W(exc->w.fw)))
 	{
@@ -444,50 +390,7 @@ static void __execute_function(
 			w = FW_W(exc->w.fw);
 		}
 	}
-
-	set_silent = False;
-	if (action[0] == '-')
-	{
-		exec_flags |= FUNC_DONT_EXPAND_COMMAND;
-		action++;
-	}
-
-	taction = action;
-	/* parse prefixes */
-	trash = PeekToken(taction, &trash2);
-	while (trash)
-	{
-		if (StrEquals(trash, PRE_SILENT))
-		{
-			if (Scr.flags.are_functions_silent == 0)
-			{
-				set_silent = 1;
-				Scr.flags.are_functions_silent = 1;
-			}
-			taction = trash2;
-			trash = PeekToken(taction, &trash2);
-		}
-		else if (StrEquals(trash, PRE_KEEPRC))
-		{
-			do_keep_rc = True;
-			taction = trash2;
-			trash = PeekToken(taction, &trash2);
-		}
-		else
-		{
-			break;
-		}
-	}
-	if (taction == NULL)
-	{
-		if (set_silent)
-		{
-			Scr.flags.are_functions_silent = 0;
-		}
-		func_depth--;
-		return;
-	}
-	if (cond_rc == NULL || do_keep_rc == True)
+	if (cond_rc == NULL || do_keep_rc)
 	{
 		condrc_init(&dummy_rc);
 		func_rc = &dummy_rc;
@@ -496,42 +399,22 @@ static void __execute_function(
 	{
 		func_rc = cond_rc;
 	}
-
-	GetNextToken(taction, &function);
-	if (function)
-	{
-		char *tmp = function;
-		function = expand_vars(
-			function, arguments, False, False, func_rc, exc);
-		free(tmp);
-	}
-	if (function && function[0] != '*')
 	{
-#if 1
-		/* DV: with this piece of code it is impossible to have a
-		 * complex function with embedded whitespace that begins with a
-		 * builtin function name, e.g. a function "echo hello". */
-		/* DV: ... and without it some of the complex functions will
-		 * fail */
-		char *tmp = function;
+		const char *func;

-		while (*tmp && !isspace(*tmp))
+		func = cmdparser_hooks->parse_command_name(&pc, func_rc, exc);
+		if (func != NULL)
 		{
-			tmp++;
+cmdparser_hooks->debug(&pc, "!!!D");
+			bif = find_builtin_function(func);
+			err_func = func;
 		}
-		*tmp = 0;
-#endif
-		bif = find_builtin_function(function);
-		must_free_function = True;
-	}
-	else
-	{
-		bif = NULL;
-		if (function)
+		else
 		{
-			free(function);
+cmdparser_hooks->debug(&pc, "!!!E");
+			bif = NULL;
+			err_func = "";
 		}
-		function = "";
 	}

 	if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor &&
@@ -539,177 +422,163 @@ static void __execute_function(
 	{
 		fvwm_debug(__func__,
 			   "Command can not be added to a decor; executing"
-			   " command now: '%s'", action);
+			   " command now: '%s'", err_cline);
 	}

 	if (!(exec_flags & FUNC_DONT_EXPAND_COMMAND))
 	{
-		expaction = expand_vars(
-			taction, arguments, (bif) ?
-			!!(bif->flags & FUNC_ADD_TO) :
-			False, (taction[0] == '*'), func_rc, exc);
-		must_free_expaction = True;
-		if (func_depth <= 1)
+		cmdparser_hooks->expand_command_line(
+			&pc, (bif) ? !!(bif->flags & FUNC_ADD_TO) : False,
+			func_rc, exc);
+cmdparser_hooks->debug(&pc, "!!!F");
+		if (pc.call_depth <= 1)
 		{
-			must_free_expaction = set_repeat_data(expaction, REPEAT_COMMAND, bif);
+			Bool do_free_string_ourselves;
+
+			do_free_string_ourselves = set_repeat_data(
+				pc.expline, REPEAT_COMMAND, bif);
+			if (do_free_string_ourselves == False)
+			{
+				cmdparser_hooks->release_expanded_line(&pc);
+cmdparser_hooks->debug(&pc, "!!!F");
+			}
 		}
 	}
-	else
-	{
-		expaction = taction;
-		must_free_expaction = False;
-	}
+#if 1 /*!!!*/
+fprintf(stderr, "!!!pc.cline: '%s'\n", pc.cline);
+#endif

-	/* Note: the module config command, "*" can not be handled by the
-	 * regular command table because there is no required white space after
-	 * the asterisk. */
-	if (expaction[0] == '*')
+	exec_type = cmdparser_hooks->find_something_to_execute(
+		&pc, &bif, &complex_function);
+cmdparser_hooks->debug(&pc, "!!!H");
+fprintf(stderr, "!!!exec_type %d\n", exec_type);
+	switch (exec_type)
 	{
-		if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
-		{
-			fvwm_debug(__func__,
-				   "Command can not be added to a decor;"
-				   " executing command now: '%s'", expaction);
-		}
-
-		/* process a module config command */
-		ModuleConfig(expaction);
-	}
-	else
+	case CP_EXECTYPE_BUILTIN_FUNCTION:
 	{
 		const exec_context_t *exc2;
 		exec_context_changes_t ecc;
 		exec_context_change_mask_t mask;

+cmdparser_hooks->debug(&pc, "!!!J");
+#if 1 /*!!!*/
+		assert(bif && bif->func_c != F_FUNCTION);
+#endif
+#if 1 /*!!!*/
+fprintf(stderr, "!!!pc.cline: '%s'\n", pc.cline);
+#endif
 		mask = (w != exc->w.w) ? ECC_W : 0;
 		ecc.w.fw = exc->w.fw;
 		ecc.w.w = w;
 		ecc.w.wcontext = exc->w.wcontext;
-		if (bif && bif->func_t != F_FUNCTION)
+		if (
+			(bif->flags & FUNC_NEEDS_WINDOW) &&
+			!(exec_flags & FUNC_DONT_DEFER))
 		{
-			char *runaction;
-			Bool rc = False;
+			Bool rc;

-			runaction = SkipNTokens(expaction, 1);
-			if ((bif->flags & FUNC_NEEDS_WINDOW) &&
-			    !(exec_flags & FUNC_DONT_DEFER))
-			{
-				rc = DeferExecution(
-					&ecc, &mask, bif->cursor,
-					exc->x.elast->type,
-					(bif->flags & FUNC_ALLOW_UNMANAGED));
-			}
-			else if ((bif->flags & FUNC_NEEDS_WINDOW) &&
-				 !__context_has_window(
-					 exc,
-					 bif->flags & FUNC_ALLOW_UNMANAGED))
-			{
-				/* no context window and not allowed to defer,
-				 * skip command */
-				rc = True;
-			}
-			if (rc == False)
+			rc = DeferExecution(
+				&ecc, &mask, bif->cursor, exc->x.elast->type,
+				(bif->flags & FUNC_ALLOW_UNMANAGED));
+			if (rc == True)
 			{
-				exc2 = exc_clone_context(exc, &ecc, mask);
-				if (has_ref_window_moved &&
-				    (bif->func_t == F_ANIMATED_MOVE ||
-				     bif->func_t == F_MOVE ||
-				     bif->func_t == F_RESIZE ||
-				     bif->func_t == F_RESIZEMOVE ||
-				     bif->func_t == F_RESIZE_MAXIMIZE ||
-				     bif->func_t == F_RESIZEMOVE_MAXIMIZE))
-				{
-					dummy_w = PressedW;
-					PressedW = None;
-					bif->action(func_rc, exc2, runaction);
-					PressedW = dummy_w;
-				}
-				else
-				{
-					bif->action(func_rc, exc2, runaction);
-				}
-				exc_destroy_context(exc2);
+				break;
 			}
+#if 1 /*!!!*/
+fprintf(stderr, "!!!deferred: %d\n", rc);
+#endif
 		}
-		else
+		else if (
+			(bif->flags & FUNC_NEEDS_WINDOW) &&
+			!__context_has_window(
+				exc, bif->flags & FUNC_ALLOW_UNMANAGED))
 		{
-			Bool desperate = 1;
-			char *runaction;
-
-			if (bif)
-			{
-				/* strip "function" command */
-				runaction = SkipNTokens(expaction, 1);
-			}
-			else
-			{
-				runaction = expaction;
-			}
-			exc2 = exc_clone_context(exc, &ecc, mask);
-			execute_complex_function(
-				func_rc, exc2, runaction, &desperate,
-				has_ref_window_moved);
-			if (!bif && desperate)
-			{
-				if (executeModuleDesperate(
-					    func_rc, exc, runaction) == NULL &&
-				    *function != 0 && !set_silent)
-				{
-					fvwm_debug(__func__,
-						   "No such command '%s'",
-						   function);
-				}
-			}
-			exc_destroy_context(exc2);
+#if 1 /*!!!*/
+fprintf(stderr, "!!!skip no-defer\n");
+#endif
+			/* no context window and not allowed to defer,
+			 * skip command */
+			break;
 		}
+		exc2 = exc_clone_context(exc, &ecc, mask);
+		dummy_w = PressedW;
+		if (
+			has_ref_window_moved &&
+			(bif->flags & FUNC_IS_MOVE_TYPE))
+		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!erase PressedW\n");
+#endif
+			PressedW = None;
+		}
+#if 1 /*!!!*/
+fprintf(stderr, "!!!execute '%s'\n", bif->keyword);
+#endif
+		bif->action(func_rc, exc2, pc.cline, &pc);
+		PressedW = dummy_w;
+		exc_destroy_context(exc2);
+		break;
 	}
-
-	if (set_silent)
-	{
-		Scr.flags.are_functions_silent = 0;
-	}
-	if (cond_rc != NULL)
-	{
-		cond_rc->break_levels = func_rc->break_levels;
-	}
-	if (must_free_function)
+	case CP_EXECTYPE_COMPLEX_FUNCTION:
 	{
-		free(function);
+		const exec_context_t *exc2;
+		exec_context_changes_t ecc;
+		exec_context_change_mask_t mask;
+
+		mask = (w != exc->w.w) ? ECC_W : 0;
+		ecc.w.fw = exc->w.fw;
+		ecc.w.w = w;
+		ecc.w.wcontext = exc->w.wcontext;
+		exc2 = exc_clone_context(exc, &ecc, mask);
+		execute_complex_function(
+			func_rc, exc2, &pc, complex_function,
+			has_ref_window_moved);
+		exc_destroy_context(exc2);
+		break;
 	}
-	/* Free the string allocated by expand_vars earlier in this function. */
-	if (must_free_expaction)
-	{
-		free(expaction);
+	case CP_EXECTYPE_COMPLEX_FUNCTION_DOES_NOT_EXIST:
+		fvwm_debug(
+			__func__, "No such function %s",
+			pc.complex_function_name);
+		break;
+	case CP_EXECTYPE_MODULECONFIG:
+		/* Note: the module config command, "*" can not be handled by
+		 * the regular command table because there is no required
+		 * white space after the asterisk. */
+		if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
+		{
+			fvwm_debug(
+				__func__, "Command can not be added to a decor;"
+				" executing command now: '%s'", pc.expline);
+		}
+		/* process a module config command */
+		ModuleConfig(pc.expline);
+		goto fn_exit;
+	case CP_EXECTYPE_UNKNOWN:
+		if (executeModuleDesperate(func_rc, exc, pc.cline, &pc) ==
+		    NULL && *err_func != 0 && !set_silent)
+		{
+			fvwm_debug(__func__, "No such command '%s'", err_func);
+		}
+		break;
+#if 1 /*!!!*/
+	default:
+		assert(!"bad exec_type");
+#endif
 	}
-	func_depth--;
-
-	return;
-}

-/* find_complex_function expects a token as the input. Make sure you have used
- * GetNextToken before passing a function name to remove quotes */
-static FvwmFunction *find_complex_function(const char *function_name)
-{
-	FvwmFunction *func;
-
-	if (function_name == NULL || *function_name == 0)
+  fn_exit:
+	if (func_rc != NULL && cond_rc != NULL)
 	{
-		return NULL;
+		cond_rc->break_levels = func_rc->break_levels;
 	}
-	func = Scr.functions;
-	while (func != NULL)
+	if (set_silent)
 	{
-		if (func->name != NULL)
-		{
-			if (strcasecmp(function_name, func->name) == 0)
-			{
-				return func;
-			}
-		}
-		func = func->next_func;
+		Scr.flags.are_functions_silent = 0;
 	}
+	cmdparser_hooks->destroy_context(&pc);

-	return NULL;
+	return;
 }

 /*
@@ -801,7 +670,9 @@ static cfunc_action_t CheckActionType(

 static void __run_complex_function_items(
 	cond_rc_t *cond_rc, char cond, FvwmFunction *func,
-	const exec_context_t *exc, char *args[], Bool has_ref_window_moved)
+	const exec_context_t *exc, cmdparser_context_t *caller_pc,
+	char *all_pos_args_string, char *pos_arg_tokens[],
+	Bool has_ref_window_moved)
 {
 	char c;
 	FunctionItem *fi;
@@ -825,9 +696,10 @@ static void __run_complex_function_items(
 		}
 		if (c == cond)
 		{
-			__execute_function(
-				cond_rc, exc, fi->action, FUNC_DONT_DEFER,
-				args, has_ref_window_moved);
+			__execute_command_line(
+				cond_rc, exc, fi->action, caller_pc,
+				FUNC_DONT_DEFER, all_pos_args_string,
+				pos_arg_tokens, has_ref_window_moved);
 			if (!has_ref_window_moved && PressedW &&
 			    XTranslateCoordinates(
 				  dpy, PressedW , Scr.Root, 0, 0, &x, &y,
@@ -843,7 +715,8 @@ static void __run_complex_function_items(
 }

 static void __cf_cleanup(
-	int *depth, char **arguments, cond_rc_t *cond_rc)
+	int *depth, char *all_pos_args_string, char **pos_arg_tokens,
+	cond_rc_t *cond_rc)
 {
 	int i;

@@ -852,11 +725,15 @@ static void __cf_cleanup(
 	{
 		Scr.flags.is_executing_complex_function = 0;
 	}
-	for (i = 0; i < 11; i++)
+	if (all_pos_args_string != NULL)
 	{
-		if (arguments[i] != NULL)
+		free(all_pos_args_string);
+	}
+	for (i = 0; i < CMDPARSER_NUM_POS_ARGS; i++)
+	{
+		if (pos_arg_tokens[i] != NULL)
 		{
-			free(arguments[i]);
+			free(pos_arg_tokens[i]);
 		}
 	}
 	if (cond_rc->break_levels > 0)
@@ -868,8 +745,8 @@ static void __cf_cleanup(
 }

 static void execute_complex_function(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	Bool *desperate, Bool has_ref_window_moved)
+	cond_rc_t *cond_rc, const exec_context_t *exc, cmdparser_context_t *pc,
+	FvwmFunction *func, Bool has_ref_window_moved)
 {
 	cond_rc_t tmp_rc;
 	cfunc_action_t type = CF_MOTION;
@@ -884,11 +761,11 @@ static void execute_complex_function(
 	int do_run_late_immediate = 0;
 	int do_allow_unmanaged = FUNC_ALLOW_UNMANAGED;
 	int do_allow_unmanaged_immediate = FUNC_ALLOW_UNMANAGED;
-	char *arguments[11], *taction;
-	char *func_name;
+	char *all_pos_args_string;
+	char *pos_arg_tokens[CMDPARSER_NUM_POS_ARGS];
+	char *taction;
 	int x, y ,i;
 	XEvent d;
-	FvwmFunction *func;
 	static int depth = 0;
 	const exec_context_t *exc2;
 	exec_context_changes_t ecc;
@@ -897,6 +774,9 @@ static void execute_complex_function(
 	int button;
 	XEvent *te;

+#if 1 /*!!!*/
+	assert(func != NULL);
+#endif
 	if (cond_rc == NULL)
 	{
 		condrc_init(&tmp_rc);
@@ -908,54 +788,36 @@ static void execute_complex_function(
 	ecc.w.fw = exc->w.fw;
 	ecc.w.w = exc->w.w;
 	ecc.w.wcontext = exc->w.wcontext;
-	/* find_complex_function expects a token, not just a quoted string */
-	func_name = PeekToken(action, &taction);
-	if (!func_name)
-	{
-		return;
-	}
-	func = find_complex_function(func_name);
-	if (func == NULL)
-	{
-		if (*desperate == 0)
-		{
-			fvwm_debug(__func__, "No such function %s",
-				   action);
-		}
-		return;
-	}
 	if (!depth)
 	{
 		Scr.flags.is_executing_complex_function = 1;
 	}
 	depth++;
-	*desperate = 0;
 	/* duplicate the whole argument list for use as '$*' */
+	taction = pc->cline;
 	if (taction)
 	{
-		arguments[0] = fxstrdup(taction);
+		all_pos_args_string = fxstrdup(taction);
 		/* strip trailing newline */
-		if (arguments[0][0])
+		if (all_pos_args_string[0])
 		{
-			int l= strlen(arguments[0]);
+			int l = strlen(all_pos_args_string);

-			if (arguments[0][l - 1] == '\n')
+			if (all_pos_args_string[l - 1] == '\n')
 			{
-				arguments[0][l - 1] = 0;
+				all_pos_args_string[l - 1] = 0;
 			}
 		}
 		/* Get the argument list */
-		for (i = 1; i < 11; i++)
+		for (i = 0; i < CMDPARSER_NUM_POS_ARGS; i++)
 		{
-			taction = GetNextToken(taction, &arguments[i]);
+			taction = GetNextToken(taction, &pos_arg_tokens[i]);
 		}
 	}
 	else
 	{
-		for (i = 0; i < 11; i++)
-		{
-			arguments[i] = NULL;
-		}
+		all_pos_args_string = 0;
+		memset(pos_arg_tokens, 0, sizeof(pos_arg_tokens));
 	}
 	/* In case we want to perform an action on a button press, we
 	 * need to fool other routines */
@@ -992,10 +854,12 @@ static void execute_complex_function(
 	{
 		if (DeferExecution(
 			    &ecc, &mask, CRS_SELECT, trigger_evtype,
-			    do_allow_unmanaged_immediate))
+			    do_allow_unmanaged_immediate) == True)
 		{
 			func->use_depth--;
-			__cf_cleanup(&depth, arguments, cond_rc);
+			__cf_cleanup(
+				&depth, all_pos_args_string, pos_arg_tokens,
+				cond_rc);
 			return;
 		}
 		NeedsTarget = False;
@@ -1012,16 +876,19 @@ static void execute_complex_function(
 	if (!GrabEm(CRS_NONE, GRAB_NORMAL))
 	{
 		func->use_depth--;
-		fvwm_debug(__func__, "Grab failed in function %s,"
-			   " unable to execute immediate action", action);
-		__cf_cleanup(&depth, arguments, cond_rc);
+		fvwm_debug(
+			__func__,
+			"Grab failed, unable to execute immediate action");
+		__cf_cleanup(
+			&depth, all_pos_args_string, pos_arg_tokens, cond_rc);
 		return;
 	}
 	if (has_immediate)
 	{
 		exc2 = exc_clone_context(exc, &ecc, mask);
 		__run_complex_function_items(
-			cond_rc, CF_IMMEDIATE, func, exc2, arguments,
+			cond_rc, CF_IMMEDIATE, func, exc2, pc,
+			all_pos_args_string, pos_arg_tokens,
 			has_ref_window_moved);
 		exc_destroy_context(exc2);
 	}
@@ -1055,7 +922,8 @@ static void execute_complex_function(
 	if (!Persist || cond_rc->break_levels != 0)
 	{
 		func->use_depth--;
-		__cf_cleanup(&depth, arguments, cond_rc);
+		__cf_cleanup(
+			&depth, all_pos_args_string, pos_arg_tokens, cond_rc);
 		UngrabEm(GRAB_NORMAL);
 		return;
 	}
@@ -1066,10 +934,12 @@ static void execute_complex_function(
 	{
 		if (DeferExecution(
 			    &ecc, &mask, CRS_SELECT, trigger_evtype,
-			    do_allow_unmanaged))
+			    do_allow_unmanaged) == True)
 		{
 			func->use_depth--;
-			__cf_cleanup(&depth, arguments, cond_rc);
+			__cf_cleanup(
+				&depth, all_pos_args_string, pos_arg_tokens,
+				cond_rc);
 			UngrabEm(GRAB_NORMAL);
 			return;
 		}
@@ -1107,7 +977,8 @@ static void execute_complex_function(
 	{
 		exc2 = exc_clone_context(exc, &ecc, mask);
 		__run_complex_function_items(
-			cond_rc, CF_LATE_IMMEDIATE, func, exc2, arguments,
+			cond_rc, CF_LATE_IMMEDIATE, func, exc2, pc,
+			all_pos_args_string, pos_arg_tokens,
 			has_ref_window_moved);
 		exc_destroy_context(exc2);
 		do_run_late_immediate = 0;
@@ -1179,135 +1050,55 @@ static void execute_complex_function(
 	if (do_run_late_immediate)
 	{
 		__run_complex_function_items(
-			cond_rc, CF_LATE_IMMEDIATE, func, exc2, arguments,
+			cond_rc, CF_LATE_IMMEDIATE, func, exc2, pc,
+			all_pos_args_string, pos_arg_tokens,
 			has_ref_window_moved);
 	}
 	__run_complex_function_items(
-		cond_rc, type, func, exc2, arguments, has_ref_window_moved);
+		cond_rc, type, func, exc2, pc, all_pos_args_string,
+		pos_arg_tokens, has_ref_window_moved);
 	exc_destroy_context(exc2);
 	/* This is the right place to ungrab the pointer (see comment above).
 	 */
 	func->use_depth--;
-	__cf_cleanup(&depth, arguments, cond_rc);
+	__cf_cleanup(&depth, all_pos_args_string, pos_arg_tokens, cond_rc);
 	UngrabEm(GRAB_NORMAL);

 	return;
 }

-/*
- * create a new FvwmFunction
- */
-static FvwmFunction *NewFvwmFunction(const char *name)
-{
-	FvwmFunction *tmp;
-
-	tmp = fxmalloc(sizeof *tmp);
-	tmp->next_func = Scr.functions;
-	tmp->first_item = NULL;
-	tmp->last_item = NULL;
-	tmp->name = stripcpy(name);
-	tmp->use_depth = 0;
-	Scr.functions = tmp;
-
-	return tmp;
-}
-
-static void DestroyFunction(FvwmFunction *func)
-{
-	FunctionItem *fi,*tmp2;
-	FvwmFunction *tmp, *prev;
-
-	if (func == NULL)
-	{
-		return;
-	}
-
-	tmp = Scr.functions;
-	prev = NULL;
-	while (tmp && tmp != func)
-	{
-		prev = tmp;
-		tmp = tmp->next_func;
-	}
-	if (tmp != func)
-	{
-		return;
-	}
-
-	if (func->use_depth != 0)
-	{
-		fvwm_debug(__func__,
-			   "Function %s is in use (depth %d)", func->name,
-			   func->use_depth);
-		return;
-	}
-
-	if (prev == NULL)
-	{
-		Scr.functions = func->next_func;
-	}
-	else
-	{
-		prev->next_func = func->next_func;
-	}
-
-	free(func->name);
-
-	fi = func->first_item;
-	while (fi != NULL)
-	{
-		tmp2 = fi->next_item;
-		if (fi->action != NULL)
-		{
-			free(fi->action);
-		}
-		free(fi);
-		fi = tmp2;
-	}
-	free(func);
-
-	return;
-}
-
 /* ---------------------------- interface functions ------------------------ */

-Bool functions_is_complex_function(const char *function_name)
+void functions_init(void)
 {
-	if (find_complex_function(function_name) != NULL)
-	{
-		return True;
-	}
+	cmdparser_hooks = cmdparser_old_get_hooks();

-	return False;
+	return;
 }

-void execute_function(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	FUNC_FLAGS_TYPE exec_flags)
+void execute_function(F_CMD_ARGS, func_flags_t exec_flags)
 {
-	__execute_function(cond_rc, exc, action, exec_flags, NULL, False);
+	__execute_command_line(F_PASS_ARGS, exec_flags, NULL, NULL, False);

 	return;
 }

 void execute_function_override_wcontext(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	FUNC_FLAGS_TYPE exec_flags, int wcontext)
+	F_CMD_ARGS, func_flags_t exec_flags, int wcontext)
 {
 	const exec_context_t *exc2;
 	exec_context_changes_t ecc;

 	ecc.w.wcontext = wcontext;
 	exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
-	execute_function(cond_rc, exc2, action, exec_flags);
+	execute_function(F_PASS_ARGS_WITH_EXC(exc2), exec_flags);
 	exc_destroy_context(exc2);

 	return;
 }

 void execute_function_override_window(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	FUNC_FLAGS_TYPE exec_flags, FvwmWindow *fw)
+	F_CMD_ARGS, func_flags_t exec_flags, FvwmWindow *fw)
 {
 	const exec_context_t *exc2;
 	exec_context_changes_t ecc;
@@ -1335,13 +1126,13 @@ void execute_function_override_window(
 		exc2 = exc_create_context(
 			&ecc, ECC_TYPE | ECC_FW | ECC_W | ECC_WCONTEXT);
 	}
-	execute_function(cond_rc, exc2, action, exec_flags);
+	execute_function(F_PASS_ARGS_WITH_EXC(exc2), exec_flags);
 	exc_destroy_context(exc2);

 	return;
 }

-void find_func_t(char *action, short *func_t, unsigned char *flags)
+void find_func_t(char *action, short *ret_func_c, func_flags_t *flags)
 {
 	int j, len = 0;
 	char *endtok = action;
@@ -1365,9 +1156,9 @@ void find_func_t(char *action, short *func_t, unsigned char *flags)
 			{
 				matched=True;
 				/* found key word */
-				if (func_t)
+				if (ret_func_c)
 				{
-					*func_t = func_table[j].func_t;
+					*ret_func_c = func_table[j].func_c;
 				}
 				if (flags)
 				{
@@ -1382,9 +1173,9 @@ void find_func_t(char *action, short *func_t, unsigned char *flags)
 		}
 		/* No clue what the function is. Just return "BEEP" */
 	}
-	if (func_t)
+	if (ret_func_c)
 	{
-		*func_t = F_BEEP;
+		*ret_func_c = F_BEEP;
 	}
 	if (flags)
 	{
diff --git a/fvwm/functions.h b/fvwm/functions.h
index b127626f..b87256a8 100644
--- a/fvwm/functions.h
+++ b/fvwm/functions.h
@@ -6,6 +6,8 @@
 /* ---------------------------- included header files ---------------------- */

 #include "execcontext.h"
+#include "functable.h"
+#include "functable_complex.h"

 /* ---------------------------- global definitions ------------------------- */

@@ -17,10 +19,12 @@ typedef enum
 	FUNC_ADD_TO              = 0x04,
 	FUNC_DECOR               = 0x08,
 	FUNC_ALLOW_UNMANAGED     = 0x10,
+	/* only used in __execute_command_line */
+	FUNC_IS_MOVE_TYPE        = 0x20,
 	/* only to be passed to execute_function() */
-	FUNC_IS_UNMANAGED        = 0x20,
-	FUNC_DONT_EXPAND_COMMAND = 0x40,
-	FUNC_DONT_DEFER          = 0x80,
+	FUNC_IS_UNMANAGED        = 0x40,
+	FUNC_DONT_EXPAND_COMMAND = 0x80,
+	FUNC_DONT_DEFER          = 0x100,

 	/* The values are not used internally but by external scripts parsing
 	 * functable.  Hence all the values below are 0
@@ -51,36 +55,17 @@ typedef enum

 /* ---------------------------- type definitions --------------------------- */

-/* used for parsing commands*/
-typedef struct
-{
-	char *keyword;
-#ifdef __STDC__
-	void (*action)(F_CMD_ARGS);
-#else
-	void (*action)();
-#endif
-	short func_t;
-	FUNC_FLAGS_TYPE flags;
-	int cursor;
-} func_t;
-
 /* ---------------------------- exported variables (globals) --------------- */

 /* ---------------------------- interface functions ------------------------ */

-void find_func_t(
-	char *action, short *func_t, FUNC_FLAGS_TYPE *flags);
-Bool functions_is_complex_function(
-	const char *function_name);
-void execute_function(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	FUNC_FLAGS_TYPE exec_flags);
+/* needs to be called before any command line can be executed */
+void functions_init(void);
+void find_func_t(char *action, short *func_t, func_flags_t *flags);
+void execute_function(F_CMD_ARGS, func_flags_t exec_flags);
 void execute_function_override_wcontext(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	FUNC_FLAGS_TYPE exec_flags, int wcontext);
+	F_CMD_ARGS, func_flags_t exec_flags, int wcontext);
 void execute_function_override_window(
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
-	FUNC_FLAGS_TYPE exec_flags, FvwmWindow *fw);
+	F_CMD_ARGS, func_flags_t exec_flags, FvwmWindow *fw);

 #endif /* FVWM_FUNCTIONS_H */
diff --git a/fvwm/fvwm.h b/fvwm/fvwm.h
index bdc04ee8..4013f1db 100644
--- a/fvwm/fvwm.h
+++ b/fvwm/fvwm.h
@@ -41,6 +41,7 @@
 #include "libs/FScreen.h"
 #include "window_flags.h"
 #include "condrc.h"
+#include "cmdparser.h"

 /* ---------------------------- global definitions ------------------------- */

@@ -59,9 +60,9 @@

 /* Macro for args passed to fvwm commands... */
 #define F_CMD_ARGS \
-	cond_rc_t *cond_rc, const exec_context_t *exc, char *action
-#define F_PASS_ARGS cond_rc, exc, action
-#define FUNC_FLAGS_TYPE unsigned char
+	cond_rc_t *cond_rc, const exec_context_t *exc, char *action, cmdparser_context_t *pc
+#define F_PASS_ARGS cond_rc, exc, action, pc
+#define F_PASS_ARGS_WITH_EXC(new_exc) cond_rc, (new_exc), action, pc

 /* access macros */
 #define FW_W_FRAME(fw)        ((fw)->wins.frame)
@@ -426,48 +427,6 @@ struct name_condition		/* matches to namelists in this list are
 	struct name_condition *next;
 };

-/* Window mask for Circulate and Direction functions */
-typedef struct WindowConditionMask
-{
-	struct
-	{
-		unsigned do_accept_focus : 1;
-		unsigned do_check_desk : 1;
-		unsigned do_check_screen : 1;
-		unsigned do_check_cond_desk : 1;
-		unsigned do_check_desk_and_global_page : 1;
-		unsigned do_check_desk_and_page : 1;
-		unsigned do_check_global_page : 1;
-		unsigned do_check_overlapped : 1;
-		unsigned do_check_page : 1;
-		unsigned do_not_check_screen : 1;
-		unsigned needs_current_desk : 1;
-		unsigned needs_current_desk_and_global_page : 1;
-		unsigned needs_current_desk_and_page : 1;
-		unsigned needs_current_global_page : 1;
-		unsigned needs_current_page : 1;
-#define NEEDS_ANY   0
-#define NEEDS_TRUE  1
-#define NEEDS_FALSE 2
-		unsigned needs_focus : 2;
-		unsigned needs_overlapped : 2;
-		unsigned needs_pointer : 2;
-		unsigned needs_same_layer : 1;
-		unsigned use_circulate_hit : 1;
-		unsigned use_circulate_hit_icon : 1;
-		unsigned use_circulate_hit_shaded : 1;
-		unsigned use_do_accept_focus : 1;
-	} my_flags;
-	window_flags flags;
-	window_flags flag_mask;
-	struct name_condition *name_condition;
-	int layer;
-	int desk;
-	struct monitor *screen;
-	int placed_by_button_mask;
-	int placed_by_button_set_mask;
-} WindowConditionMask;
-
 typedef struct pl_penalty_struct
 {
 	float normal;
diff --git a/fvwm/fvwm3.c b/fvwm/fvwm3.c
index 90acfe13..031e5763 100644
--- a/fvwm/fvwm3.c
+++ b/fvwm/fvwm3.c
@@ -556,7 +556,7 @@ void Done(int restart, char *command)
 		ecc.type = restart ? EXCT_TORESTART : EXCT_QUIT;
 		ecc.w.wcontext = C_ROOT;
 		exc = exc_create_context(&ecc, ECC_TYPE | ECC_WCONTEXT);
-		execute_function(NULL, exc, action, 0);
+		execute_function(NULL, exc, action, NULL, 0);
 		exc_destroy_context(exc);
 		free(action);
 	}
@@ -1401,7 +1401,7 @@ static void SetRCDefaults(void)
 		exc = exc_create_context(&ecc, ECC_TYPE | ECC_WCONTEXT);
 		xasprintf(&cmd, "%s%s%s", defaults[i][0], defaults[i][1],
 			defaults[i][2]);
-		execute_function(NULL, exc, cmd, 0);
+		execute_function(NULL, exc, cmd, NULL, 0);
 		free(cmd);
 		exc_destroy_context(exc);
 	}
@@ -1525,7 +1525,7 @@ void StartupStuff(void)
 	{
 		char *action = "Function " start_func_name;

-		execute_function(NULL, exc, action, 0);
+		execute_function(NULL, exc, action, NULL, 0);
 	}

 	/* migo (03-Jul-1999): execute [Session]{Init|Restart}Function */
@@ -1535,7 +1535,7 @@ void StartupStuff(void)
 		char *action;

 		xasprintf(&action, "Function %s", init_func_name);
-		execute_function(NULL, exc, action, 0);
+		execute_function(NULL, exc, action, NULL, 0);
 		free(action);
 	}
 	/* see comment above */
@@ -1738,6 +1738,7 @@ int main(int argc, char **argv)
 	exec_context_changes_t ecc;
 	struct monitor	*m = NULL;

+	functions_init();
 	fvwmlib_init_max_fd();
 	/* Tell the FEvent module an event type that is not used by fvwm. */
 	fev_init_invalid_event_type(KeymapNotify);
@@ -2460,7 +2461,8 @@ int main(int argc, char **argv)
 		for (i = 0; i < num_config_commands; i++)
 		{
 			DoingCommandLine = True;
-			execute_function(NULL, exc, config_commands[i], 0);
+			execute_function(
+				NULL, exc, config_commands[i], NULL, 0);
 			free(config_commands[i]);
 		}
 		DoingCommandLine = False;
diff --git a/fvwm/menucmd.c b/fvwm/menucmd.c
index fb7ea219..e4dfd6f9 100644
--- a/fvwm/menucmd.c
+++ b/fvwm/menucmd.c
@@ -27,6 +27,7 @@
 #include "libs/Strings.h"
 #include "fvwm.h"
 #include "functions.h"
+#include "cmdparser.h"
 #include "repeat.h"
 #include "misc.h"
 #include "move_resize.h"
@@ -113,7 +114,7 @@ static void menu_func(F_CMD_ARGS, Bool fStaysUp)
 	do_menu(&mp, &mret);
 	if (mret.rc == MENU_DOUBLE_CLICKED && action)
 	{
-		execute_function(cond_rc, exc2, action, 0);
+		execute_function(cond_rc, exc2, action, NULL, 0);
 	}
 	if (ret_action != NULL)
 	{
diff --git a/fvwm/menus.c b/fvwm/menus.c
index 520befa0..bd7d6542 100644
--- a/fvwm/menus.c
+++ b/fvwm/menus.c
@@ -37,6 +37,7 @@
 #include "libs/wcontext.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "execcontext.h"
 #include "events.h"
 #include "eventhandler.h"
@@ -242,7 +243,7 @@ static void __menu_execute_function(const exec_context_t **pexc, char *action)
 	exc = exc_clone_context(*pexc, &ecc, ECC_W);
 	old_emf = Scr.flags.is_executing_menu_function;
 	Scr.flags.is_executing_menu_function = 1;
-	execute_function(NULL, exc, action, FUNC_DONT_EXPAND_COMMAND);
+	execute_function(NULL, exc, action, NULL, FUNC_DONT_EXPAND_COMMAND);
 	Scr.flags.is_executing_menu_function = old_emf;
 	exc_destroy_context(exc);
 	/* See if the window has been deleted */
@@ -5610,6 +5611,7 @@ static void menu_tear_off(MenuRoot *mr_to_copy)
 	char *action;
 	cond_rc_t *cond_rc = NULL;
 	const exec_context_t *exc = NULL;
+	cmdparser_context_t *pc = NULL;

 	/* keep the menu open */
 	if (MR_WINDOW(mr_to_copy) != None)
diff --git a/fvwm/misc.c b/fvwm/misc.c
index bd8e483a..a44eedeb 100644
--- a/fvwm/misc.c
+++ b/fvwm/misc.c
@@ -24,6 +24,7 @@
 #include "libs/ftime.h"
 #include "libs/Parse.h"
 #include "libs/Target.h"
+#include "libs/fvwmrect.h"
 #include "libs/FEvent.h"
 #include "fvwm.h"
 #include "externs.h"
diff --git a/fvwm/module_interface.c b/fvwm/module_interface.c
index fdcb493c..c140c59a 100644
--- a/fvwm/module_interface.c
+++ b/fvwm/module_interface.c
@@ -33,6 +33,7 @@
 #include "libs/FEvent.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "functions.h"
 #include "bindings.h"
 #include "misc.h"
@@ -727,7 +728,7 @@ void module_input_execute(struct fmodule_input *input)
 	exc = exc_create_context(
 		&ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W | ECC_WCONTEXT |
 		ECC_MODULE);
-	execute_function(NULL, exc, input->command, flags);
+	execute_function(NULL, exc, input->command, NULL, flags);
 	exc_destroy_context(exc);
 	module_input_discard(input);

diff --git a/fvwm/move_resize.c b/fvwm/move_resize.c
index 7091a984..c10d7d35 100644
--- a/fvwm/move_resize.c
+++ b/fvwm/move_resize.c
@@ -33,6 +33,7 @@
 #include "libs/FEvent.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "cursor.h"
 #include "execcontext.h"
 #include "commands.h"
@@ -2193,7 +2194,8 @@ static void __move_window(F_CMD_ARGS, Bool do_animate, int mode)
 		if (fWarp & !do_animate)
 		{
 			char *cmd = "WarpToWindow 50 50";
-			execute_function_override_window(NULL, exc, cmd, 0, fw);
+			execute_function_override_window(
+				NULL, exc, cmd, NULL, 0, fw);
 		}
 		if (IS_MAXIMIZED(fw))
 		{
@@ -3397,7 +3399,7 @@ void CMD_HideGeometryWindow(F_CMD_ARGS)
 	    "Converting to use: GeometryWindow hide %s", action);

 	xasprintf(&cmd, "GeometryWindow hide %s", action);
-	execute_function_override_window(NULL, NULL, cmd, 0, NULL);
+	execute_function_override_window(NULL, NULL, cmd, NULL, 0, NULL);
 	free(cmd);
 }

@@ -3415,7 +3417,7 @@ void CMD_SnapAttraction(F_CMD_ARGS)
 		   "The command SnapAttraction is obsolete. Please use the"
 		   " following command instead:\n\n%s", cmd);
 	execute_function(
-		cond_rc, exc, cmd,
+		cond_rc, exc, cmd, pc,
 		FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
 	free(cmd);

@@ -3436,7 +3438,7 @@ void CMD_SnapGrid(F_CMD_ARGS)
 		   "The command SnapGrid is obsolete. Please use the following"
 		   " command instead:\n\n%s", cmd);
 	execute_function(
-		cond_rc, exc, cmd,
+		cond_rc, exc, cmd, pc,
 		FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
 	free(cmd);

@@ -4997,7 +4999,7 @@ static void grow_to_closest_type(
 }

 static void unmaximize_fvwm_window(
-	FvwmWindow *fw)
+	FvwmWindow *fw, cmdparser_context_t *pc)
 {
 	char	*cmd;
 	rectangle new_g;
@@ -5016,8 +5018,9 @@ static void unmaximize_fvwm_window(
 	 * If the window was not maximized, then we use the window's normal
 	 * geometry.
 	 */
-	get_relative_geometry(fw, &new_g, fw->fullscreen.was_maximized ?
-			&fw->fullscreen.g.max : &fw->g.normal);
+	get_relative_geometry(
+		fw, &new_g, fw->fullscreen.was_maximized ?
+		&fw->fullscreen.g.max : &fw->g.normal);

 	if (fw->fullscreen.was_maximized)
 	{
@@ -5049,7 +5052,7 @@ static void unmaximize_fvwm_window(
 		fw, new_g.x, new_g.y, new_g.width, new_g.height, True);

 	xasprintf(&cmd, "MoveToScreen %s", fw->m->si->name);
-	execute_function_override_window(NULL, NULL, cmd, 0, fw);
+	execute_function_override_window(NULL, NULL, cmd, NULL, 0, fw);
 	free(cmd);

 	border_draw_decorations(
@@ -5058,14 +5061,14 @@ static void unmaximize_fvwm_window(
 	if (fw->fullscreen.is_shaded)
 	{
 		execute_function_override_window(
-			NULL, NULL, "WindowShade on", 0, fw);
+			NULL, NULL, "WindowShade on", NULL, 0, fw);

 		fw->fullscreen.is_shaded = 0;
 	}

 	if (fw->fullscreen.is_iconified) {
 		execute_function_override_window(
-			NULL, NULL, "Iconify on", 0, fw);
+			NULL, NULL, "Iconify on", NULL, 0, fw);
 		fw->fullscreen.is_iconified = 0;
 	}

@@ -5216,7 +5219,7 @@ void CMD_Maximize(F_CMD_ARGS)
 		}

 		if (toggle == 0 && IS_EWMH_FULLSCREEN(fw)) {
-			unmaximize_fvwm_window(fw);
+			unmaximize_fvwm_window(fw, pc);
 			return;
 		}
 		return;
@@ -5368,11 +5371,11 @@ void CMD_Maximize(F_CMD_ARGS)
 	if (do_forget == True)
 	{
 		fw->g.normal = fw->g.max;
-		unmaximize_fvwm_window(fw);
+		unmaximize_fvwm_window(fw, pc);
 	}
 	else if (IS_MAXIMIZED(fw) && !do_force_maximize)
 	{
-		unmaximize_fvwm_window(fw);
+		unmaximize_fvwm_window(fw, pc);
 	}
 	else /* maximize */
 	{
diff --git a/fvwm/placement.c b/fvwm/placement.c
index ee404906..a6cce055 100644
--- a/fvwm/placement.c
+++ b/fvwm/placement.c
@@ -367,14 +367,15 @@ adjust_for_shared_placement(FvwmWindow *fw, const exec_context_t *exc)

 		xasprintf(&cmd, "GotoDesk %s 0 %d", m_style->si->name,
 				fw->Desk);
-		execute_function_override_window(NULL, NULL, cmd, 0, fw);
+		execute_function_override_window(NULL, NULL, cmd, NULL, 0, fw);
 		free(cmd);
 	}
 done:
 	TAILQ_FOREACH(m, &monitor_q, entry) {
 		if (m->virtual_scr.CurrentDesk == fw->Desk) {
 			xasprintf(&cmd, "MoveToScreen %s", m->si->name);
-			execute_function_override_window(NULL, exc, cmd, 0, fw);
+			execute_function_override_window(
+				NULL, exc, cmd, NULL, 0, fw);
 			free(cmd);

 			break;
diff --git a/fvwm/read.c b/fvwm/read.c
index d71a2efb..dc4d6e4b 100644
--- a/fvwm/read.c
+++ b/fvwm/read.c
@@ -28,6 +28,7 @@
 #include "libs/Strings.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "cursor.h"
 #include "functions.h"
 #include "events.h"
@@ -112,7 +113,8 @@ const char *get_current_read_dir(void)
  * Read and execute each line from stream.
  */
 void run_command_stream(
-	cond_rc_t *cond_rc, FILE *f, const exec_context_t *exc)
+	cond_rc_t *cond_rc, FILE *f, const exec_context_t *exc,
+	cmdparser_context_t *pc)
 {
 	char *tline;
 	char line[1024];
@@ -143,7 +145,7 @@ void run_command_stream(
 		{
 			tline[l - 1] = '\0';
 		}
-		execute_function(cond_rc, exc, tline, 0);
+		execute_function(cond_rc, exc, tline, pc, 0);
 		tline = fgets(line, (sizeof line) - 1, f);
 	}

@@ -192,7 +194,7 @@ static int parse_filename(
  * Returns 0 if file not found
  **/
 int run_command_file(
-	char *filename, const exec_context_t *exc)
+	char *filename, const exec_context_t *exc, cmdparser_context_t *pc)
 {
 	char *full_filename;
 	FILE *f = NULL;
@@ -251,7 +253,7 @@ int run_command_file(
 		fclose(f);
 		return 0;
 	}
-	run_command_stream(NULL, f, exc);
+	run_command_stream(NULL, f, exc, pc);
 	fclose(f);
 	pop_read_file();

@@ -317,7 +319,7 @@ void CMD_Read(F_CMD_ARGS)
 		return;
 	}
 	cursor_control(True);
-	if (!run_command_file(filename, exc))
+	if (!run_command_file(filename, exc, pc))
 	{
 		if (!read_quietly)
 		{
@@ -384,7 +386,7 @@ void CMD_PipeRead(F_CMD_ARGS)
 	}
 	free(command);

-	run_command_stream(cond_rc,f, exc);
+	run_command_stream(cond_rc, f, exc, pc);
 	pclose(f);
 	cursor_control(False);

diff --git a/fvwm/repeat.c b/fvwm/repeat.c
index 92b02b64..3c3b24a4 100644
--- a/fvwm/repeat.c
+++ b/fvwm/repeat.c
@@ -21,6 +21,7 @@
 #include "libs/fvwmlib.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "cursor.h"
 #include "functions.h"
 #include "repeat.h"
@@ -85,6 +86,9 @@ FvwmWindow *repeat_last_fvwm_window = NULL;
  * from within this function have depth 2 and higher, this may be applicable
  * to future enhancements like menus).
  *
+ * Returns True if the caller needs to free the string himself and False if
+ * the repeat module will take care of freeing the string.
+ *
  * TODO: [finish and update description]
  */
 Bool set_repeat_data(void *data, repeat_t type, const func_t *builtin)
@@ -157,7 +161,7 @@ void CMD_Repeat(F_CMD_ARGS)
 	default:
 		action = last.command_line;
 		execute_function(
-			cond_rc, exc, action, FUNC_DONT_EXPAND_COMMAND);
+			cond_rc, exc, action, pc, FUNC_DONT_EXPAND_COMMAND);
 		break;
 	}
 	repeat_depth--;
diff --git a/fvwm/schedule.c b/fvwm/schedule.c
index b1f13223..33bfaf1e 100644
--- a/fvwm/schedule.c
+++ b/fvwm/schedule.c
@@ -25,6 +25,7 @@
 #include "libs/FEvent.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "colorset.h"
 #include "bindings.h"
 #include "misc.h"
@@ -189,7 +190,7 @@ static void execute_obj_func(void *object, void *args)
 			mask |= ECC_FW;
 		}
 		exc = exc_create_context(&ecc, mask);
-		execute_function(NULL, exc, obj->command, 0);
+		execute_function(NULL, exc, obj->command, NULL, 0);
 		exc_destroy_context(exc);
 	}
 	if (obj->period > 0)
diff --git a/fvwm/update.c b/fvwm/update.c
index c3399de2..8cc8ed75 100644
--- a/fvwm/update.c
+++ b/fvwm/update.c
@@ -147,17 +147,18 @@ static void apply_window_updates(
 			/* stick and unstick the window to force the icon on
 			 * the current page */
 			handle_stick(
-				NULL, exc, "",
+				NULL, exc, "", NULL,
 				S_IS_STICKY_ACROSS_PAGES(SCF(*pstyle)),
 				S_IS_STICKY_ACROSS_DESKS(SCF(*pstyle)), 1, 1);
-			handle_stick(NULL, exc, "", 0, 0, 1, 0);
+			handle_stick(NULL, exc, "", NULL, 0, 0, 1, 0);
 		}
 		flags->do_update_icon_title = True;
 	}
 	else if (flags->do_update_stick)
 	{
 		handle_stick(
-			NULL, exc, "", S_IS_STICKY_ACROSS_PAGES(SCF(*pstyle)),
+			NULL, exc, "", NULL,
+			S_IS_STICKY_ACROSS_PAGES(SCF(*pstyle)),
 			S_IS_STICKY_ACROSS_DESKS(SCF(*pstyle)), 0, 0);
 	}
 	exc_destroy_context(exc);
diff --git a/fvwm/virtual.c b/fvwm/virtual.c
index cfe1ec17..27f728cb 100644
--- a/fvwm/virtual.c
+++ b/fvwm/virtual.c
@@ -28,6 +28,7 @@
 #include "libs/FScreen.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "execcontext.h"
 #include "expand.h"
 #include "cursor.h"
@@ -64,7 +65,8 @@
 	do {								    \
 		char	*cmd;						    \
 		xasprintf(&cmd, "GotoDesk %s 0 %d", (m)->si->name, (d));    \
-		execute_function_override_window(NULL, NULL, cmd, 0, NULL); \
+		execute_function_override_window(			    \
+			NULL, NULL, cmd, NULL, 0, NULL);		    \
 		free(cmd);						    \
 	} while (0)

@@ -74,7 +76,8 @@
 		xasprintf(&cmd, "All (!Screen %s, Desk %d, !CirculateHit) " \
 		    "MoveToPage %s $[w.pagex] $[w.pagey]",		    \
 		    (m)->si->name, (d), (m)->si->name);			    \
-		execute_function_override_window(NULL, NULL, cmd, 0, NULL); \
+		execute_function_override_window(			    \
+			NULL, NULL, cmd, NULL, 0, NULL);		    \
 		free(cmd);                                                  \
 	} while (0)

@@ -628,8 +631,8 @@ static void MapDesk(struct monitor *m, int desk, Bool grab)
 					/* execute_function_override_window()
 					 * will expand cmd's variables.
 					 */
-					execute_function_override_window(NULL,
-					    NULL, cmd, 0, t);
+					execute_function_override_window(
+						NULL, NULL, cmd, NULL, 0, t);
 					free(cmd);

 					/* No need to map the window as it's
@@ -2204,13 +2207,13 @@ void CMD_EdgeResistance(F_CMD_ARGS)
 			   " instead:\n%s\n%s\n%s\n", cmd, stylecmd,
 			   stylecmd2);
 		execute_function(
-			cond_rc, exc, cmd,
+			cond_rc, exc, cmd, pc,
 			FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
 		execute_function(
-			cond_rc, exc, stylecmd,
+			cond_rc, exc, stylecmd, pc,
 			FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
 		execute_function(
-			cond_rc, exc, stylecmd2,
+			cond_rc, exc, stylecmd2, pc,
 			FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
 	}
 	else
@@ -2249,7 +2252,8 @@ void CMD_DesktopConfiguration(F_CMD_ARGS)
 				m->virtual_scr.Vx / monitor_get_all_widths(),
 				m->virtual_scr.Vy / monitor_get_all_heights());

-			execute_function_override_window(NULL, NULL, cmd, 0, NULL);
+			execute_function_override_window(
+				NULL, NULL, cmd, NULL, 0, NULL);
 			free(cmd);
 		}
 		monitor_mode = MONITOR_TRACKING_G;
@@ -2660,8 +2664,8 @@ void CMD_MoveToDesk(F_CMD_ARGS)
 				xasprintf(&cmd,
 				    "MoveToPage %s $[w.pagex] $[w.pagey]",
 				    m->si->name);
-				execute_function_override_window(NULL, NULL,
-				    cmd, 0, fw);
+				execute_function_override_window(
+					NULL, NULL, cmd, NULL, 0, fw);
 				free(cmd);

 				return;
diff --git a/fvwm/windowlist.c b/fvwm/windowlist.c
index f77cf675..d4be0dba 100644
--- a/fvwm/windowlist.c
+++ b/fvwm/windowlist.c
@@ -30,6 +30,7 @@
 #include "libs/Strings.h"
 #include "fvwm.h"
 #include "externs.h"
+#include "cmdparser.h"
 #include "functions.h"
 #include "misc.h"
 #include "screen.h"
@@ -1085,7 +1086,7 @@ void CMD_WindowList(F_CMD_ARGS)
 	if (mret.rc == MENU_DOUBLE_CLICKED &&
 	    default_action && *default_action)
 	{
-		execute_function(cond_rc, exc2, default_action, 0);
+		execute_function(cond_rc, exc2, default_action, pc, 0);
 	}
 	if (default_action != NULL)
 	{
--
2.30.2

From 41ba4f2b2dd1fd86f118e6922bd00916a0898790 Mon Sep 17 00:00:00 2001
From: Dominik Vogt <dominik.v...@gmx.de>
Date: Wed, 17 Nov 2021 19:53:39 +0100
Subject: [PATCH 2/2] !!!debug fprintfs

---
 fvwm/cmdparser_old.c          | 24 +++++++++
 fvwm/functions.c              | 95 +++++++++++++++++++++++++++++++++--
 fvwm/modconf.c                | 17 ++++++-
 modules/FvwmButtons/dynamic.c | 23 +++++++++
 4 files changed, 153 insertions(+), 6 deletions(-)

diff --git a/fvwm/cmdparser_old.c b/fvwm/cmdparser_old.c
index 11636030..6d01682d 100644
--- a/fvwm/cmdparser_old.c
+++ b/fvwm/cmdparser_old.c
@@ -274,12 +274,21 @@ static const char *ocp_parse_command_name(
 	cmdparser_context_t *c, void *func_rc, const void *exc)
 {
 	GetNextToken(c->cline, &c->command);
+#if 1 /*!!!*/
+fprintf(stderr, "%s: c->command '%s'\n", __func__, (c->command) ? c->command : "(nil)");
+#endif
 	if (c->command != NULL)
 	{
 		char *tmp = c->command;

+#if 1 /*!!!*/
+fprintf(stderr, "%s: expand c->command\n", __func__);
+#endif
 		c->command = expand_vars(
 			c->command, c, False, False, func_rc, exc);
+#if 1 /*!!!*/
+fprintf(stderr, "%s: c->command '%s'\n", __func__, (c->command) ? c->command : "(nil)");
+#endif
 		free(tmp);
 	}
 	if (c->command && !ocp_is_module_config(c))
@@ -292,11 +301,17 @@ static const char *ocp_parse_command_name(
 		 * fail */
 		char *tmp = c->command;

+#if 1 /*!!!*/
+fprintf(stderr, "%s: remove trailing spaces\n", __func__);
+#endif
 		while (*tmp && !isspace(*tmp))
 		{
 			tmp++;
 		}
 		*tmp = 0;
+#if 1 /*!!!*/
+fprintf(stderr, "%s: c->command '%s'\n", __func__, (c->command) ? c->command : "(nil)");
+#endif
 #endif
 		return c->command;
 	}
@@ -330,6 +345,9 @@ static cmdparser_execute_type_t ocp_find_something_to_execute(
 {
 	int is_function_builtin;

+#if 1 /*!!!*/
+fprintf(stderr, "%s: c->command '%s'\n", __func__, (c->command) ? c->command : "(nil)");
+#endif
 	*ret_complex_function = NULL;
 	/* Note: the module config command, "*" can not be handled by the
 	 * regular command table because there is no required white space after
@@ -380,8 +398,14 @@ static cmdparser_execute_type_t ocp_find_something_to_execute(
 		{
 			break;
 		}
+#if 1 /*!!!*/
+fprintf(stderr, "%s: lookup cf '%s'\n", __func__, complex_function_name);
+#endif
 		*ret_complex_function =
 			find_complex_function(complex_function_name);
+#if 1 /*!!!*/
+fprintf(stderr, "%s: lookup cf '%s' -> %p\n", __func__, complex_function_name, *ret_complex_function);
+#endif
 		if (*ret_complex_function != NULL)
 		{
 			c->cline = rest_of_line;
diff --git a/fvwm/functions.c b/fvwm/functions.c
index 571251ec..31fafa2a 100644
--- a/fvwm/functions.c
+++ b/fvwm/functions.c
@@ -125,21 +125,36 @@ static Bool DeferExecution(
 	original_w = w;
 	wcontext = ret_ecc->w.wcontext;
 	FinishEvent = ((fw != NULL) ? ButtonRelease : ButtonPress);
+#if 1 /*!!!*/
+	fprintf(stderr, "!!!%s: A wc 0x%x\n", __func__, wcontext);
+#endif
 	if (wcontext == C_UNMANAGED && do_allow_unmanaged)
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Bf\n", __func__);
+#endif
 		return False;
 	}
 	if (wcontext != C_ROOT && wcontext != C_NO_CONTEXT && fw != NULL &&
 	    wcontext != C_EWMH_DESKTOP)
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: C\n", __func__);
+#endif
 		if (FinishEvent == ButtonPress ||
 		    (FinishEvent == ButtonRelease &&
 		     trigger_evtype != ButtonPress))
 		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Df\n", __func__);
+#endif
 			return False;
 		}
 		else if (FinishEvent == ButtonRelease)
 		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: E\n", __func__);
+#endif
 			/* We are only waiting until the user releases the
 			 * button. Do not change the cursor. */
 			cursor = CRS_NONE;
@@ -148,16 +163,25 @@ static Bool DeferExecution(
 	}
 	if (Scr.flags.are_functions_silent)
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Ft\n", __func__);
+#endif
 		return True;
 	}
 	if (!GrabEm(cursor, GRAB_NORMAL))
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Gt\n", __func__);
+#endif
 		XBell(dpy, 0);
 		return True;
 	}
 	MyXGrabKeyboard(dpy);
 	while (!finished)
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: H\n", __func__);
+#endif
 		done = 0;
 		/* block until there is an event */
 		FMaskEvent(
@@ -168,9 +192,15 @@ static Bool DeferExecution(

 		if (e.type == KeyPress)
 		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: I\n", __func__);
+#endif
 			KeySym keysym = XLookupKeysym(&e.xkey, 0);
 			if (keysym == XK_Escape)
 			{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Jt\n", __func__);
+#endif
 				ret_ecc->x.etrigger = &e;
 				*ret_mask |= ECC_ETRIGGER;
 				UngrabEm(GRAB_NORMAL);
@@ -181,33 +211,57 @@ static Bool DeferExecution(
 		}
 		if (e.type == FinishEvent)
 		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: K\n", __func__);
+#endif
 			finished = 1;
 		}
 		switch (e.type)
 		{
 		case KeyPress:
 		case ButtonPress:
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: L\n", __func__);
+#endif
 			if (e.type != FinishEvent)
 			{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: M\n", __func__);
+#endif
 				original_w = e.xany.window;
 			}
 			done = 1;
 			break;
 		case ButtonRelease:
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: N\n", __func__);
+#endif
 			done = 1;
 			break;
 		default:
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: O\n", __func__);
+#endif
 			break;
 		}
 		if (!done)
 		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: P\n", __func__);
+#endif
 			dispatch_event(&e);
 		}
 	}
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Q\n", __func__);
+#endif
 	MyXUngrabKeyboard(dpy);
 	UngrabEm(GRAB_NORMAL);
 	if (just_waiting_for_finish)
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Rf\n", __func__);
+#endif
 		return False;
 	}
 	w = e.xany.window;
@@ -216,11 +270,17 @@ static Bool DeferExecution(
 	if ((w == Scr.Root || w == Scr.NoFocusWin) &&
 	    e.xbutton.subwindow != None)
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: S\n", __func__);
+#endif
 		w = e.xbutton.subwindow;
 		e.xany.window = w;
 	}
 	if (w == Scr.Root || IS_EWMH_DESKTOP(w))
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Tt\n", __func__);
+#endif
 		ret_ecc->w.w = w;
 		ret_ecc->w.wcontext = C_ROOT;
 		XBell(dpy, 0);
@@ -229,6 +289,9 @@ static Bool DeferExecution(
 	*ret_mask |= ECC_FW;
 	if (XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Ut\n", __func__);
+#endif
 		ret_ecc->w.fw = NULL;
 		ret_ecc->w.w = w;
 		ret_ecc->w.wcontext = C_ROOT;
@@ -237,10 +300,16 @@ static Bool DeferExecution(
 	}
 	if (w == FW_W_PARENT(fw))
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: V\n", __func__);
+#endif
 		w = FW_W(fw);
 	}
 	if (original_w == FW_W_PARENT(fw))
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: W\n", __func__);
+#endif
 		original_w = FW_W(fw);
 	}
 	/* this ugly mess attempts to ensure that the release and press
@@ -249,8 +318,14 @@ static Bool DeferExecution(
 	    original_w != None && original_w != Scr.NoFocusWin &&
 	    !IS_EWMH_DESKTOP(original_w))
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: X\n", __func__);
+#endif
 		if (w != FW_W_FRAME(fw) || original_w != FW_W(fw))
 		{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Yt\n", __func__);
+#endif
 			ret_ecc->w.fw = fw;
 			ret_ecc->w.w = w;
 			ret_ecc->w.wcontext = C_ROOT;
@@ -261,12 +336,18 @@ static Bool DeferExecution(

 	if (IS_EWMH_DESKTOP(FW_W(fw)))
 	{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: Zt\n", __func__);
+#endif
 		ret_ecc->w.fw = fw;
 		ret_ecc->w.w = w;
 		ret_ecc->w.wcontext = C_ROOT;
 		XBell(dpy, 0);
 		return True;
 	}
+#if 1 /*!!!*/
+fprintf(stderr, "!!!%s: @f\n", __func__);
+#endif
 	wcontext = GetContext(NULL, fw, &e, &dummy);
 	ret_ecc->w.fw = fw;
 	ret_ecc->w.w = w;
@@ -297,9 +378,6 @@ static void __execute_command_line(
 	Window dummy_w;
 	int rc;

-#if 1 /*!!!*/
-	fprintf(stderr, "%s: cpc %p, xaction '%s'\n", __func__, caller_pc, xaction);
-#endif
 	set_silent = 0;
 	/* generate a parsing context; this *must* be destroyed before
 	 * returning */
@@ -359,10 +437,12 @@ cmdparser_hooks->debug(&pc, "!!!C");
 	{
 		if (exec_flags & FUNC_IS_UNMANAGED)
 		{
+fprintf(stderr, "!!!AAA unmanaged\n");
 			w = exc->w.w;
 		}
 		else
 		{
+fprintf(stderr, "!!!BBB root\n");
 			w = Scr.Root;
 		}
 	}
@@ -407,11 +487,12 @@ cmdparser_hooks->debug(&pc, "!!!C");
 		{
 cmdparser_hooks->debug(&pc, "!!!D");
 			bif = find_builtin_function(func);
+fprintf(stderr, "!!! --> find bif '%s' -> %p\n", func, bif);
 			err_func = func;
 		}
 		else
 		{
-cmdparser_hooks->debug(&pc, "!!!E");
+cmdparser_hooks->debug(&pc, "!!!E no command name");
 			bif = NULL;
 			err_func = "";
 		}
@@ -466,6 +547,7 @@ cmdparser_hooks->debug(&pc, "!!!J");
 #endif
 #if 1 /*!!!*/
 fprintf(stderr, "!!!pc.cline: '%s'\n", pc.cline);
+fprintf(stderr, "!!!bif: '%s' 0x%x\n", bif->keyword, bif->flags);
 #endif
 		mask = (w != exc->w.w) ? ECC_W : 0;
 		ecc.w.fw = exc->w.fw;
@@ -482,6 +564,9 @@ fprintf(stderr, "!!!pc.cline: '%s'\n", pc.cline);
 				(bif->flags & FUNC_ALLOW_UNMANAGED));
 			if (rc == True)
 			{
+#if 1 /*!!!*/
+fprintf(stderr, "!!!not deferred: %d\n", rc);
+#endif
 				break;
 			}
 #if 1 /*!!!*/
@@ -512,7 +597,7 @@ fprintf(stderr, "!!!erase PressedW\n");
 			PressedW = None;
 		}
 #if 1 /*!!!*/
-fprintf(stderr, "!!!execute '%s'\n", bif->keyword);
+fprintf(stderr, "!!!execute '%s' (fw %p)\n", bif->keyword, exc2->w.fw);
 #endif
 		bif->action(func_rc, exc2, pc.cline, &pc);
 		PressedW = dummy_w;
diff --git a/fvwm/modconf.c b/fvwm/modconf.c
index 8726df83..ab17b9d8 100644
--- a/fvwm/modconf.c
+++ b/fvwm/modconf.c
@@ -90,6 +90,9 @@ void ModuleConfig(char *action)
 	fmodule *module;
 	struct moduleInfoList *new_entry;

+#if 1 /*!!!*/
+fprintf(stderr, "%s: action '%s'\n", __func__, (action) ? action : "(nil)");
+#endif
 	end = strlen(action) - 1;
 	if (action[end] == '\n')
 		action[end] = '\0';
@@ -143,8 +146,16 @@ static struct moduleInfoList *AddToModList(char *tline)
 	{
 		/* migo (01-Sep-2000): construct an old-style config line */
 		char *conf_start = alias_end + 1;
+#if 1 /*!!!*/
+fprintf(stderr, "%s: conf_start '%s'\n", __func__, conf_start);
+#endif
 		while (isspace(*conf_start)) conf_start++;
 		*alias_end = '\0';
+#if 1 /*!!!*/
+fprintf(stderr, "%s: alias '%s'\n", __func__, tline ? tline : "(nil)");
+fprintf(stderr, "%s: conf_start '%s'\n", __func__, conf_start);
+#endif
+
 		xasprintf(&rline, "%s%s", tline, conf_start);
 		*alias_end = MODULE_CONFIG_DELIM;
 		this->alias_len = alias_end - tline;
@@ -152,9 +163,13 @@ static struct moduleInfoList *AddToModList(char *tline)

 	exc = exc_create_null_context();
 	this->data = expand_vars(rline, NULL, False, True, NULL, exc);
+#if 1 /*!!!*/
+fprintf(stderr, "%s: mldata '%s', mlaliaslen %d\n", __func__, this->data, this->alias_len);
+#endif
 	exc_destroy_context(exc);
 	/* Free rline only if it is xasprintf'd memory (not pointing at tline
-	 * anymore). If we free our tline argument it causes a crash in __execute_function. */
+	 * anymore). If we free our tline argument it causes a crash in
+	 * __execute_function. */
 	if (rline != tline)
 		free(rline);

diff --git a/modules/FvwmButtons/dynamic.c b/modules/FvwmButtons/dynamic.c
index 2739e291..e6d53c8b 100644
--- a/modules/FvwmButtons/dynamic.c
+++ b/modules/FvwmButtons/dynamic.c
@@ -242,6 +242,9 @@ void parse_message_line(char *line)
 		return;
 	}

+#if 1 /*!!!*/
+fprintf(stderr, "action: '%d'\n", action);
+#endif
 	switch (action)
 	{
 	case 1:
@@ -259,18 +262,32 @@ void parse_message_line(char *line)
 			char *value0, *value;
 			FvwmPicture *icon;

+#if 1 /*!!!*/
+fprintf(stderr, "loop: rest: '%s'\n", rest);
+#endif
 			/* parse option and value and give diagnostics */
 			rest = GetQuotedString(
 				rest, &option_pair, ",", NULL, NULL, NULL);
+#if 1 /*!!!*/
+fprintf(stderr, "l2  : rest: '%s'\n", rest);
+fprintf(stderr, "l2  : opt : '%s'\n", option_pair);
+#endif
 			while (isspace(*rest))
 			{
 				rest++;
 			}
+#if 1 /*!!!*/
+fprintf(stderr, "l3  : rest: '%s'\n", rest);
+#endif
 			if (!option_pair)
 				continue;

 			option = GetTokenIndex(
 				option_pair, button_options, -1, &value0);
+#if 1 /*!!!*/
+fprintf(stderr, "4   : opt : '%d'\n", option);
+fprintf(stderr, "4   : v0  : '%s'\n", value0);
+#endif
 			if (option < 0)
 			{
 				show_error(
@@ -281,6 +298,9 @@ void parse_message_line(char *line)
 			}

 			GetNextToken(value0, &value);
+#if 1 /*!!!*/
+fprintf(stderr, "5   : tok : '%s'\n", value);
+#endif
 			free(option_pair);

 			if (value == NULL)
@@ -306,6 +326,9 @@ void parse_message_line(char *line)
 				b->flags.b_ActiveTitle = 1;
 				b->title = value;
 				value = NULL;
+#if 1 /*!!!*/
+fprintf(stderr, "6   : copy: '%s'\n", b->title);
+#endif
 				break;
 			case 4:
 				/* PressTitle */
--
2.30.2

Reply via email to