From e2d0030bca4d26d7016d18181be9c399618b2f54 Mon Sep 17 00:00:00 2001
From: Christophe CURIS <[email protected]>
Date: Sat, 23 Jun 2012 15:21:43 +0200
Subject: [PATCH 5/7] Remove dependency to CPP: added pre-defined macros

A number of macros are pre-defined by WindowMaker for CPP in the
function 'MakeCPPArgs', they are now available in the internal
parser too.
CPP also had some predefined macros, the most useful ones have been
added too.
---
 src/rootmenu.h       |    1 +
 src/rootmenumacros.c |  222 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/rootmenuparser.c |    3 +
 3 files changed, 226 insertions(+)

diff --git a/src/rootmenu.h b/src/rootmenu.h
index f69657f..6df6c4b 100644
--- a/src/rootmenu.h
+++ b/src/rootmenu.h
@@ -76,6 +76,7 @@ struct parser_macro {
 
 Bool menu_parser_skip_spaces_and_comments(WMenuParser parser);
 
+void menu_parser_create_preset_macros(WMenuParser parser);
 void menu_parser_define_macro(WMenuParser parser);
 void menu_parser_delete_macros(WMenuParser parser);
 
diff --git a/src/rootmenumacros.c b/src/rootmenumacros.c
index 4f2d5aa..a514b9f 100644
--- a/src/rootmenumacros.c
+++ b/src/rootmenumacros.c
@@ -23,6 +23,11 @@
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <X11/Xlib.h>
+#include <WINGs/WUtil.h>
 
 #define MENU_PARSER_INTERNALS
 #include "WindowMaker.h"
@@ -32,6 +37,7 @@
  This file contains the functions related to macros:
   - parse a macro being defined
   - handle single macro expansion
+  - pre-defined parser's macros
 
  Some design notes for macro internal storage:
 
@@ -56,6 +62,21 @@
   This structure allows to store any number and combination
    of text/parameter and still provide very fast generation
    at macro replacement time.
+
+ Predefined macros are using a call-back function mechanism
+   to generate the value on-demand. This value is generated
+   in the 'value' buffer of the structure.
+ Most of these call-backs will actually cache the value:
+   they generate it on the first use (inside a parser,
+   not globally) and reuse that value on next call(s).
+
+ Because none of these macros take parameters, the call-back
+   mechanism does not include passing of user arguments; the
+   complex storage mechanism for argument replacement being
+   not necessary the macro->value parameter is used as a
+   plain C string to be copied, this fact being recognised
+   by macro->function being non-null. It was chosen that the
+   call-back function would not have the possibility to fail.
 */
 
 #define MAX_MACRO_ARG_COUNT 32
@@ -514,3 +535,204 @@ static Bool menu_parser_read_macro_args(WMenuParser parser, WParserMacro *macro,
 								  macro->name);
 	return True;
 }
+
+/* Name of the originally loaded file (before #includes) */
+static void mpm_base_file(WParserMacro *this, WMenuParser parser)
+{
+	unsigned char *src, *dst;
+
+	if (this->value[0] != '\0') return; // Value already evaluated, re-use previous
+
+	while (parser->parent_file != NULL)
+		parser = parser->parent_file;
+
+	dst = this->value;
+	src = (unsigned char *) parser->file_name;
+	*dst++ = '\"';
+	while (*src != '\0')
+		if (dst < this->value + sizeof(this->value) - 2)
+			*dst++ = *src++;
+		else
+			break;
+	*dst++ = '\"';
+	*dst   = '\0';
+}
+
+/* Number of #include currently nested */
+static void mpm_include_level(WParserMacro *this, WMenuParser parser)
+{
+	int level = 0;
+	while (parser->parent_file != NULL) {
+		parser = parser->parent_file;
+		level++;
+	}
+	snprintf((char *) this->value, sizeof(this->value), "%d", level);
+}
+
+/* Name of current file */
+static void mpm_current_file(WParserMacro *this, WMenuParser parser)
+{
+	unsigned char *src, *dst;
+
+	dst = this->value;
+	src = (unsigned char *) parser->file_name;
+	*dst++ = '\"';
+	while (*src != '\0')
+		if (dst < this->value + sizeof(this->value) - 2)
+			*dst++ = *src++;
+		else
+			break;
+	*dst++ = '\"';
+	*dst   = '\0';
+}
+
+/* Number of current line */
+static void mpm_current_line(WParserMacro *this, WMenuParser parser)
+{
+	snprintf((char *) this->value, sizeof(this->value), "%d", parser->num_line);
+}
+
+/* Name of host on which we are running, not necessarily displaying */
+static void mpm_get_hostname(WParserMacro *this, WMenuParser parser)
+{
+	char *h;
+
+	if (this->value[0] != '\0') return; // Value already evaluated, re-use previous
+
+	h = getenv("HOSTNAME");
+	if (h == NULL) {
+		h = getenv("HOST");
+		if (h == NULL) {
+			if (gethostname((char *) this->value, sizeof(this->value) ) != 0) {
+				menu_parser_warning(parser, _("could not determine %s"), "HOSTNAME");
+				this->value[0] = '?';
+				this->value[1] = '?';
+				this->value[2] = '?';
+				this->value[3] = '\0';
+			}
+			return;
+		}
+	}
+	wstrlcpy((char *) this->value, h, sizeof(this->value) );
+}
+
+/* Name of the current user */
+static void mpm_get_user_name(WParserMacro *this, WMenuParser parser)
+{
+	char *user;
+
+	if (this->value[0] != '\0') return; // Value already evaluated, re-use previous
+
+	user = getlogin();
+	if (user == NULL) {
+		struct passwd *pw_user;
+
+		pw_user = getpwuid(getuid());
+		if (pw_user == NULL) {
+		error_no_username:
+			menu_parser_warning(parser, _("could not determine %s"), "USER" );
+			/* Fall back on numeric id - better than nothing */
+			snprintf((char *) this->value, sizeof(this->value), "%d", getuid() );
+			return;
+		}
+		user = pw_user->pw_name;
+		if (user == NULL) goto error_no_username;
+	}
+	wstrlcpy((char *) this->value, user, sizeof(this->value) );
+}
+
+/* Number id of the user under which we are running */
+static void mpm_get_user_id(WParserMacro *this, WMenuParser parser)
+{
+	if (this->value[0] != '\0') return; // Already evaluated, re-use previous
+	snprintf((char *) this->value, sizeof(this->value), "%d", getuid() );
+}
+
+/* Name of the display to which we are connected */
+static void mpm_get_xdisplay(WParserMacro *this, WMenuParser parser)
+{
+	char *disp;
+
+	if (this->value[0] != '\0') return; // Already evaluated, re-use previous
+	disp = XDisplayName(DisplayString(dpy));
+	wstrlcpy((char *) this->value, disp, sizeof(this->value) );
+}
+
+/* Version of WindowMaker (which is now also the parser's version) */
+static void mpm_get_version(WParserMacro *this, WMenuParser parser)
+{
+	if (this->value[0] != '\0') return; // Already evaluated, re-use previous
+	wstrlcpy((char *) this->value, "\"" VERSION "\"", sizeof(this->value) );
+}
+
+/* Number id of the current visual */
+static void mpm_get_xvisual(WParserMacro *this, WMenuParser parser)
+{
+	Visual *visual;
+
+	if (this->value[0] != '\0') return; // Already evaluated, re-use previous
+	visual = DefaultVisual(dpy, DefaultScreen(dpy));
+	snprintf((char *) this->value, sizeof(this->value), "%d", visual->class);
+}
+
+/* Depth of current screen */
+static void mpm_get_xdepth(WParserMacro *this, WMenuParser parser)
+{
+	if (this->value[0] != '\0') return; // Already evaluated, re-use previous
+	snprintf((char *) this->value, sizeof(this->value), "%d",
+				DefaultDepth(dpy, DefaultScreen(dpy)) );
+}
+
+/* Width and Height of current screen */
+static void mpm_get_screen_width(WParserMacro *this, WMenuParser parser)
+{
+	if (this->value[0] != '\0') return; // Already evaluated, re-use previous
+	snprintf((char *) this->value, sizeof(this->value), "%d",
+				WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
+}
+
+static void mpm_get_screen_height(WParserMacro *this, WMenuParser parser)
+{
+	if (this->value[0] != '\0') return; // Already evaluated, re-use previous
+	snprintf((char *) this->value, sizeof(this->value), "%d",
+				HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
+}
+
+/* Small helper to automate creation of one pre-defined macro in the parser */
+static void create_macro(WMenuParser parser, const char *name, WParserMacroFunction *handler)
+{
+	WParserMacro *macro;
+
+	macro = wmalloc(sizeof(*macro));
+	strcpy(macro->name, name);
+	macro->function = handler;
+	macro->arg_count = -1;
+	macro->next = parser->macros;
+	parser->macros = macro;
+}
+
+/***** Register all the pre-defined macros in the parser *****/
+void menu_parser_create_preset_macros(WMenuParser parser)
+{
+	/* Defined by CPP: common predefined macros (GNU C extension) */
+	create_macro(parser, "__BASE_FILE__", mpm_base_file);
+	create_macro(parser, "__INCLUDE_LEVEL__", mpm_include_level);
+	create_macro(parser, "__VERSION__", mpm_get_version); // Now returns wmaker's version
+
+	/* Defined by CPP: standard predefined macros */
+	create_macro(parser, "__FILE__", mpm_current_file);
+	create_macro(parser, "__LINE__", mpm_current_line);
+	// create_macro(parser, "__DATE__", NULL);  [will be implemented per user request]
+	// create_macro(parser, "__TIME__", NULL);  [will be implemented per user request]
+
+	/* Historically defined by WindowMaker */
+	create_macro(parser, "VISUAL", mpm_get_xvisual);
+	create_macro(parser, "DEPTH", mpm_get_xdepth);
+	create_macro(parser, "SCR_WIDTH", mpm_get_screen_width);
+	create_macro(parser, "SCR_HEIGHT", mpm_get_screen_height);
+	create_macro(parser, "HOST", mpm_get_hostname);
+	create_macro(parser, "UID", mpm_get_user_id);
+	create_macro(parser, "USER", mpm_get_user_name);
+	create_macro(parser, "DISPLAY", mpm_get_xdisplay);
+	create_macro(parser, "WM_VERSION", mpm_get_version);
+}
diff --git a/src/rootmenuparser.c b/src/rootmenuparser.c
index acd7d03..5478bf7 100644
--- a/src/rootmenuparser.c
+++ b/src/rootmenuparser.c
@@ -112,6 +112,9 @@ Bool menu_parser_get_line(WMenuParser top_parser, char **title, char **command,
 	*shortcut = NULL;
 	scanmode = GET_TITLE;
 
+	if (top_parser->macros == NULL)
+		menu_parser_create_preset_macros(top_parser);
+
  read_next_line_with_filechange:
 	cur_parser = top_parser;
 	while (cur_parser->include_file)
-- 
1.7.10

Reply via email to