Add reminder to investigate recursive commands for 2.6
[fvwm.git] / libs / Parse.c
blob881c939758a29dff640bccf4789607e1cb692408
1 /* -*-c-*- */
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 ---------------------- */
23 #include "config.h"
25 #include <stdio.h>
26 #include <ctype.h>
28 #include "fvwmlib.h"
29 #include "Strings.h"
30 #include "Parse.h"
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
52 * to be NULL. */
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,
57 char *out_delim)
59 int len = 0;
60 char *t;
62 while (*src != 0 && !(isspace((unsigned char)*src) ||
63 (snum && strchr(spaces, *src)) ||
64 (dnum && strchr(delims, *src))))
66 /* Check for quoted text */
67 if (IsQuote(*src))
69 char c = *src;
71 src++;
72 while ((*src != c)&&(*src != 0))
74 if ((*src == '\\' && *(src+1) != 0))
76 /* Skip over backslashes */
77 src++;
79 if (len < MAX_TOKEN_LENGTH - 1)
81 len++;
82 *(dest++) = *(src++);
84 else
86 /* token too long, just skip rest */
87 src++;
90 if (*src == c)
92 src++;
95 else
97 if ((*src == '\\' && *(src+1) != 0))
99 /* Skip over backslashes */
100 src++;
102 if (len < MAX_TOKEN_LENGTH - 1)
104 len++;
105 *(dest++) = *(src++);
107 else
109 /* token too long, just skip rest of token */
110 src++;
114 if (out_delim)
116 *out_delim = *src;
118 *dest = 0;
119 t = SkipSpaces(src, spaces, snum);
120 if (*t != 0 && dnum && strchr(delims, *t) != NULL)
122 if (out_delim)
124 *out_delim = *t;
126 src = t + 1;
128 else if (*src != 0)
130 src++;
133 return src;
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)
143 char *t;
144 char *ret;
145 int len;
147 for (len = 1, t = s; *t ; t++, len++)
149 if (strchr(qchars, *t) != NULL)
151 len++;
154 ret = (char *)safemalloc(len);
155 for (t = ret; *s; s++, t++)
157 if (strchr(qchars, *s) != NULL)
159 *t = echar;
160 t++;
162 *t = *s;
164 *t = 0;
166 return ret;
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 { }.
176 * precedence:
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
182 * as a single token.
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.
190 char *SkipQuote(
191 char *s, const char *qlong, const char *qstart, const char *qend)
193 char *t;
195 if (s == NULL || *s == 0)
197 return s;
199 if (!qlong)
201 qlong = "\"'`";
203 if (!qstart)
205 qstart = "";
207 if (!qend)
209 qend = "";
212 if (*s == '\\' && s[1] != 0)
214 return s+2;
216 else if (*qlong && (t = strchr(qlong, *s)))
218 char c = *t;
220 s++;
221 while (*s && *s != c)
223 /* Skip over escaped text, ie \quote */
224 if (*s == '\\' && *(s+1) != 0)
226 s++;
228 s++;
230 if (*s == c)
232 s++;
234 return s;
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;
247 return ++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)
260 char *t = sin;
261 unsigned int len;
263 if (!sout)
265 return NULL;
267 if (!sin)
269 *sout = NULL;
270 return NULL;
273 while (*t && !strchr(delims, *t))
275 t = SkipQuote(t, qlong, qstart, qend);
277 len = t - sin;
278 *sout = (char *)safemalloc(len + 1);
279 memcpy(*sout, sin, len);
280 (*sout)[len] = 0;
281 if (*t)
283 t++;
286 return t;
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
292 * in indata. */
293 char *SkipSpaces(char *indata, char *spaces, int snum)
295 while (*indata != 0 && (isspace((unsigned char)*indata) ||
296 (snum && strchr(spaces, *indata))))
298 indata++;
300 return 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
311 ** is a bit faster.
313 /* NOTE: If indata is the pointer returned by a previous call to PeekToken or
314 * DoPeekToken, the input string will be destroyed. */
315 char *DoPeekToken(
316 char *indata, char **token, char *spaces, char *delims, char *out_delim)
318 char *end;
319 int snum;
320 int dnum;
321 static char tmptok[MAX_TOKEN_LENGTH];
323 snum = (spaces) ? strlen(spaces) : 0;
324 dnum = (delims) ? strlen(delims) : 0;
325 if (indata == NULL)
327 if (out_delim)
329 *out_delim = '\0';
331 *token = NULL;
332 return NULL;
334 indata = SkipSpaces(indata, spaces, snum);
335 end = CopyToken(indata, tmptok, spaces, snum, delims, dnum, out_delim);
337 if (tmptok[0] == 0)
339 *token = NULL;
341 else
343 *token = tmptok;
346 return end;
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)
358 char *dummy;
359 char *token;
361 if (!outdata)
363 outdata = &dummy;
365 *outdata = DoPeekToken(indata, &token, NULL, NULL, NULL);
367 return token;
371 /**** SMR: Defined but not used -- is this for the future or a relic of the
372 **** past? ****/
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). */
379 #if 0
380 int CheckNTokens(char *indata, unsigned int n)
382 unsigned int i;
383 char *token;
385 for (i = 0, token = (char *)1; i < n && token != NULL; i++)
387 token = PeekToken(indata, NULL);
390 return i;
392 #endif
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)
400 int rc=0;
401 char *ntok;
403 DoPeekToken(pstr, &ntok, NULL, NULL, NULL);
404 if (ntok)
406 rc = (strcasecmp(tok,ntok)==0);
409 return rc;
412 /* unused at the moment */
414 function: XCmpToken
415 description: compare 1st word of s to 1st word of t
416 returns: < 0 if s < t
417 = 0 if s = t
418 > 0 if s > t
420 int XCmpToken(const char *s, const char **t)
422 register const char *w=*t;
424 if (w==NULL)
426 return 1;
428 if (s==NULL)
430 return -1;
433 while (*w && (*s==*w || toupper(*s)==toupper(*w)) )
435 s++;
436 w++;
439 if ((*s=='\0' && (ispunct(*w) || isspace(*w)))||
440 (*w=='\0' && (ispunct(*s) || isspace(*s))) )
442 return 0; /* 1st word equal */
444 else
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)
473 char *tmptok;
474 char *end;
476 end = DoPeekToken(indata, &tmptok, spaces, delims, out_delim);
477 if (tmptok == NULL)
479 *token = NULL;
481 else
483 *token = safestrdup(tmptok);
486 return end;
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);
522 return 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)
542 char *tmp;
543 char *next;
545 if (!module_name)
547 *resource = NULL;
548 return indata;
550 tmp = PeekToken(indata, &next);
551 if (!tmp)
553 return next;
556 if (tmp[0] != '*' ||
557 strncasecmp(tmp+1, module_name, strlen(module_name)))
559 *resource = NULL;
560 return indata;
562 CopyString(resource, tmp+1+strlen(module_name));
564 return next;
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)
582 int i;
583 int j;
584 int n;
585 char *token;
586 int suffixes;
588 /* initialize */
589 suffixes = 0;
590 if (suffixlist != 0)
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);
599 if (token == NULL)
601 break;
603 if (sscanf(token, parsestring, &(retvals[i]), &n) < 1)
605 break;
607 if (suffixes != 0 && ret_suffixnum != NULL)
609 int len;
610 char c;
612 len = strlen(token) - 1;
613 c = token[len];
614 if (isupper(c))
616 c = tolower(c);
618 for (j = 0; j < suffixes; j++)
620 char c2 = suffixlist[j];
622 if (isupper(c2))
624 c2 = tolower(c2);
626 if (c == c2)
628 ret_suffixnum[i] = j+1;
629 break;
632 if (j == suffixes)
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 */
640 break;
643 if (ret_action != NULL)
645 *ret_action = action;
648 return i;
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,
657 "%d%n");
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
693 * prefixes.
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)
719 int i;
720 int l;
721 int k;
723 if (!token || !list)
725 if (next)
727 *next = NULL;
729 return -1;
731 l = (len) ? len : strlen(token);
732 for (i = 0; list[i] != NULL; i++)
734 k = strlen(list[i]);
735 if (len < 0)
737 l = k;
739 else if (len == 0 && k != l)
741 continue;
743 if (!strncasecmp(token, list[i], l))
745 break;
748 if (next)
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)
766 char *token;
767 char *next;
769 if (!index)
771 return action;
774 token = PeekToken(action, &next);
775 if (!token)
777 *index = -1;
778 return action;
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)
795 char *token;
796 int n;
798 token = PeekToken(action, NULL);
799 if (!token)
801 return 0;
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)
812 unsigned int len;
813 char *token;
814 int n;
816 *value = 0;
817 if (!action)
819 return 0;
821 token = PeekToken(action, NULL);
822 if (!token)
824 return 0;
826 len = strlen(token);
827 /* token never contains an empty string, so this is ok */
828 if (token[len - 1] == 'p' || token[len - 1] == 'P')
830 *unit_io = 100;
831 token[len - 1] = '\0';
833 n = sscanf(token, "%d", value);
835 return n;
839 int GetTwoPercentArguments(
840 char *action, int *val1, int *val2, int *val1_unit, int *val2_unit)
842 char *tok1;
843 char *tok2;
844 char *next;
845 int n = 0;
847 *val1 = 0;
848 *val2 = 0;
850 tok1 = PeekToken(action, &next);
851 action = GetNextToken(action, &tok1);
852 if (!tok1)
854 return 0;
856 GetNextToken(action, &tok2);
857 if (GetOnePercentArgument(tok2, val2, val2_unit) == 1 &&
858 GetOnePercentArgument(tok1, val1, val1_unit) == 1)
860 free(tok1);
861 free(tok2);
862 return 2;
865 /* now try MxN style number, specifically for DeskTopSize: */
866 n = GetRectangleArguments(tok1, val1, val2);
867 free(tok1);
868 if (tok2)
870 free(tok2);
873 return n;
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)
887 int index;
888 int rc;
889 char *next;
890 char *optlist[] = {
891 "toggle",
892 "yes", "no",
893 "true", "false",
894 "on", "off",
895 "t", "f",
896 "y", "n",
897 NULL
900 next = GetNextTokenIndex(action, optlist, 0, &index);
901 if (index == 0 && no_toggle == 0)
903 /* toggle requested explicitly */
904 rc = -1;
906 else if (index == -1 || (index == 0 && no_toggle))
908 /* nothing selected, use default and don't modify action */
909 rc = default_ret;
910 next = action;
912 else
914 /* odd numbers denote True, even numbers denote False */
915 rc = (index & 1);
917 if (ret_action)
919 *ret_action = next;
922 return rc;
925 /* Strips the path from 'path' and returns the last component in a malloc'ed
926 * area. */
927 char *GetFileNameFromPath(char *path)
929 char *s;
930 char *name;
932 /* we get rid of the path from program name */
933 s = strrchr(path, '/');
934 if (s)
936 s++;
938 else
940 s = path;
942 name = (char *)safemalloc(strlen(s)+1);
943 strcpy(name, s);
945 return name;