2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ** Parse.c: routines for parsing in fvwm & modules
21 /* ---------------------------- included header files ---------------------- */
32 /* ---------------------------- local definitions -------------------------- */
34 /* ---------------------------- local macros ------------------------------- */
36 /* ---------------------------- imports ------------------------------------ */
38 /* ---------------------------- included code files ------------------------ */
40 /* ---------------------------- local types -------------------------------- */
42 /* ---------------------------- forward declarations ----------------------- */
44 /* ---------------------------- local variables ---------------------------- */
46 /* ---------------------------- exported variables (globals) --------------- */
48 /* ---------------------------- local functions ---------------------------- */
50 /* Copies a token beginning at src to a previously allocated area dest. dest
51 * must be large enough to hold the token. Leading whitespace causes the token
53 /* NOTE: CopyToken can be called with dest == src. The token will be copied
54 * back down over the src string. */
55 static char *CopyToken(
56 char *src
, char *dest
, char *spaces
, int snum
, char *delims
, int dnum
,
62 while (*src
!= 0 && !(isspace((unsigned char)*src
) ||
63 (snum
&& strchr(spaces
, *src
)) ||
64 (dnum
&& strchr(delims
, *src
))))
66 /* Check for quoted text */
72 while ((*src
!= c
)&&(*src
!= 0))
74 if ((*src
== '\\' && *(src
+1) != 0))
76 /* Skip over backslashes */
79 if (len
< MAX_TOKEN_LENGTH
- 1)
86 /* token too long, just skip rest */
97 if ((*src
== '\\' && *(src
+1) != 0))
99 /* Skip over backslashes */
102 if (len
< MAX_TOKEN_LENGTH
- 1)
105 *(dest
++) = *(src
++);
109 /* token too long, just skip rest of token */
119 t
= SkipSpaces(src
, spaces
, snum
);
120 if (*t
!= 0 && dnum
&& strchr(delims
, *t
) != NULL
)
136 /* ---------------------------- interface functions ------------------------ */
138 /* This function escapes all occurences of the characters in the string qchars
139 * in the string as with a preceding echar character. The resulting string is
140 * returned in a malloced memory area. */
141 char *EscapeString(char *s
, const char *qchars
, char echar
)
147 for (len
= 1, t
= s
; *t
; t
++, len
++)
149 if (strchr(qchars
, *t
) != NULL
)
154 ret
= (char *)safemalloc(len
);
155 for (t
= ret
; *s
; s
++, t
++)
157 if (strchr(qchars
, *s
) != NULL
)
169 /* If the string s begins with a quote chracter SkipQuote returns a pointer
170 * to the first unquoted character or to the final '\0'. If it does not, a
171 * pointer to the next character in the string is returned.
172 * There are three possible types of quoting: a backslash quotes the next
173 * character only. Long quotes like " " or ' ' quoting everything in
174 * between and quote pairs like ( ) or { }.
178 * 1) Backslashes are honoured always, even inside long or pair quotes.
179 * 2) long quotes do quote quoting pair characters but not simple quotes. All
180 * long quotes can quote all other types of long quotes).
181 * 3) pair quotes none of the above. Text between a pair of quotes is treated
184 * qlong - string of long quoted (defaults to "'` )
185 * qstart - string of pair quote start characters (defaults to empty string)
186 * qend - string of pair quote end characters (defaults to empty string)
188 * The defaults are used if NULL is passed for the corresponding string.
191 char *s
, const char *qlong
, const char *qstart
, const char *qend
)
195 if (s
== NULL
|| *s
== 0)
212 if (*s
== '\\' && s
[1] != 0)
216 else if (*qlong
&& (t
= strchr(qlong
, *s
)))
221 while (*s
&& *s
!= c
)
223 /* Skip over escaped text, ie \quote */
224 if (*s
== '\\' && *(s
+1) != 0)
236 else if (*qstart
&& (t
= strchr(qstart
, *s
)))
238 char c
= *((t
- qstart
) + qend
);
240 while (*s
&& *s
!= c
)
242 s
= SkipQuote(s
, qlong
, "", "");
244 return (*s
== c
) ? ++s
: s
;
250 /* Returns a string up to the first character from the string delims in a
251 * malloc'd area just like GetNextToken. Quotes are not removed from the
252 * returned string. The returned string is stored in *sout, the return value
253 * of this call is a pointer to the first character after the delimiter or
254 * to the terminating '\0'. Quoting is handled like in SkipQuote. If sin is
255 * NULL, the function returns NULL in *sout. */
256 char *GetQuotedString(
257 char *sin
, char **sout
, const char *delims
, const char *qlong
,
258 const char *qstart
, const char *qend
)
273 while (*t
&& !strchr(delims
, *t
))
275 t
= SkipQuote(t
, qlong
, qstart
, qend
);
278 *sout
= (char *)safemalloc(len
+ 1);
279 memcpy(*sout
, sin
, len
);
289 /* SkipSpaces: returns a pointer to the first character in indata that is
290 * neither a whitespace character nor contained in the string 'spaces'. snum
291 * is the number of characters in 'spaces'. You must not pass a NULL pointer
293 char *SkipSpaces(char *indata
, char *spaces
, int snum
)
295 while (*indata
!= 0 && (isspace((unsigned char)*indata
) ||
296 (snum
&& strchr(spaces
, *indata
))))
304 ** DoPeekToken: returns next token from string, leaving string intact
305 ** (you must not free returned string)
307 ** WARNING: The returned pointer points to a static array that will be
308 ** overwritten in all functions in this file!
310 ** For a description of the parameters see DoGetNextToken below. DoPeekToken
313 /* NOTE: If indata is the pointer returned by a previous call to PeekToken or
314 * DoPeekToken, the input string will be destroyed. */
316 char *indata
, char **token
, char *spaces
, char *delims
, char *out_delim
)
321 static char tmptok
[MAX_TOKEN_LENGTH
];
323 snum
= (spaces
) ? strlen(spaces
) : 0;
324 dnum
= (delims
) ? strlen(delims
) : 0;
334 indata
= SkipSpaces(indata
, spaces
, snum
);
335 end
= CopyToken(indata
, tmptok
, spaces
, snum
, delims
, dnum
, out_delim
);
350 * PeekToken takes the input string "indata" and returns a pointer to the
351 * token, stored in a static char array. The pointer is invalidated by
352 * the next call to PeekToken. If "outdata" is not NULL, the pointer
353 * to the first character after the token is returned through
354 * *outdata. (Note that outdata is a char **, not just a char *).
356 char *PeekToken(char *indata
, char **outdata
)
365 *outdata
= DoPeekToken(indata
, &token
, NULL
, NULL
, NULL
);
371 /**** SMR: Defined but not used -- is this for the future or a relic of the
373 /* domivogt (27-Jun-1999): It's intended for future use. I have no problem
374 * commenting it out if it's not used. */
376 /* Tries to seek up to n tokens in indata. Returns the number of tokens
377 * actually found (up to a maximum of n). */
380 int CheckNTokens(char *indata
, unsigned int n
)
385 for (i
= 0, token
= (char *)1; i
< n
&& token
!= NULL
; i
++)
387 token
= PeekToken(indata
, NULL
);
395 ** MatchToken: does case-insensitive compare on next token in string, leaving
396 ** string intact (returns true if matches, false otherwise)
398 int MatchToken(char *pstr
,char *tok
)
403 DoPeekToken(pstr
, &ntok
, NULL
, NULL
, NULL
);
406 rc
= (strcasecmp(tok
,ntok
)==0);
412 /* unused at the moment */
415 description: compare 1st word of s to 1st word of t
416 returns: < 0 if s < t
420 int XCmpToken(const char *s
, const char **t
)
422 register const char *w
=*t
;
433 while (*w
&& (*s
==*w
|| toupper(*s
)==toupper(*w
)) )
439 if ((*s
=='\0' && (ispunct(*w
) || isspace(*w
)))||
440 (*w
=='\0' && (ispunct(*s
) || isspace(*s
))) )
442 return 0; /* 1st word equal */
446 return toupper(*s
)-toupper(*w
); /* smaller/greater */
453 * Gets the next "word" of input from string indata.
454 * "word" is a string with no spaces, or a qouted string.
455 * Return value is ptr to indata,updated to point to text after the word
456 * which is extracted.
457 * token is the extracted word, which is copied into a malloced
458 * space, and must be freed after use. DoGetNextToken *never* returns an
459 * empty string or token. If the token consists only of whitespace or
460 * delimiters, the returned token is NULL instead. If out_delim is given,
461 * the character ending the string is returned therein.
463 * spaces = string of characters to treat as spaces
464 * delims = string of characters delimiting token
466 * Use "spaces" and "delims" to define additional space/delimiter
467 * characters (spaces are skipped before a token, delimiters are not).
470 char *DoGetNextToken(
471 char *indata
, char **token
, char *spaces
, char *delims
, char *out_delim
)
476 end
= DoPeekToken(indata
, &tmptok
, spaces
, delims
, out_delim
);
483 *token
= safestrdup(tmptok
);
490 * GetNextToken works similarly to PeekToken, but: stores the token in
491 * *token, & returns a pointer to the first character after the token
492 * in *token. The memory in *token is allocated with malloc and the
493 * calling function has to free() it.
495 * If possible, use PeekToken because it's faster and does not risk
496 * creating memory leaks.
498 char *GetNextToken(char *indata
, char **token
)
500 return DoGetNextToken(indata
, token
, NULL
, NULL
, NULL
);
503 /* fetch next token and stop at next ',' */
504 char *GetNextSimpleOption(char *indata
, char **option
)
506 return DoGetNextToken(indata
, option
, NULL
, ",", NULL
);
509 /* read multiple tokens up to next ',' or end of line */
510 char *GetNextFullOption(char *indata
, char **option
)
512 return GetQuotedString(indata
, option
, ",\n", NULL
, NULL
, NULL
);
515 char *SkipNTokens(char *indata
, unsigned int n
)
517 for ( ; n
> 0 && indata
!= NULL
&& *indata
!= 0; n
--)
519 PeekToken(indata
, &indata
);
526 * convenience functions
531 * Works like GetNextToken, but with the following differences:
533 * If *indata begins with a "*" followed by the string module_name,
534 * it returns the string following directly after module_name as the
535 * new token. Otherwise NULL is returned.
536 * e.g. GetModuleResource("*FvwmPagerGeometry", &token, "FvwmPager")
537 * returns "Geometry" in token.
540 char *GetModuleResource(char *indata
, char **resource
, char *module_name
)
550 tmp
= PeekToken(indata
, &next
);
557 strncasecmp(tmp
+1, module_name
, strlen(module_name
)))
562 CopyString(resource
, tmp
+1+strlen(module_name
));
570 * This function uses GetNextToken to parse action for up to num integer
571 * arguments. The number of values actually found is returned.
572 * If ret_action is non-NULL, a pointer to the next token is returned there.
573 * The suffixlist parameter points to a string of possible suffixes for the
574 * integer values. The index of the matched suffix is returned in
575 * ret_suffixnum (0 = no suffix, 1 = first suffix in suffixlist ...).
578 static int _get_suffixed_integer_arguments(
579 char *action
, char **ret_action
, int *retvals
, int num
,
580 char *suffixlist
, int *ret_suffixnum
, char *parsestring
)
592 /* if passed a suffixlist save its length */
593 suffixes
= strlen(suffixlist
);
596 for (i
= 0; i
< num
&& action
; i
++)
598 token
= PeekToken(action
, &action
);
603 if (sscanf(token
, parsestring
, &(retvals
[i
]), &n
) < 1)
607 if (suffixes
!= 0 && ret_suffixnum
!= NULL
)
612 len
= strlen(token
) - 1;
618 for (j
= 0; j
< suffixes
; j
++)
620 char c2
= suffixlist
[j
];
628 ret_suffixnum
[i
] = j
+1;
634 ret_suffixnum
[i
] = 0;
637 else if (token
[n
] != 0 && !isspace(token
[n
]))
639 /* there is a suffix but no suffix list was specified */
643 if (ret_action
!= NULL
)
645 *ret_action
= action
;
651 int GetSuffixedIntegerArguments(
652 char *action
, char **ret_action
, int *retvals
, int num
,
653 char *suffixlist
, int *ret_suffixnum
)
655 return _get_suffixed_integer_arguments(
656 action
, ret_action
, retvals
, num
, suffixlist
, ret_suffixnum
,
662 * This function converts the suffix/number pairs returned by
663 * GetSuffixedIntegerArguments into pixels. The unit_table is an array of
664 * integers that determine the factor to multiply with in hundredths of
665 * pixels. I.e. a unit of 100 means: translate the value into pixels,
666 * 50 means divide value by 2 to get the number of pixels and so on.
667 * The unit used is determined by the suffix which is taken as the index
668 * into the table. No size checking of the unit_table is done, so make sure
669 * it is big enough before calling this function.
672 int SuffixToPercentValue(int value
, int suffix
, int *unit_table
)
674 return (value
* unit_table
[suffix
]) / 100;
679 * This function uses GetNextToken to parse action for up to num integer
680 * arguments. The number of values actually found is returned.
681 * If ret_action is non-NULL, a pointer to the next token is returned there.
684 int GetIntegerArguments(char *action
, char **ret_action
, int *retvals
,int num
)
686 return _get_suffixed_integer_arguments(
687 action
, ret_action
, retvals
, num
, NULL
, NULL
, "%d%n");
692 * Same as above, but supports hexadecimal and octal integers via 0x and 0
696 int GetIntegerArgumentsAnyBase(
697 char *action
, char **ret_action
, int *retvals
,int num
)
699 return _get_suffixed_integer_arguments(
700 action
, ret_action
, retvals
, num
, NULL
, NULL
, "%i%n");
705 * This function tries to match a token with a list of strings and returns
706 * the position of token in the array or -1 if no match is found. The last
707 * entry in the list must be NULL.
709 * len = 0 : only exact matches
710 * len < 0 : match, if token begins with one of the strings in list
711 * len > 0 : match, if the first len characters do match
713 * if next is non-NULL, *next will be set to point to the first character
714 * in token after the match.
717 int GetTokenIndex(char *token
, char **list
, int len
, char **next
)
731 l
= (len
) ? len
: strlen(token
);
732 for (i
= 0; list
[i
] != NULL
; i
++)
739 else if (len
== 0 && k
!= l
)
743 if (!strncasecmp(token
, list
[i
], l
))
750 *next
= (list
[i
]) ? token
+ l
: token
;
753 return (list
[i
]) ? i
: -1;
758 * This function does roughly the same as GetTokenIndex but reads the
759 * token from string action with GetNextToken. The index is returned
760 * in *index. The return value is a pointer to the character after the
761 * token (just like the return value of GetNextToken).
764 char *GetNextTokenIndex(char *action
, char **list
, int len
, int *index
)
774 token
= PeekToken(action
, &next
);
780 *index
= GetTokenIndex(token
, list
, len
, NULL
);
782 return (*index
== -1) ? action
: next
;
788 * Parses two integer arguments given in the form <int><character><int>.
789 * character can be ' ' or 'x', but any other character is allowed too
790 * (except 'p' or 'P').
793 int GetRectangleArguments(char *action
, int *width
, int *height
)
798 token
= PeekToken(action
, NULL
);
803 n
= sscanf(token
, "%d%*c%d", width
, height
);
805 return (n
== 2) ? 2 : 0;
808 /* unit_io is input as well as output. If action has a postfix 'p' or 'P',
809 * *unit_io is set to 100, otherwise it is left untouched. */
810 int GetOnePercentArgument(char *action
, int *value
, int *unit_io
)
821 token
= PeekToken(action
, NULL
);
827 /* token never contains an empty string, so this is ok */
828 if (token
[len
- 1] == 'p' || token
[len
- 1] == 'P')
831 token
[len
- 1] = '\0';
833 n
= sscanf(token
, "%d", value
);
839 int GetTwoPercentArguments(
840 char *action
, int *val1
, int *val2
, int *val1_unit
, int *val2_unit
)
850 tok1
= PeekToken(action
, &next
);
851 action
= GetNextToken(action
, &tok1
);
856 GetNextToken(action
, &tok2
);
857 if (GetOnePercentArgument(tok2
, val2
, val2_unit
) == 1 &&
858 GetOnePercentArgument(tok1
, val1
, val1_unit
) == 1)
865 /* now try MxN style number, specifically for DeskTopSize: */
866 n
= GetRectangleArguments(tok1
, val1
, val2
);
877 /* Parses the next token in action and returns 1 if it is "yes", "true", "y",
878 * "t" or "on", zero if it is "no", "false", "n", "f" or "off" and -1 if it is
879 * "toggle". A pointer to the first character in action behind the token is
880 * returned through ret_action in this case. ret_action may be NULL. If the
881 * token matches none of these strings the default_ret value is returned and
882 * the action itself is passed back in ret_action. If the no_toggle flag is
883 * non-zero, the "toggle" string is handled as no match. */
884 int ParseToggleArgument(
885 char *action
, char **ret_action
, int default_ret
, char no_toggle
)
900 next
= GetNextTokenIndex(action
, optlist
, 0, &index
);
901 if (index
== 0 && no_toggle
== 0)
903 /* toggle requested explicitly */
906 else if (index
== -1 || (index
== 0 && no_toggle
))
908 /* nothing selected, use default and don't modify action */
914 /* odd numbers denote True, even numbers denote False */
925 /* Strips the path from 'path' and returns the last component in a malloc'ed
927 char *GetFileNameFromPath(char *path
)
932 /* we get rid of the path from program name */
933 s
= strrchr(path
, '/');
942 name
= (char *)safemalloc(strlen(s
)+1);