diff --git a/WINGs/WINGs/WUtil.h b/WINGs/WINGs/WUtil.h index 0d3343c1..30476e09 100644 --- a/WINGs/WINGs/WUtil.h +++ b/WINGs/WINGs/WUtil.h @@ -874,6 +874,8 @@ typedef struct w_menu_parser *WMenuParser; WMenuParser WMenuParserCreate(const char *file_name, void *file, const char *include_default_paths); +void WMenuParserRegisterSimpleMacro(WMenuParser parser, const char *name, const char *value); + void WMenuParserError(WMenuParser parser, const char *msg, ...) __attribute__ ((format (printf, 2, 3))); diff --git a/WINGs/menuparser.c b/WINGs/menuparser.c index b39b3b17..6c74144d 100644 --- a/WINGs/menuparser.c +++ b/WINGs/menuparser.c @@ -42,6 +42,7 @@ WMenuParser WMenuParserCreate(const char *file_name, void *file, WMenuParser parser; parser = menu_parser_create_new(file_name, file, include_default_paths); + menu_parser_register_preset_macros(parser); return parser; } diff --git a/WINGs/menuparser.h b/WINGs/menuparser.h index e7d101d1..4407fc91 100644 --- a/WINGs/menuparser.h +++ b/WINGs/menuparser.h @@ -61,6 +61,8 @@ struct w_parser_macro { Bool menu_parser_skip_spaces_and_comments(WMenuParser parser); +void menu_parser_register_preset_macros(WMenuParser parser); + void menu_parser_define_macro(WMenuParser parser); void menu_parser_free_macros(WMenuParser parser); diff --git a/WINGs/menuparser_macros.c b/WINGs/menuparser_macros.c index 05cb9c78..12e75480 100644 --- a/WINGs/menuparser_macros.c +++ b/WINGs/menuparser_macros.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include @@ -32,6 +34,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 +59,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. */ static Bool menu_parser_read_macro_def(WMenuParser parser, WParserMacro *macro, char **argname); @@ -512,3 +530,175 @@ static Bool menu_parser_read_macro_args(WMenuParser parser, WParserMacro *macro, macro->name); return True; } + +/******************************************************************************/ +/* Definition of pre-defined macros */ +/******************************************************************************/ + +void WMenuParserRegisterSimpleMacro(WMenuParser parser, const char *name, const char *value) +{ + WParserMacro *macro; + size_t len; + unsigned char *wr; + + macro = wmalloc(sizeof(*macro)); + strncpy(macro->name, name, sizeof(macro->name)-1); + macro->arg_count = -1; + len = strlen(value); + if (len > sizeof(macro->value) - 3) { + wwarning(_("size of value for macro '%s' is too big, truncated"), name); + len = sizeof(macro->value) - 3; + } + macro->value[0] = (len >> 8) & 0xFF; + macro->value[1] = len & 0xFF; + wr = ¯o->value[2]; + while (len-- > 0) + *wr++ = *value++; + *wr = 0xFF; + macro->next = parser->macros; + parser->macros = macro; +} + +/* 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->line_number); +} + +/* 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) { + WMenuParserError(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: + WMenuParserError(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() ); +} + +/* Small helper to automate creation of one pre-defined macro in the parser */ +static void w_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_register_preset_macros(WMenuParser parser) +{ + /* Defined by CPP: common predefined macros (GNU C extension) */ + w_create_macro(parser, "__BASE_FILE__", mpm_base_file); + w_create_macro(parser, "__INCLUDE_LEVEL__", mpm_include_level); + + /* Defined by CPP: standard predefined macros */ + w_create_macro(parser, "__FILE__", mpm_current_file); + w_create_macro(parser, "__LINE__", mpm_current_line); + // w_create_macro(parser, "__DATE__", NULL); [will be implemented only per user request] + // w_create_macro(parser, "__TIME__", NULL); [will be implemented only per user request] + + /* Historically defined by WindowMaker */ + w_create_macro(parser, "HOST", mpm_get_hostname); + w_create_macro(parser, "UID", mpm_get_user_id); + w_create_macro(parser, "USER", mpm_get_user_name); +} diff --git a/src/rootmenu.c b/src/rootmenu.c index 165ab92c..7e1b2ea0 100644 --- a/src/rootmenu.c +++ b/src/rootmenu.c @@ -65,6 +65,7 @@ static WMenu *readMenuPipe(WScreen * scr, char **file_name); static WMenu *readMenuFile(WScreen * scr, char *file_name); static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command); static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals); +static void menu_parser_register_macros(WMenuParser parser); typedef struct Shortcut { struct Shortcut *next; @@ -970,6 +971,7 @@ static WMenu *readMenuFile(WScreen * scr, char *file_name) } } parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS); + menu_parser_register_macros(parser); while (WMenuParserGetLine(parser, &title, &command, ¶ms, &shortcut)) { @@ -1061,6 +1063,7 @@ static WMenu *readMenuPipe(WScreen * scr, char **file_name) } } parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS); + menu_parser_register_macros(parser); while (WMenuParserGetLine(parser, &title, &command, ¶ms, &shortcut)) { @@ -1106,6 +1109,34 @@ static int myCompare(const void *d1, const void *d2) return strcmp(p1->name, p2->name); } +/***** Preset some macro for file parser *****/ +static void menu_parser_register_macros(WMenuParser parser) +{ + Visual *visual; + char buf[32]; + + // Used to return CPP verion, now returns wmaker's version + WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION); + + // All macros below were historically defined by WindowMaker + visual = DefaultVisual(dpy, DefaultScreen(dpy)); + snprintf(buf, sizeof(buf), "%d", visual->class); + WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf); + + snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) ); + WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf); + + snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) ); + WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf); + + snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) ); + WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf); + + WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) ); + + WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\""); +} + /************ Menu Configuration From Directory *************/ static Bool isFilePackage(char *file)