Update Serbian translation from master branch
[wmaker-crm.git] / WINGs / menuparser_macros.c
blob8c12f2de8dddde39972de1fb12c47f46fc7ee6e6
1 /*
2 * Window Maker window manager
4 * Copyright (c) 2012 Christophe Curis
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "wconfig.h"
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <unistd.h>
27 #include <pwd.h>
29 #include <WINGs/WUtil.h>
31 #include "menuparser.h"
34 This file contains the functions related to macros:
35 - parse a macro being defined
36 - handle single macro expansion
37 - pre-defined parser's macros
39 Some design notes for macro internal storage:
41 arg_count is -1 when the macro does not take arguments
42 but 0 when no args but still using parenthesis. The
43 difference is explained in GNU cpp's documentation:
44 http://gcc.gnu.org/onlinedocs/cpp/Function_002dlike-Macros.html
46 the value is stored for fast expansion; here is an
47 example of the storage format used:
48 #define EXAMPLE(a, b) "text:" a and b!
49 will be stored in macro->value[] as:
50 0x0000: 0x00 0x07 (strlen(part 1))
51 0x0002: '"', 't', 'e', 'x', 't', ':', '"' (first part)
52 0x0009: 0x00 (part 2, id=0 for replacement by 1st parameter 'a')
53 0x000A: 0x00 0x03 (strlen(part 3))
54 0x000C: 'a', 'n', 'd' (part 3)
55 0x000F: 0x01 (part 4, id=1 for replacement by 2nd parameter 'b')
56 0x0010: 0x00 0x01 (strlen(part 5))
57 0x0012: '!' (part 5)
58 0x0013: 0xFF (end of macro)
59 This structure allows to store any number and combination
60 of text/parameter and still provide very fast generation
61 at macro replacement time.
63 Predefined macros are using a call-back function mechanism
64 to generate the value on-demand. This value is generated
65 in the 'value' buffer of the structure.
66 Most of these call-backs will actually cache the value:
67 they generate it on the first use (inside a parser,
68 not globally) and reuse that value on next call(s).
70 Because none of these macros take parameters, the call-back
71 mechanism does not include passing of user arguments; the
72 complex storage mechanism for argument replacement being
73 not necessary the macro->value parameter is used as a
74 plain C string to be copied, this fact being recognised
75 by macro->function being non-null. It was chosen that the
76 call-back function would not have the possibility to fail.
79 static Bool menu_parser_read_macro_def(WMenuParser parser, WParserMacro *macro, char **argname);
81 static Bool menu_parser_read_macro_args(WMenuParser parser, WParserMacro *macro,
82 char *array[], char *buffer, ssize_t buffer_size);
84 /* Free all used memory associated with parser's macros */
85 void menu_parser_free_macros(WMenuParser parser)
87 WParserMacro *macro, *mnext;
88 #ifdef DEBUG
89 unsigned char *rd;
90 unsigned int size;
91 unsigned int count;
93 /* if we were compiled with debugging, we take the opportunity that we
94 parse the list of macros, for memory release, to print all the
95 definitions */
96 printf(__FILE__ ": Macros defined while parsing \"%s\"\n", parser->file_name);
97 count = 0;
98 #endif
99 for (macro = parser->macros; macro != NULL; macro = mnext) {
100 #ifdef DEBUG
101 printf(" %s", macro->name);
102 if (macro->arg_count >= 0)
103 printf("(args=%d)", macro->arg_count);
104 printf(" = ");
106 if (macro->function != NULL) {
107 macro->function(macro, parser);
108 printf("function:\"%s\"", macro->value);
109 } else {
110 rd = macro->value;
111 for (;;) {
112 putchar('"');
113 size = (*rd++) << 8;
114 size |= *rd++;
115 while (size-- > 0) putchar(*rd++);
116 putchar('"');
117 if (*rd == 0xFF) break;
118 printf(" #%d ", (*rd++) + 1);
121 printf(", used %d times\n", macro->usage_count);
122 count++;
123 #endif
124 mnext = macro->next;
125 wfree(macro);
127 #ifdef DEBUG
128 printf(__FILE__ ": %d macros\n", count);
129 #endif
130 parser->macros = NULL; // Security
133 /* Check wether the specified character is valid for a name (macro, parameter) or not */
134 int isnamechr(char ch)
136 static const int table[256] = {
137 [0] = 0, // In case we'd fall on buggy compiler, to avoid crash
138 // C99: 6.7.8.21 -> non specified values are initialised to 0
139 ['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1, ['4'] = 1,
140 ['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1,
141 ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1,
142 ['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
143 ['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1,
144 ['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
145 ['Y'] = 1, ['Z'] = 1,
146 ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1,
147 ['g'] = 1, ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1,
148 ['m'] = 1, ['n'] = 1, ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1,
149 ['s'] = 1, ['t'] = 1, ['u'] = 1, ['v'] = 1, ['w'] = 1, ['x'] = 1,
150 ['y'] = 1, ['z'] = 1,
151 ['_'] = 1
152 // We refuse any UTF-8 coded character, or accents in ISO-xxx codepages
154 return table[0x00FF & (unsigned)ch ];
157 /* Parse the definition of the macro and add it to the top-most parser's list */
158 void menu_parser_define_macro(WMenuParser parser)
160 WParserMacro *macro;
161 int idx;
162 char arg_names_buf[MAXLINE];
163 char *arg_name[MAX_MACRO_ARG_COUNT];
165 if (!menu_parser_skip_spaces_and_comments(parser)) {
166 WMenuParserError(parser, _("no macro name found for #define") );
167 return;
169 macro = wmalloc(sizeof(*macro));
170 memset(arg_name, 0, MAX_MACRO_ARG_COUNT * sizeof(char *));
172 /* Isolate name of macro */
173 idx = 0;
174 while (isnamechr(*parser->rd)) {
175 if (idx < sizeof(macro->name) - 1)
176 macro->name[idx++] = *parser->rd;
177 parser->rd++;
179 // macro->name[idx] = '\0'; -> Already present because wmalloc filled struct with 0s
181 /* Build list of expected arguments */
182 if (*parser->rd == '(') {
183 parser->rd++;
184 idx = 0;
185 for (;;) {
186 if (!menu_parser_skip_spaces_and_comments(parser)) {
187 arglist_error_premature_eol:
188 WMenuParserError(parser, _("premature end of file while reading arg-list for macro \"%s\""), macro->name);
189 wfree(macro);
190 return;
192 if (*parser->rd == ')') break;
194 if (macro->arg_count >= wlengthof(arg_name)) {
195 WMenuParserError(parser, _("too many parameters for macro \"%s\" definition"), macro->name);
196 wfree(macro);
197 *parser->rd = '\0'; // fake end-of-line to avoid warnings from remaining line content
198 return;
200 if (isnamechr(*parser->rd)) {
201 arg_name[macro->arg_count++] = arg_names_buf + idx;
202 do {
203 if (idx < sizeof(arg_names_buf) - 1)
204 arg_names_buf[idx++] = *parser->rd;
205 parser->rd++;
206 } while (isnamechr(*parser->rd));
207 arg_names_buf[idx] = '\0';
208 if (idx < sizeof(arg_names_buf) - 1) idx++;
209 } else {
210 WMenuParserError(parser, _("invalid character '%c' in arg-list for macro \"%s\" while expecting parameter name"),
211 *parser->rd, macro->name);
212 wfree(macro);
213 *parser->rd = '\0'; // fake end-of-line to avoid warnings from remaining line content
214 return;
216 if (!menu_parser_skip_spaces_and_comments(parser))
217 goto arglist_error_premature_eol;
218 if (*parser->rd == ')') break;
220 if (*parser->rd != ',') {
221 WMenuParserError(parser, _("invalid character '%c' in arg-list for macro \"%s\" while expecting ',' or ')'"),
222 *parser->rd, macro->name);
223 wfree(macro);
224 *parser->rd = '\0'; // fake end-of-line to avoid warnings from remaining line content
225 return;
227 parser->rd++;
229 parser->rd++; // skip the closing ')'
230 } else
231 macro->arg_count = -1; // Means no parenthesis at all to expect
233 /* If we're inside a #if sequence, we abort now, but not sooner in
234 order to keep the syntax check */
235 if (parser->cond.stack[0].skip) {
236 wfree(macro);
237 *parser->rd = '\0'; // Ignore macro content
238 return;
241 /* Get the macro's definition */
242 menu_parser_skip_spaces_and_comments(parser);
243 if (!menu_parser_read_macro_def(parser, macro, arg_name)) {
244 wfree(macro);
245 return;
248 /* Create the macro in the Root parser */
249 while (parser->parent_file != NULL)
250 parser = parser->parent_file;
252 /* Check that the macro was not already defined */
253 if (menu_parser_find_macro(parser, macro->name) != NULL) {
254 WMenuParserError(parser, _("macro \"%s\" already defined, ignoring redefinition"),
255 macro->name);
256 wfree(macro);
257 return;
260 /* Append at beginning of list */
261 macro->next = parser->macros;
262 parser->macros = macro;
265 /* Check if the current word in the parser matches a macro */
266 WParserMacro *menu_parser_find_macro(WMenuParser parser, const char *name)
268 const char *ref, *cmp;
269 WParserMacro *macro;
271 while (parser->parent_file != NULL)
272 parser = parser->parent_file;
273 for (macro = parser->macros; macro != NULL; macro = macro->next) {
274 ref = macro->name;
275 cmp = name;
276 while (*ref != '\0')
277 if (*ref++ != *cmp++)
278 goto check_next_macro;
279 if (isnamechr(*cmp))
280 continue;
282 return macro;
283 check_next_macro:
286 return NULL;
289 /* look to see if the next word matches the name of one of the parameter
290 names for a macro definition
291 This function is internal to the macro definition function as this is
292 where the analysis is done */
293 static inline char *mp_is_parameter(char *parse, const char *param) {
294 while (*param)
295 if (*parse++ != *param++)
296 return NULL;
297 if (isnamechr(*parse))
298 return NULL;
299 return parse;
302 /* Read the content definition part of a #define construct (the part after the optional
303 argument list) and store it in the prepared format for quick expansion
305 There is no need to keep track of the names of the parameters, so they are stored in
306 a temporary storage for the time of the macro parsing. */
307 static Bool menu_parser_read_macro_def(WMenuParser parser, WParserMacro *macro, char **arg)
309 unsigned char *wr_size;
310 unsigned char *wr;
311 unsigned int size_data;
312 unsigned int size_max;
313 int i;
315 wr_size = macro->value;
316 size_data = 0;
317 wr = wr_size + 2;
318 size_max = sizeof(macro->value) - (wr - macro->value) - 3;
319 while (menu_parser_skip_spaces_and_comments(parser)) {
320 if (isnamechr(*parser->rd)) {
321 char *next_rd;
323 /* Is the current word a parameter to replace? */
324 for (i = 0; i < macro->arg_count; i++) {
325 next_rd = mp_is_parameter(parser->rd, arg[i]);
326 if (next_rd != NULL) {
327 if (wr + 4 >= macro->value + sizeof(macro->value))
328 goto error_too_much_data;
329 wr_size[0] = (size_data >> 8) & 0xFF;
330 wr_size[1] = size_data & 0xFF;
331 *wr++ = i;
332 wr_size = wr;
333 wr += 2;
334 parser->rd = next_rd;
335 *wr++ = ' ';
336 size_data = 1;
337 size_max = sizeof(macro->value) - (wr - macro->value) - 3;
338 goto next_loop; // Because we can't 'break' this loop and 'continue'
339 // the outer one in a clean and easy way
343 /* Not parameter name -> copy as-is */
344 do {
345 *wr++ = *parser->rd++;
346 if (++size_data >= size_max) {
347 error_too_much_data:
348 WMenuParserError(parser, _("more content than supported for the macro \"%s\""),
349 macro->name);
350 return False;
352 } while (isnamechr(*parser->rd));
353 if (isspace(*parser->rd)) {
354 *wr++ = ' ';
355 if (++size_data >= size_max)
356 goto error_too_much_data;
358 } else {
359 /* Some uninterresting characters, copy as-is */
360 while (*parser->rd != '\0') {
361 if (isnamechr(*parser->rd)) break; // handle in next loop
362 if (parser->rd[0] == '/')
363 if ((parser->rd[1] == '*') || (parser->rd[1] == '/'))
364 break; // Comments are handled by std function
365 if ((parser->rd[0] == '\\') &&
366 (parser->rd[1] == '\n') &&
367 (parser->rd[2] == '\0'))
368 break; // Long-lines are handled by std function
369 *wr++ = *parser->rd++;
370 if (++size_data >= size_max)
371 goto error_too_much_data;
374 next_loop:
377 wr_size[0] = (size_data >> 8) & 0xFF;
378 wr_size[1] = size_data & 0xFF;
379 *wr = 0xFF;
380 return True;
383 /* When a macro is being used in the file, this function will generate the
384 expanded value for the macro in the parser's work line.
385 It blindly supposes that the data generated in macro->value is valid */
386 void menu_parser_expand_macro(WMenuParser parser, WParserMacro *macro)
388 char save_buf[sizeof(parser->line_buffer)];
389 char arg_values_buf[MAXLINE];
390 char *arg_value[MAX_MACRO_ARG_COUNT];
391 char *src, *dst;
392 unsigned char *rd;
393 unsigned int size;
394 int i, space_left;
396 memset(arg_value, 0, MAX_MACRO_ARG_COUNT * sizeof(char *));
398 /* Skip the name of the macro, this was not done by caller */
399 for (i = 0; macro->name[i] != '\0'; i++)
400 parser->rd++;
402 if (macro->arg_count >= 0) {
403 menu_parser_skip_spaces_and_comments(parser);
404 if (!menu_parser_read_macro_args(parser, macro, arg_value, arg_values_buf, sizeof(arg_values_buf)))
405 return;
408 #ifdef DEBUG
409 macro->usage_count++;
410 #endif
412 /* Save the remaining data from current line as we will overwrite the
413 current line's workspace with the expanded macro, so we can re-append
414 it afterwards */
415 dst = save_buf;
416 while ((*dst++ = *parser->rd++) != '\0') ;
418 /* Generate expanded macro */
419 dst = parser->line_buffer;
420 parser->rd = dst;
421 space_left = sizeof(parser->line_buffer) - 1;
422 if (macro->function != NULL) {
423 /* Parser's pre-defined macros actually proposes a function call to
424 generate dynamic value for the expansion of the macro. In this case
425 it is generated as a C string in the macro->value and used directly */
426 macro->function(macro, parser);
427 rd = macro->value;
428 while (--space_left > 0)
429 if ((*dst = *rd++) == '\0')
430 break;
431 else
432 dst++;
433 } else {
434 rd = macro->value;
435 for (;;) {
436 size = (*rd++) << 8;
437 size |= *rd++;
438 while (size-- > 0) {
439 *dst = *rd++;
440 if (--space_left > 0) dst++;
442 if (*rd == 0xFF) break;
443 src = arg_value[*rd++];
444 while (*src) {
445 *dst = *src++;
446 if (--space_left > 0) dst++;
451 /* Copy finished -> Re-append the text that was following the macro */
452 src = save_buf;
453 while (--space_left > 0)
454 if ((*dst++ = *src++) == '\0')
455 break;
456 *dst = '\0';
458 if (space_left <= 0)
459 WMenuParserError(parser, _("expansion for macro \"%s\" too big, line truncated"),
460 macro->name);
463 /* When reading a macro to be expanded (not being defined), that takes arguments,
464 this function parses the arguments being provided */
465 static Bool menu_parser_read_macro_args(WMenuParser parser, WParserMacro *macro,
466 char *array[], char *buffer, ssize_t buffer_size)
468 int arg;
470 if (*parser->rd != '(') {
471 WMenuParserError(parser, _("macro \"%s\" needs parenthesis for arguments"),
472 macro->name);
473 return False;
475 parser->rd++;
477 buffer_size--; // Room for final '\0'
478 menu_parser_skip_spaces_and_comments(parser);
479 arg = 0;
480 for (;;) {
481 int paren_count;
483 array[arg] = buffer;
484 paren_count = 0;
485 while (*parser->rd != '\0') {
487 if (*parser->rd == '(')
488 paren_count++;
490 if (paren_count <= 0)
491 if ((*parser->rd == ',') ||
492 (*parser->rd == ')') ) break;
494 if ((*parser->rd == '"') || (*parser->rd == '\'')) {
495 char eot = *parser->rd++;
496 if (buffer_size-- > 0) *buffer++ = eot;
497 while (*parser->rd) {
498 if ((*buffer = *parser->rd++) == eot)
499 goto found_end_of_string;
500 if (buffer_size-- > 0) buffer++;
502 WMenuParserError(parser, _("missing closing quote or double-quote before end-of-line") );
503 return False;
504 found_end_of_string:
505 continue;
508 if (isspace(*parser->rd)) {
509 if (buffer_size-- > 0) *buffer++ = ' ';
510 menu_parser_skip_spaces_and_comments(parser);
511 continue;
514 *buffer = *parser->rd++;
515 if (buffer_size-- > 0) buffer++;
517 *buffer = '\0';
518 if (buffer_size-- > 0) buffer++;
520 arg++;
522 if (*parser->rd == ',') {
523 parser->rd++;
524 if (arg >= macro->arg_count) {
525 WMenuParserError(parser, _("too many arguments for macro \"%s\", expected only %d"),
526 macro->name, macro->arg_count);
527 return False;
529 continue;
531 break;
533 if (*parser->rd != ')') {
534 WMenuParserError(parser, _("premature end of line while searching for arguments to macro \"%s\""),
535 macro->name);
536 return False;
538 parser->rd++;
539 if (arg < macro->arg_count) {
540 WMenuParserError(parser, _("not enough arguments for macro \"%s\", expected %d but got only %d"),
541 macro->name, macro->arg_count, arg);
542 return False;
544 if (buffer_size < 0)
545 WMenuParserError(parser, _("too much data in parameter list of macro \"%s\", truncated"),
546 macro->name);
547 return True;
550 /******************************************************************************/
551 /* Definition of pre-defined macros */
552 /******************************************************************************/
554 void WMenuParserRegisterSimpleMacro(WMenuParser parser, const char *name, const char *value)
556 WParserMacro *macro;
557 size_t len;
558 unsigned char *wr;
560 macro = wmalloc(sizeof(*macro));
561 strncpy(macro->name, name, sizeof(macro->name)-1);
562 macro->arg_count = -1;
563 len = strlen(value);
564 if (len > sizeof(macro->value) - 3) {
565 wwarning(_("size of value for macro '%s' is too big, truncated"), name);
566 len = sizeof(macro->value) - 3;
568 macro->value[0] = (len >> 8) & 0xFF;
569 macro->value[1] = len & 0xFF;
570 wr = &macro->value[2];
571 while (len-- > 0)
572 *wr++ = *value++;
573 *wr = 0xFF;
574 macro->next = parser->macros;
575 parser->macros = macro;
578 /* Name of the originally loaded file (before #includes) */
579 static void mpm_base_file(WParserMacro *this, WMenuParser parser)
581 unsigned char *src, *dst;
583 if (this->value[0] != '\0') return; // Value already evaluated, re-use previous
585 while (parser->parent_file != NULL)
586 parser = parser->parent_file;
588 dst = this->value;
589 src = (unsigned char *) parser->file_name;
590 *dst++ = '\"';
591 while (*src != '\0')
592 if (dst < this->value + sizeof(this->value) - 2)
593 *dst++ = *src++;
594 else
595 break;
596 *dst++ = '\"';
597 *dst = '\0';
600 /* Number of #include currently nested */
601 static void mpm_include_level(WParserMacro *this, WMenuParser parser)
603 int level = 0;
604 while (parser->parent_file != NULL) {
605 parser = parser->parent_file;
606 level++;
608 snprintf((char *) this->value, sizeof(this->value), "%d", level);
611 /* Name of current file */
612 static void mpm_current_file(WParserMacro *this, WMenuParser parser)
614 unsigned char *src, *dst;
616 dst = this->value;
617 src = (unsigned char *) parser->file_name;
618 *dst++ = '\"';
619 while (*src != '\0')
620 if (dst < this->value + sizeof(this->value) - 2)
621 *dst++ = *src++;
622 else
623 break;
624 *dst++ = '\"';
625 *dst = '\0';
628 /* Number of current line */
629 static void mpm_current_line(WParserMacro *this, WMenuParser parser)
631 snprintf((char *) this->value, sizeof(this->value), "%d", parser->line_number);
634 /* Name of host on which we are running, not necessarily displaying */
635 static void mpm_get_hostname(WParserMacro *this, WMenuParser parser)
637 char *h;
639 if (this->value[0] != '\0') return; // Value already evaluated, re-use previous
641 h = getenv("HOSTNAME");
642 if (h == NULL) {
643 h = getenv("HOST");
644 if (h == NULL) {
645 if (gethostname((char *) this->value, sizeof(this->value) ) != 0) {
646 WMenuParserError(parser, _("could not determine %s"), "HOSTNAME");
647 this->value[0] = '?';
648 this->value[1] = '?';
649 this->value[2] = '?';
650 this->value[3] = '\0';
652 return;
655 wstrlcpy((char *) this->value, h, sizeof(this->value) );
658 /* Name of the current user */
659 static void mpm_get_user_name(WParserMacro *this, WMenuParser parser)
661 char *user;
663 if (this->value[0] != '\0') return; // Value already evaluated, re-use previous
665 user = getlogin();
666 if (user == NULL) {
667 struct passwd *pw_user;
669 pw_user = getpwuid(getuid());
670 if (pw_user == NULL) {
671 error_no_username:
672 WMenuParserError(parser, _("could not determine %s"), "USER" );
673 /* Fall back on numeric id - better than nothing */
674 snprintf((char *) this->value, sizeof(this->value), "%d", getuid() );
675 return;
677 user = pw_user->pw_name;
678 if (user == NULL) goto error_no_username;
680 wstrlcpy((char *) this->value, user, sizeof(this->value) );
683 /* Number id of the user under which we are running */
684 static void mpm_get_user_id(WParserMacro *this, WMenuParser parser)
686 /* Parameter not used, but tell the compiler that it is ok */
687 (void) parser;
689 if (this->value[0] != '\0') return; // Already evaluated, re-use previous
690 snprintf((char *) this->value, sizeof(this->value), "%d", getuid() );
693 /* Small helper to automate creation of one pre-defined macro in the parser */
694 static void w_create_macro(WMenuParser parser, const char *name, WParserMacroFunction *handler)
696 WParserMacro *macro;
698 macro = wmalloc(sizeof(*macro));
699 strncpy(macro->name, name, sizeof(macro->name) - 1);
700 macro->function = handler;
701 macro->arg_count = -1;
702 macro->next = parser->macros;
703 parser->macros = macro;
706 /***** Register all the pre-defined macros in the parser *****/
707 void menu_parser_register_preset_macros(WMenuParser parser)
709 /* Defined by CPP: common predefined macros (GNU C extension) */
710 w_create_macro(parser, "__BASE_FILE__", mpm_base_file);
711 w_create_macro(parser, "__INCLUDE_LEVEL__", mpm_include_level);
713 /* Defined by CPP: standard predefined macros */
714 w_create_macro(parser, "__FILE__", mpm_current_file);
715 w_create_macro(parser, "__LINE__", mpm_current_line);
716 // w_create_macro(parser, "__DATE__", NULL); [will be implemented only per user request]
717 // w_create_macro(parser, "__TIME__", NULL); [will be implemented only per user request]
719 /* Historically defined by WindowMaker */
720 w_create_macro(parser, "HOST", mpm_get_hostname);
721 w_create_macro(parser, "UID", mpm_get_user_id);
722 w_create_macro(parser, "USER", mpm_get_user_name);