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.
6  *
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.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, see: <http://www.gnu.org/licenses/>
14  */
15 
16 /*
17 ** Parse.c: routines for parsing in fvwm & modules
18 */
19 
20 /* ---------------------------- included header files ---------------------- */
21 
22 #include "config.h"
23 
24 #include <stdio.h>
25 #include <ctype.h>
26 
27 #include "fvwmlib.h"
28 #include "Strings.h"
29 #include "Parse.h"
30 
31 /* ---------------------------- local definitions -------------------------- */
32 
33 /* ---------------------------- local macros ------------------------------- */
34 
35 /* ---------------------------- imports ------------------------------------ */
36 
37 /* ---------------------------- included code files ------------------------ */
38 
39 /* ---------------------------- local types -------------------------------- */
40 
41 /* ---------------------------- forward declarations ----------------------- */
42 
43 /* ---------------------------- local variables ---------------------------- */
44 
45 /* ---------------------------- exported variables (globals) --------------- */
46 
47 /* ---------------------------- local functions ---------------------------- */
48 
49 /* Copies a token beginning at src to a previously allocated area dest. dest
50  * must be large enough to hold the token. Leading whitespace causes the token
51  * to be NULL. */
52 /* NOTE: CopyToken can be called with dest == src. The token will be copied
53  * back down over the src string. */
CopyToken(char * src,char * dest,char * spaces,int snum,char * delims,int dnum,char * out_delim)54 static char *CopyToken(
55 	char *src, char *dest, char *spaces, int snum, char *delims, int dnum,
56 	char *out_delim)
57 {
58 	int len = 0;
59 	char *t;
60 
61 	while (*src != 0 && !(isspace((unsigned char)*src) ||
62 			      (snum && strchr(spaces, *src)) ||
63 			      (dnum && strchr(delims, *src))))
64 	{
65 		/* Check for quoted text */
66 		if (IsQuote(*src))
67 		{
68 			char c = *src;
69 
70 			src++;
71 			while ((*src != c)&&(*src != 0))
72 			{
73 				if ((*src == '\\' && *(src+1) != 0))
74 				{
75 					/* Skip over backslashes */
76 					src++;
77 				}
78 				if (len < MAX_TOKEN_LENGTH - 1)
79 				{
80 					len++;
81 					*(dest++) = *(src++);
82 				}
83 				else
84 				{
85 					/* token too long, just skip rest */
86 					src++;
87 				}
88 			}
89 			if (*src == c)
90 			{
91 				src++;
92 			}
93 		}
94 		else
95 		{
96 			if ((*src == '\\' && *(src+1) != 0))
97 			{
98 				/* Skip over backslashes */
99 				src++;
100 			}
101 			if (len < MAX_TOKEN_LENGTH - 1)
102 			{
103 				len++;
104 				*(dest++) = *(src++);
105 			}
106 			else
107 			{
108 				/* token too long, just skip rest of token */
109 				src++;
110 			}
111 		}
112 	}
113 	if (out_delim)
114 	{
115 		*out_delim = *src;
116 	}
117 	*dest = 0;
118 	t = SkipSpaces(src, spaces, snum);
119 	if (*t != 0 && dnum && strchr(delims, *t) != NULL)
120 	{
121 		if (out_delim)
122 		{
123 			*out_delim = *t;
124 		}
125 		src = t + 1;
126 	}
127 	else if (*src != 0)
128 	{
129 		src++;
130 	}
131 
132 	return src;
133 }
134 
135 /* ---------------------------- interface functions ------------------------ */
136 
137 /* This function escapes all occurences of the characters in the string qchars
138  * in the string as with a preceding echar character.  The resulting string is
139  * returned in a malloced memory area. */
EscapeString(char * s,const char * qchars,char echar)140 char *EscapeString(char *s, const char *qchars, char echar)
141 {
142 	char *t;
143 	char *ret;
144 	int len;
145 
146 	for (len = 1, t = s; *t ; t++, len++)
147 	{
148 		if (strchr(qchars, *t) != NULL)
149 		{
150 			len++;
151 		}
152 	}
153 	ret = (char *)safemalloc(len);
154 	for (t = ret; *s; s++, t++)
155 	{
156 		if (strchr(qchars, *s) != NULL)
157 		{
158 			*t = echar;
159 			t++;
160 		}
161 		*t = *s;
162 	}
163 	*t = 0;
164 
165 	return ret;
166 }
167 
168 /* If the string s begins with a quote chracter SkipQuote returns a pointer
169  * to the first unquoted character or to the final '\0'. If it does not, a
170  * pointer to the next character in the string is returned.
171  * There are three possible types of quoting: a backslash quotes the next
172  * character only. Long quotes like " " or ' ' quoting everything in
173  * between and quote pairs like ( ) or { }.
174  *
175  * precedence:
176  *
177  * 1) Backslashes are honoured always, even inside long or pair quotes.
178  * 2) long quotes do quote quoting pair characters but not simple quotes. All
179  *    long quotes can quote all other types of long quotes).
180  * 3) pair quotes none of the above. Text between a pair of quotes is treated
181  *    as a single token.
182  *
183  * qlong   - string of long quoted (defaults to "'` )
184  * qstart  - string of pair quote start characters (defaults to empty string)
185  * qend	   - string of pair quote end characters (defaults to empty string)
186  *
187  * The defaults are used if NULL is passed for the corresponding string.
188  */
SkipQuote(char * s,const char * qlong,const char * qstart,const char * qend)189 char *SkipQuote(
190 	char *s, const char *qlong, const char *qstart, const char *qend)
191 {
192 	char *t;
193 
194 	if (s == NULL || *s == 0)
195 	{
196 		return s;
197 	}
198 	if (!qlong)
199 	{
200 		qlong = "\"'`";
201 	}
202 	if (!qstart)
203 	{
204 		qstart = "";
205 	}
206 	if (!qend)
207 	{
208 		qend = "";
209 	}
210 
211 	if (*s == '\\' && s[1] != 0)
212 	{
213 		return s+2;
214 	}
215 	else if (*qlong && (t = strchr(qlong, *s)))
216 	{
217 		char c = *t;
218 
219 		s++;
220 		while (*s && *s != c)
221 		{
222 			/* Skip over escaped text, ie \quote */
223 			if (*s == '\\' && *(s+1) != 0)
224 			{
225 				s++;
226 			}
227 			s++;
228 		}
229 		if (*s == c)
230 		{
231 			s++;
232 		}
233 		return s;
234 	}
235 	else if (*qstart && (t = strchr(qstart, *s)))
236 	{
237 		char c = *((t - qstart) + qend);
238 
239 		while (*s && *s != c)
240 		{
241 			s = SkipQuote(s, qlong, "", "");
242 		}
243 		return (*s == c) ? ++s : s;
244 	}
245 
246 	return ++s;
247 }
248 
249 /* Returns a string up to the first character from the string delims in a
250  * malloc'd area just like GetNextToken. Quotes are not removed from the
251  * returned string. The returned string is stored in *sout, the return value
252  * of this call is a pointer to the first character after the delimiter or
253  * to the terminating '\0'. Quoting is handled like in SkipQuote. If sin is
254  * NULL, the function returns NULL in *sout. */
GetQuotedString(char * sin,char ** sout,const char * delims,const char * qlong,const char * qstart,const char * qend)255 char *GetQuotedString(
256 	char *sin, char **sout, const char *delims, const char *qlong,
257 	const char *qstart, const char *qend)
258 {
259 	char *t = sin;
260 	unsigned int len;
261 
262 	if (!sout)
263 	{
264 		return NULL;
265 	}
266 	if (!sin)
267 	{
268 		*sout = NULL;
269 		return NULL;
270 	}
271 
272 	while (*t && !strchr(delims, *t))
273 	{
274 		t = SkipQuote(t, qlong, qstart, qend);
275 	}
276 	len = t - sin;
277 	*sout = (char *)safemalloc(len + 1);
278 	memcpy(*sout, sin, len);
279 	(*sout)[len] = 0;
280 	if (*t)
281 	{
282 		t++;
283 	}
284 
285 	return t;
286 }
287 
288 /* SkipSpaces: returns a pointer to the first character in indata that is
289  * neither a whitespace character nor contained in the string 'spaces'. snum
290  * is the number of characters in 'spaces'. You must not pass a NULL pointer
291  * in indata. */
SkipSpaces(char * indata,char * spaces,int snum)292 char *SkipSpaces(char *indata, char *spaces, int snum)
293 {
294 	while (*indata != 0 && (isspace((unsigned char)*indata) ||
295 				(snum && strchr(spaces, *indata))))
296 	{
297 		indata++;
298 	}
299 	return indata;
300 }
301 
302 /*
303 ** DoPeekToken: returns next token from string, leaving string intact
304 **		(you must not free returned string)
305 **
306 ** WARNING: The returned pointer points to a static array that will be
307 **	     overwritten in all functions in this file!
308 **
309 ** For a description of the parameters see DoGetNextToken below. DoPeekToken
310 ** is a bit faster.
311 */
312 /* NOTE: If indata is the pointer returned by a previous call to PeekToken or
313  * DoPeekToken, the input string will be destroyed. */
DoPeekToken(char * indata,char ** token,char * spaces,char * delims,char * out_delim)314 char *DoPeekToken(
315 	char *indata, char **token, char *spaces, char *delims, char *out_delim)
316 {
317 	char *end;
318 	int snum;
319 	int dnum;
320 	static char tmptok[MAX_TOKEN_LENGTH];
321 
322 	snum = (spaces) ? strlen(spaces) : 0;
323 	dnum = (delims) ? strlen(delims) : 0;
324 	if (indata == NULL)
325 	{
326 		if (out_delim)
327 		{
328 			*out_delim = '\0';
329 		}
330 		*token = NULL;
331 		return NULL;
332 	}
333 	indata = SkipSpaces(indata, spaces, snum);
334 	end = CopyToken(indata, tmptok, spaces, snum, delims, dnum, out_delim);
335 
336 	if (tmptok[0] == 0)
337 	{
338 		*token = NULL;
339 	}
340 	else
341 	{
342 		*token = tmptok;
343 	}
344 
345 	return end;
346 }
347 
348 /*
349  * PeekToken takes the input string "indata" and returns a pointer to the
350  * token, stored in a static char array.  The pointer is invalidated by
351  * the next call to PeekToken.  If "outdata" is not NULL, the pointer
352  * to the first character after the token is returned through
353  * *outdata. (Note that outdata is a char **, not just a char *).
354  */
PeekToken(char * indata,char ** outdata)355 char *PeekToken(char *indata, char **outdata)
356 {
357 	char *dummy;
358 	char *token;
359 
360 	if (!outdata)
361 	{
362 		outdata = &dummy;
363 	}
364 	*outdata = DoPeekToken(indata, &token, NULL, NULL, NULL);
365 
366 	return token;
367 }
368 
369 
370 /**** SMR: Defined but not used -- is this for the future or a relic of the
371  **** past? ****/
372 /* domivogt (27-Jun-1999): It's intended for future use. I have no problem
373  * commenting it out if it's not used. */
374 
375 /* Tries to seek up to n tokens in indata. Returns the number of tokens
376  * actually found (up to a maximum of n). */
377 
378 #if 0
379 int CheckNTokens(char *indata, unsigned int n)
380 {
381 	unsigned int i;
382 	char *token;
383 
384 	for (i = 0, token = (char *)1; i < n && token != NULL; i++)
385 	{
386 		token = PeekToken(indata, NULL);
387 	}
388 
389 	return i;
390 }
391 #endif
392 
393 /*
394 ** MatchToken: does case-insensitive compare on next token in string, leaving
395 **	       string intact (returns true if matches, false otherwise)
396 */
MatchToken(char * pstr,char * tok)397 int MatchToken(char *pstr,char *tok)
398 {
399 	int rc=0;
400 	char *ntok;
401 
402 	DoPeekToken(pstr, &ntok, NULL, NULL, NULL);
403 	if (ntok)
404 	{
405 		rc = (strcasecmp(tok,ntok)==0);
406 	}
407 
408 	return rc;
409 }
410 
411 /* unused at the moment */
412 /*
413   function:	       XCmpToken
414   description:	       compare 1st word of s to 1st word of t
415   returns:	       < 0  if s < t
416   = 0  if s = t
417   > 0  if s > t
418 */
XCmpToken(const char * s,const char ** t)419 int XCmpToken(const char *s, const char **t)
420 {
421 	register const char *w=*t;
422 
423 	if (w==NULL)
424 	{
425 		return 1;
426 	}
427 	if (s==NULL)
428 	{
429 		return -1;
430 	}
431 
432 	while (*w && (*s==*w || toupper(*s)==toupper(*w)) )
433 	{
434 		s++;
435 		w++;
436 	}
437 
438 	if ((*s=='\0' && (ispunct(*w) || isspace(*w)))||
439 	    (*w=='\0' && (ispunct(*s) || isspace(*s))) )
440 	{
441 		return 0;			/* 1st word equal */
442 	}
443 	else
444 	{
445 		return toupper(*s)-toupper(*w); /* smaller/greater */
446 	}
447 }
448 
449 
450 /*
451  *
452  * Gets the next "word" of input from string indata.
453  * "word" is a string with no spaces, or a qouted string.
454  * Return value is ptr to indata,updated to point to text after the word
455  * which is extracted.
456  * token is the extracted word, which is copied into a malloced
457  * space, and must be freed after use. DoGetNextToken *never* returns an
458  * empty string or token. If the token consists only of whitespace or
459  * delimiters, the returned token is NULL instead. If out_delim is given,
460  * the character ending the string is returned therein.
461  *
462  * spaces = string of characters to treat as spaces
463  * delims = string of characters delimiting token
464  *
465  * Use "spaces" and "delims" to define additional space/delimiter
466  * characters (spaces are skipped before a token, delimiters are not).
467  *
468  */
DoGetNextToken(char * indata,char ** token,char * spaces,char * delims,char * out_delim)469 char *DoGetNextToken(
470 	char *indata, char **token, char *spaces, char *delims, char *out_delim)
471 {
472 	char *tmptok;
473 	char *end;
474 
475 	end = DoPeekToken(indata, &tmptok, spaces, delims, out_delim);
476 	if (tmptok == NULL)
477 	{
478 		*token = NULL;
479 	}
480 	else
481 	{
482 		*token = safestrdup(tmptok);
483 	}
484 
485 	return end;
486 }
487 
488 /*
489  * GetNextToken works similarly to PeekToken, but: stores the token in
490  * *token, & returns a pointer to the first character after the token
491  * in *token. The memory in *token is allocated with malloc and the
492  * calling function has to free() it.
493  *
494  * If possible, use PeekToken because it's faster and does not risk
495  * creating memory leaks.
496  */
GetNextToken(char * indata,char ** token)497 char *GetNextToken(char *indata, char **token)
498 {
499 	return DoGetNextToken(indata, token, NULL, NULL, NULL);
500 }
501 
502 /* fetch next token and stop at next ',' */
GetNextSimpleOption(char * indata,char ** option)503 char *GetNextSimpleOption(char *indata, char **option)
504 {
505 	return DoGetNextToken(indata, option, NULL, ",", NULL);
506 }
507 
508 /* read multiple tokens up to next ',' or end of line */
GetNextFullOption(char * indata,char ** option)509 char *GetNextFullOption(char *indata, char **option)
510 {
511 	return GetQuotedString(indata, option, ",\n", NULL, NULL, NULL);
512 }
513 
SkipNTokens(char * indata,unsigned int n)514 char *SkipNTokens(char *indata, unsigned int n)
515 {
516 	for ( ; n > 0 && indata != NULL && *indata != 0; n--)
517 	{
518 		PeekToken(indata, &indata);
519 	}
520 
521 	return indata;
522 }
523 
524 /*
525  * convenience functions
526  */
527 
528 /*
529  *
530  * Works like GetNextToken, but with the following differences:
531  *
532  * If *indata begins with a "*" followed by the string module_name,
533  * it returns the string following directly after module_name as the
534  * new token. Otherwise NULL is returned.
535  * e.g. GetModuleResource("*FvwmPagerGeometry", &token, "FvwmPager")
536  * returns "Geometry" in token.
537  *
538  */
GetModuleResource(char * indata,char ** resource,char * module_name)539 char *GetModuleResource(char *indata, char **resource, char *module_name)
540 {
541 	char *tmp;
542 	char *next;
543 
544 	if (!module_name)
545 	{
546 		*resource = NULL;
547 		return indata;
548 	}
549 	tmp = PeekToken(indata, &next);
550 	if (!tmp)
551 	{
552 		return next;
553 	}
554 
555 	if (tmp[0] != '*' ||
556 	    strncasecmp(tmp+1, module_name, strlen(module_name)))
557 	{
558 		*resource = NULL;
559 		return indata;
560 	}
561 	CopyString(resource, tmp+1+strlen(module_name));
562 
563 	return next;
564 }
565 
566 
567 /*
568  *
569  * This function uses GetNextToken to parse action for up to num integer
570  * arguments. The number of values actually found is returned.
571  * If ret_action is non-NULL, a pointer to the next token is returned there.
572  * The suffixlist parameter points to a string of possible suffixes for the
573  * integer values. The index of the matched suffix is returned in
574  * ret_suffixnum (0 = no suffix, 1 = first suffix in suffixlist ...).
575  *
576  */
_get_suffixed_integer_arguments(char * action,char ** ret_action,int * retvals,int num,char * suffixlist,int * ret_suffixnum,char * parsestring)577 static int _get_suffixed_integer_arguments(
578 	char *action, char **ret_action, int *retvals, int num,
579 	char *suffixlist, int *ret_suffixnum, char *parsestring)
580 {
581 	int i;
582 	int j;
583 	int n;
584 	char *token;
585 	int suffixes;
586 
587 	/* initialize */
588 	suffixes = 0;
589 	if (suffixlist != 0)
590 	{
591 		/* if passed a suffixlist save its length */
592 		suffixes = strlen(suffixlist);
593 	}
594 
595 	for (i = 0; i < num && action; i++)
596 	{
597 		token = PeekToken(action, &action);
598 		if (token == NULL)
599 		{
600 			break;
601 		}
602 		if (sscanf(token, parsestring, &(retvals[i]), &n) < 1)
603 		{
604 			break;
605 		}
606 		if (suffixes != 0 && ret_suffixnum != NULL)
607 		{
608 			int len;
609 			char c;
610 
611 			len = strlen(token) - 1;
612 			c = token[len];
613 			if (isupper(c))
614 			{
615 				c = tolower(c);
616 			}
617 			for (j = 0; j < suffixes; j++)
618 			{
619 				char c2 = suffixlist[j];
620 
621 				if (isupper(c2))
622 				{
623 					c2 = tolower(c2);
624 				}
625 				if (c == c2)
626 				{
627 					ret_suffixnum[i] = j+1;
628 					break;
629 				}
630 			}
631 			if (j == suffixes)
632 			{
633 				ret_suffixnum[i] = 0;
634 			}
635 		}
636 		else if (token[n] != 0 && !isspace(token[n]))
637 		{
638 			/* there is a suffix but no suffix list was specified */
639 			break;
640 		}
641 	}
642 	if (ret_action != NULL)
643 	{
644 		*ret_action = action;
645 	}
646 
647 	return i;
648 }
649 
GetSuffixedIntegerArguments(char * action,char ** ret_action,int * retvals,int num,char * suffixlist,int * ret_suffixnum)650 int GetSuffixedIntegerArguments(
651 	char *action, char **ret_action, int *retvals, int num,
652 	char *suffixlist, int *ret_suffixnum)
653 {
654 	return _get_suffixed_integer_arguments(
655 		action, ret_action, retvals, num, suffixlist, ret_suffixnum,
656 		"%d%n");
657 }
658 
659 /*
660  *
661  * This function converts the suffix/number pairs returned by
662  * GetSuffixedIntegerArguments into pixels. The unit_table is an array of
663  * integers that determine the factor to multiply with in hundredths of
664  * pixels. I.e. a unit of 100 means: translate the value into pixels,
665  * 50 means divide value by 2 to get the number of pixels and so on.
666  * The unit used is determined by the suffix which is taken as the index
667  * into the table. No size checking of the unit_table is done, so make sure
668  * it is big enough before calling this function.
669  *
670  */
SuffixToPercentValue(int value,int suffix,int * unit_table)671 int SuffixToPercentValue(int value, int suffix, int *unit_table)
672 {
673 	return (value * unit_table[suffix]) / 100;
674 }
675 
676 /*
677  *
678  * This function uses GetNextToken to parse action for up to num integer
679  * arguments. The number of values actually found is returned.
680  * If ret_action is non-NULL, a pointer to the next token is returned there.
681  *
682  */
GetIntegerArguments(char * action,char ** ret_action,int * retvals,int num)683 int GetIntegerArguments(char *action, char **ret_action, int *retvals,int num)
684 {
685 	return _get_suffixed_integer_arguments(
686 		action, ret_action, retvals, num, NULL, NULL, "%d%n");
687 }
688 
689 /*
690  *
691  * Same as above, but supports hexadecimal and octal integers via 0x and 0
692  * prefixes.
693  *
694  */
GetIntegerArgumentsAnyBase(char * action,char ** ret_action,int * retvals,int num)695 int GetIntegerArgumentsAnyBase(
696 	char *action, char **ret_action, int *retvals,int num)
697 {
698 	return _get_suffixed_integer_arguments(
699 		action, ret_action, retvals, num, NULL, NULL, "%i%n");
700 }
701 
702 /*
703  *
704  * This function tries to match a token with a list of strings and returns
705  * the position of token in the array or -1 if no match is found. The last
706  * entry in the list must be NULL.
707  *
708  * len = 0 : only exact matches
709  * len < 0 : match, if token begins with one of the strings in list
710  * len > 0 : match, if the first len characters do match
711  *
712  * if next is non-NULL, *next will be set to point to the first character
713  * in token after the match.
714  *
715  */
GetTokenIndex(char * token,char ** list,int len,char ** next)716 int GetTokenIndex(char *token, char **list, int len, char **next)
717 {
718 	int i;
719 	int l;
720 	int k;
721 
722 	if (!token || !list)
723 	{
724 		if (next)
725 		{
726 			*next = NULL;
727 		}
728 		return -1;
729 	}
730 	l = (len) ? len : strlen(token);
731 	for (i = 0; list[i] != NULL; i++)
732 	{
733 		k = strlen(list[i]);
734 		if (len < 0)
735 		{
736 			l = k;
737 		}
738 		else if (len == 0 && k != l)
739 		{
740 			continue;
741 		}
742 		if (!strncasecmp(token, list[i], l))
743 		{
744 			break;
745 		}
746 	}
747 	if (next)
748 	{
749 		*next = (list[i]) ? token + l : token;
750 	}
751 
752 	return (list[i]) ? i : -1;
753 }
754 
755 /*
756  *
757  * This function does roughly the same as GetTokenIndex but reads the
758  * token from string action with GetNextToken. The index is returned
759  * in *index. The return value is a pointer to the character after the
760  * token (just like the return value of GetNextToken).
761  *
762  */
GetNextTokenIndex(char * action,char ** list,int len,int * index)763 char *GetNextTokenIndex(char *action, char **list, int len, int *index)
764 {
765 	char *token;
766 	char *next;
767 
768 	if (!index)
769 	{
770 		return action;
771 	}
772 
773 	token = PeekToken(action, &next);
774 	if (!token)
775 	{
776 		*index = -1;
777 		return action;
778 	}
779 	*index = GetTokenIndex(token, list, len, NULL);
780 
781 	return (*index == -1) ? action : next;
782 }
783 
784 
785 /*
786  *
787  * Parses two integer arguments given in the form <int><character><int>.
788  * character can be ' ' or 'x', but any other character is allowed too
789  * (except 'p' or 'P').
790  *
791  */
GetRectangleArguments(char * action,int * width,int * height)792 int GetRectangleArguments(char *action, int *width, int *height)
793 {
794 	char *token;
795 	int n;
796 
797 	token = PeekToken(action, NULL);
798 	if (!token)
799 	{
800 		return 0;
801 	}
802 	n = sscanf(token, "%d%*c%d", width, height);
803 
804 	return (n == 2) ? 2 : 0;
805 }
806 
807 /* unit_io is input as well as output. If action has a postfix 'p' or 'P',
808  * *unit_io is set to 100, otherwise it is left untouched. */
GetOnePercentArgument(char * action,int * value,int * unit_io)809 int GetOnePercentArgument(char *action, int *value, int *unit_io)
810 {
811 	unsigned int len;
812 	char *token;
813 	int n;
814 
815 	*value = 0;
816 	if (!action)
817 	{
818 		return 0;
819 	}
820 	token = PeekToken(action, NULL);
821 	if (!token)
822 	{
823 		return 0;
824 	}
825 	len = strlen(token);
826 	/* token never contains an empty string, so this is ok */
827 	if (token[len - 1] == 'p' || token[len - 1] == 'P')
828 	{
829 		*unit_io = 100;
830 		token[len - 1] = '\0';
831 	}
832 	n = sscanf(token, "%d", value);
833 
834 	return n;
835 }
836 
837 
GetTwoPercentArguments(char * action,int * val1,int * val2,int * val1_unit,int * val2_unit)838 int GetTwoPercentArguments(
839 	char *action, int *val1, int *val2, int *val1_unit, int *val2_unit)
840 {
841 	char *tok1;
842 	char *tok2;
843 	char *next;
844 	int n = 0;
845 
846 	*val1 = 0;
847 	*val2 = 0;
848 
849 	tok1 = PeekToken(action, &next);
850 	action = GetNextToken(action, &tok1);
851 	if (!tok1)
852 	{
853 		return 0;
854 	}
855 	GetNextToken(action, &tok2);
856 	if (GetOnePercentArgument(tok2, val2, val2_unit) == 1 &&
857 	    GetOnePercentArgument(tok1, val1, val1_unit) == 1)
858 	{
859 		free(tok1);
860 		free(tok2);
861 		return 2;
862 	}
863 
864 	/* now try MxN style number, specifically for DeskTopSize: */
865 	n = GetRectangleArguments(tok1, val1, val2);
866 	free(tok1);
867 	if (tok2)
868 	{
869 		free(tok2);
870 	}
871 
872 	return n;
873 }
874 
875 
876 /* Parses the next token in action and returns 1 if it is "yes", "true", "y",
877  * "t" or "on", zero if it is "no", "false", "n", "f" or "off" and -1 if it is
878  * "toggle". A pointer to the first character in action behind the token is
879  * returned through ret_action in this case. ret_action may be NULL. If the
880  * token matches none of these strings the default_ret value is returned and
881  * the action itself is passed back in ret_action. If the no_toggle flag is
882  * non-zero, the "toggle" string is handled as no match. */
ParseToggleArgument(char * action,char ** ret_action,int default_ret,char no_toggle)883 int ParseToggleArgument(
884 	char *action, char **ret_action, int default_ret, char no_toggle)
885 {
886 	int index;
887 	int rc;
888 	char *next;
889 	char *optlist[] = {
890 		"toggle",
891 		"yes", "no",
892 		"true", "false",
893 		"on", "off",
894 		"t", "f",
895 		"y", "n",
896 		NULL
897 	};
898 
899 	next = GetNextTokenIndex(action, optlist, 0, &index);
900 	if (index == 0 && no_toggle == 0)
901 	{
902 		/* toggle requested explicitly */
903 		rc = -1;
904 	}
905 	else if (index == -1 || (index == 0 && no_toggle))
906 	{
907 		/* nothing selected, use default and don't modify action */
908 		rc = default_ret;
909 		next = action;
910 	}
911 	else
912 	{
913 		/* odd numbers denote True, even numbers denote False */
914 		rc = (index & 1);
915 	}
916 	if (ret_action)
917 	{
918 		*ret_action = next;
919 	}
920 
921 	return rc;
922 }
923 
924 /* Strips the path from 'path' and returns the last component in a malloc'ed
925  * area. */
GetFileNameFromPath(char * path)926 char *GetFileNameFromPath(char *path)
927 {
928 	char *s;
929 	char *name;
930 
931 	/* we get rid of the path from program name */
932 	s = strrchr(path, '/');
933 	if (s)
934 	{
935 		s++;
936 	}
937 	else
938 	{
939 		s = path;
940 	}
941 	name = (char *)safemalloc(strlen(s)+1);
942 	strcpy(name, s);
943 
944 	return name;
945 }
946