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 /* ---------------------------- included header files ---------------------- */
17 
18 #include "config.h"
19 
20 #include <stdio.h>
21 #include <math.h>
22 
23 #include "libs/fvwmlib.h"
24 #include "libs/Parse.h"
25 #include "libs/wild.h"
26 #include "libs/FScreen.h"
27 #include "libs/charmap.h"
28 #include "libs/wcontext.h"
29 #include "fvwm.h"
30 #include "externs.h"
31 #include "execcontext.h"
32 #include "functions.h"
33 #include "conditional.h"
34 #include "misc.h"
35 #include "screen.h"
36 #include "update.h"
37 #include "style.h"
38 #include "focus.h"
39 #include "geometry.h"
40 #include "stack.h"
41 #include "commands.h"
42 #include "decorations.h"
43 #include "virtual.h"
44 #include "infostore.h"
45 
46 /* ---------------------------- local definitions -------------------------- */
47 
48 /* ---------------------------- local macros ------------------------------- */
49 
50 /* ---------------------------- imports ------------------------------------ */
51 
52 /* ---------------------------- included code files ------------------------ */
53 
54 /* ---------------------------- local types -------------------------------- */
55 
56 /* ---------------------------- forward declarations ----------------------- */
57 
58 /* ---------------------------- local variables ---------------------------- */
59 
60 /* ---------------------------- exported variables (globals) --------------- */
61 
62 /* ---------------------------- local functions ---------------------------- */
63 
64 /*
65  *
66  * Direction = 1 ==> "Next" operation
67  * Direction = -1 ==> "Previous" operation
68  * Direction = 0 ==> operation on current window (returns pass or fail)
69  *
70  */
Circulate(FvwmWindow * sf,char * action,int Direction,char ** restofline)71 static FvwmWindow *Circulate(
72 	FvwmWindow *sf, char *action, int Direction, char **restofline)
73 {
74 	int pass = 0;
75 	FvwmWindow *fw, *found = NULL;
76 	WindowConditionMask mask;
77 	char *flags;
78 
79 	/* Create window mask */
80 	flags = CreateFlagString(action, restofline);
81 	DefaultConditionMask(&mask);
82 	if (Direction == 0)
83 	{ /* override for Current [] */
84 		mask.my_flags.use_circulate_hit = 1;
85 		mask.my_flags.use_circulate_hit_icon = 1;
86 		mask.my_flags.use_circulate_hit_shaded = 1;
87 	}
88 	CreateConditionMask(flags, &mask);
89 	if (flags)
90 	{
91 		free(flags);
92 	}
93 	if (sf == NULL || Direction == 0)
94 	{
95 		sf = get_focus_window();
96 	}
97 	if (sf != NULL)
98 	{
99 		if (Direction > 0)
100 		{
101 			fw = sf->prev;
102 		}
103 		else if (Direction < 0)
104 		{
105 			fw = sf->next;
106 		}
107 		else
108 		{
109 			fw = sf;
110 		}
111 	}
112 	else
113 	{
114 		fw = NULL;
115 		if (Direction == 0)
116 		{
117 			FreeConditionMask(&mask);
118 			return NULL;
119 		}
120 	}
121 
122 	for (pass = 0; pass < 3 && !found; pass++)
123 	{
124 		while (fw && !found && fw != &Scr.FvwmRoot)
125 		{
126 			/* Make CirculateUp and CirculateDown take args. by
127 			 * Y.NOMURA */
128 			if (MatchesConditionMask(fw, &mask))
129 			{
130 				found = fw;
131 			}
132 			else
133 			{
134 				if (Direction > 0)
135 				{
136 					fw = fw->prev;
137 				}
138 				else
139 				{
140 					fw = fw->next;
141 				}
142 			}
143 			if (Direction == 0)
144 			{
145 				FreeConditionMask(&mask);
146 				return found;
147 			}
148 		}
149 		if (fw == NULL || fw == &Scr.FvwmRoot)
150 		{
151 			if (Direction > 0)
152 			{
153 				/* Go to end of list */
154 				for (fw = Scr.FvwmRoot.next; fw && fw->next;
155 				     fw = fw->next)
156 				{
157 					/* nop */
158 				}
159 			}
160 			else
161 			{
162 				/* Go to top of list */
163 				fw = Scr.FvwmRoot.next;
164 			}
165 		}
166 	}
167 	FreeConditionMask(&mask);
168 
169 	return found;
170 }
171 
circulate_cmd(F_CMD_ARGS,int new_context,int circ_dir,Bool do_use_found,Bool do_exec_on_match)172 static void circulate_cmd(
173 	F_CMD_ARGS, int new_context, int circ_dir, Bool do_use_found,
174 	Bool do_exec_on_match)
175 {
176 	FvwmWindow *found;
177 	char *restofline;
178 
179 	found = Circulate(exc->w.fw, action, circ_dir, &restofline);
180 	if (cond_rc != NULL)
181 	{
182 		cond_rc->rc = (found == NULL) ? COND_RC_NO_MATCH : COND_RC_OK;
183 	}
184 	if ((!found == !do_exec_on_match) && restofline)
185 	{
186 		const exec_context_t *exc2;
187 		exec_context_changes_t ecc;
188 		int flags;
189 
190 		ecc.w.fw = (do_use_found == True) ? found : NULL;
191 		if (found != NULL)
192 		{
193 			ecc.w.w = FW_W(found);
194 			flags = FUNC_DONT_DEFER;
195 		}
196 		else
197 		{
198 			ecc.w.w = None;
199 			flags = 0;
200 		}
201 		ecc.w.wcontext = new_context;
202 		exc2 = exc_clone_context(
203 			exc, &ecc, ECC_FW | ECC_W | ECC_WCONTEXT);
204 		execute_function(cond_rc, exc2, restofline, flags);
205 		exc_destroy_context(exc2);
206 	}
207 
208 	return;
209 }
210 
select_cmd(F_CMD_ARGS)211 static void select_cmd(F_CMD_ARGS)
212 {
213 	char *restofline;
214 	char *flags;
215 	WindowConditionMask mask;
216 	FvwmWindow * const fw = exc->w.fw;
217 
218 	if (!fw || IS_EWMH_DESKTOP(FW_W(fw)))
219 	{
220 		if (cond_rc != NULL)
221 		{
222 			cond_rc->rc = COND_RC_ERROR;
223 		}
224 		return;
225 	}
226 	flags = CreateFlagString(action, &restofline);
227 	DefaultConditionMask(&mask);
228 	mask.my_flags.use_circulate_hit = 1;
229 	mask.my_flags.use_circulate_hit_icon = 1;
230 	mask.my_flags.use_circulate_hit_shaded = 1;
231 	CreateConditionMask(flags, &mask);
232 	if (flags)
233 	{
234 		free(flags);
235 	}
236 	if (MatchesConditionMask(fw, &mask) && restofline)
237 	{
238 		if (cond_rc != NULL)
239 		{
240 			cond_rc->rc = COND_RC_OK;
241 		}
242 		execute_function_override_wcontext(
243 			cond_rc, exc, restofline, 0, C_WINDOW);
244 	}
245 	else if (cond_rc != NULL)
246 	{
247 		cond_rc->rc = COND_RC_NO_MATCH;
248 	}
249 	FreeConditionMask(&mask);
250 
251 	return;
252 }
253 
cond_check_access(char * file,int type,Bool im)254 static Bool cond_check_access(char *file, int type, Bool im)
255 {
256 	char *full_file;
257 	char *path = NULL;
258 
259 	if (!file || *file == 0)
260 	{
261 		return False;
262 	}
263 	if (file[0] == '/')
264 	{
265 		if (access(file, type) == 0)
266 		{
267 			return True;
268 		}
269 		else
270 		{
271 			return False;
272 		}
273 	}
274 	if (type != X_OK && im == False)
275 	{
276 		return False;
277 	}
278 	if (im == False)
279 	{
280 		path = getenv("PATH");
281 	}
282 	else
283 	{
284 		path = PictureGetImagePath();
285 	}
286 	if (path == NULL || *path == 0)
287 	{
288 		return False;
289 	}
290 	full_file = searchPath(path, file, NULL, type);
291 	if (full_file)
292 	{
293 		free(full_file);
294 		return True;
295 	}
296 	return False;
297 }
298 
299 /* ---------------------------- interface functions ------------------------ */
300 
301 /*
302  * Parses the flag string and returns the text between [ ] or ( )
303  * characters.  The start of the rest of the line is put in restptr.
304  * Note that the returned string is allocated here and it must be
305  * freed when it is not needed anymore.
306  * NOTE - exported via .h
307  */
CreateFlagString(char * string,char ** restptr)308 char *CreateFlagString(char *string, char **restptr)
309 {
310 	char *retval;
311 	char *c;
312 	char *start;
313 	char closeopt;
314 	int length;
315 
316 	c = string;
317 	while (isspace((unsigned char)*c) && (*c != 0))
318 	{
319 		c++;
320 	}
321 
322 
323 	if (*c == '[' || *c == '(')
324 	{
325 		char *d;
326 
327 		/* Get the text between [ ] or ( ) */
328 		if (*c == '[')
329 		{
330 			closeopt = ']';
331 		}
332 		else
333 		{
334 			closeopt = ')';
335 		}
336 		c++;
337 		start = c;
338 		length = 0;
339 		while (*c != closeopt)
340 		{
341 			if (*c == 0)
342 			{
343 				fvwm_msg(ERR, "CreateFlagString",
344 					 "Conditionals require closing "
345 					 "parenthesis");
346 				*restptr = NULL;
347 				return NULL;
348 			}
349 
350 			/* skip quoted string */
351 			d = SkipQuote(c, NULL, NULL, NULL);
352 			length += d - c;
353 			c = d;
354 		}
355 
356 		/* We must allocate a new string because we null terminate the
357 		 * string between the [ ] or ( ) characters. */
358 		retval = safemalloc(length + 1);
359 		strncpy(retval, start, length);
360 		retval[length] = 0;
361 
362 		*restptr = c + 1;
363 	}
364 	else
365 	{
366 		retval = NULL;
367 		*restptr = c;
368 	}
369 
370 	return retval;
371 }
372 
373 /*
374  * The name_condition field of the mask is allocated in CreateConditionMask.
375  * It must be freed.
376  * NOTE - exported via .h
377  */
FreeConditionMask(WindowConditionMask * mask)378 void FreeConditionMask(WindowConditionMask *mask)
379 {
380 	struct name_condition *pp,*pp2;
381 	struct namelist *p,*p2;
382 
383 	for (pp=mask->name_condition; pp; )
384 	{
385 		/* One malloc() is done for all the name strings.
386 		   The string is tokenised & the name fields point to
387 		   different parts of the one string.
388 		   The start of the string is the first name in the string
389 		   which is actually the last node in the linked list. */
390 		for (p=pp->namelist; p; )
391 		{
392 			p2=p->next;
393 			if(!p2)
394 			{
395 				free(p->name);
396 			}
397 			free(p);
398 			p=p2;
399 		}
400 		pp2=pp->next;
401 		free(pp);
402 		pp=pp2;
403 	}
404 }
405 
406 /* Assign the default values for the window mask
407  * NOTE - exported via .h */
DefaultConditionMask(WindowConditionMask * mask)408 void DefaultConditionMask(WindowConditionMask *mask)
409 {
410 	memset(mask, 0, sizeof(WindowConditionMask));
411 	/* -2  means no layer condition, -1 means current */
412 	mask->layer = -2;
413 
414 	return;
415 }
416 
417 /*
418  * Note that this function allocates the name field of the mask struct.
419  * FreeConditionMask must be called for the mask when the mask is discarded.
420  * NOTE - exported via .h
421  */
CreateConditionMask(char * flags,WindowConditionMask * mask)422 void CreateConditionMask(char *flags, WindowConditionMask *mask)
423 {
424 	char *allocated_condition;
425 	char *next_condition;
426 	char *condition;
427 	char *tmp;
428 	unsigned int state;
429 
430 	if (flags == NULL)
431 	{
432 		return;
433 	}
434 
435 	/* Next parse the flags in the string. */
436 	next_condition = GetNextFullOption(flags, &allocated_condition);
437 	condition = PeekToken(allocated_condition, &tmp);
438 
439 	while (condition)
440 	{
441 		char *cond;
442 		int on;
443 
444 		cond = condition;
445 		on = 1;
446 		if (*cond == '!')
447 		{
448 			on = 0;
449 			cond++;
450 		}
451 		if (StrEquals(cond,"AcceptsFocus"))
452 		{
453 			mask->my_flags.do_accept_focus = on;
454 			mask->my_flags.use_do_accept_focus = 1;
455 		}
456 		else if (StrEquals(cond,"Focused"))
457 		{
458 			mask->my_flags.needs_focus =
459 				(on) ? NEEDS_TRUE : NEEDS_FALSE;
460 		}
461 		else if (StrEquals(cond,"HasPointer"))
462 		{
463 			mask->my_flags.needs_pointer =
464 				(on) ? NEEDS_TRUE : NEEDS_FALSE;
465 		}
466 		else if (StrEquals(cond,"Iconic"))
467 		{
468 			SET_ICONIFIED(mask, on);
469 			SETM_ICONIFIED(mask, 1);
470 		}
471 		else if (StrEquals(cond,"Visible"))
472 		{
473 			SET_PARTIALLY_VISIBLE(mask, on);
474 			SETM_PARTIALLY_VISIBLE(mask, 1);
475 		}
476 		else if (StrEquals(cond,"Overlapped"))
477 		{
478 			mask->my_flags.needs_overlapped = on;
479 			mask->my_flags.do_check_overlapped = 1;
480 		}
481 		else if (StrEquals(cond,"PlacedByButton"))
482 		{
483 			int button;
484 			int button_mask;
485 
486 			if (sscanf(tmp, "%d", &button) &&
487 			    (button >= 1 &&
488 			     button <= NUMBER_OF_EXTENDED_MOUSE_BUTTONS))
489 			{
490 				tmp = SkipNTokens(tmp, 1);
491 				button_mask = (1<<(button-1));
492 			}
493 			else
494 			{
495 				button_mask =
496 				     (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS) - 1;
497 			}
498 			if (on)
499 			{
500 				if (mask->placed_by_button_mask &
501 				    mask->placed_by_button_set_mask &
502 				    ~button_mask)
503 				{
504 				  	  fvwm_msg(WARN, "PlacedByButton",
505 						   "Condition always False.");
506 				}
507 				mask->placed_by_button_mask |= button_mask;
508 			}
509 			else
510 			{
511 				mask->placed_by_button_mask &= ~button_mask;
512 			}
513 			mask->placed_by_button_set_mask |= button_mask;
514 		}
515 		else if (StrEquals(cond,"PlacedByButton3"))
516 		{
517 			if (on)
518 			{
519 				if (mask->placed_by_button_mask &
520 				    mask->placed_by_button_set_mask & ~(1<<2))
521 				{
522 				  	  fvwm_msg(WARN, "PlacedByButton3",
523 						   "Condition always False.");
524 				}
525 				mask->placed_by_button_mask |= (1<<2);
526 			}
527 			else
528 			{
529 				mask->placed_by_button_mask &= ~(1<<2);
530 			}
531 			mask->placed_by_button_set_mask |= (1<<2);
532 		}
533 		else if (StrEquals(cond,"Raised"))
534 		{
535 			SET_FULLY_VISIBLE(mask, on);
536 			SETM_FULLY_VISIBLE(mask, 1);
537 		}
538 		else if (StrEquals(cond,"Sticky"))
539 		{
540 			SET_STICKY_ACROSS_PAGES(mask, on);
541 			SET_STICKY_ACROSS_DESKS(mask, on);
542 			SETM_STICKY_ACROSS_PAGES(mask, 1);
543 			SETM_STICKY_ACROSS_DESKS(mask, 1);
544 		}
545 		else if (StrEquals(cond,"StickyAcrossPages"))
546 		{
547 			SET_STICKY_ACROSS_PAGES(mask, on);
548 			SETM_STICKY_ACROSS_PAGES(mask, 1);
549 		}
550 		else if (StrEquals(cond,"StickyAcrossDesks"))
551 		{
552 			SET_STICKY_ACROSS_DESKS(mask, on);
553 			SETM_STICKY_ACROSS_DESKS(mask, 1);
554 		}
555 		else if (StrEquals(cond,"StickyIcon"))
556 		{
557 			SET_ICON_STICKY_ACROSS_PAGES(mask, on);
558 			SET_ICON_STICKY_ACROSS_DESKS(mask, on);
559 			SETM_ICON_STICKY_ACROSS_PAGES(mask, 1);
560 			SETM_ICON_STICKY_ACROSS_DESKS(mask, 1);
561 		}
562 		else if (StrEquals(cond,"StickyAcrossPagesIcon"))
563 		{
564 			SET_ICON_STICKY_ACROSS_PAGES(mask, on);
565 			SETM_ICON_STICKY_ACROSS_PAGES(mask, 1);
566 		}
567 		else if (StrEquals(cond,"StickyAcrossDesksIcon"))
568 		{
569 			SET_ICON_STICKY_ACROSS_DESKS(mask, on);
570 			SETM_ICON_STICKY_ACROSS_DESKS(mask, 1);
571 		}
572 		else if (StrEquals(cond,"Maximized"))
573 		{
574 			SET_MAXIMIZED(mask, on);
575 			SETM_MAXIMIZED(mask, 1);
576 		}
577 		else if (StrEquals(cond, "Fullscreen"))
578 		{
579 			mask->my_flags.do_check_fullscreen = on;
580 		}
581 		else if (StrEquals(cond,"FixedSize"))
582 		{
583 			/* don't set mask here, because we make the test here
584 			   (and don't compare against window's mask)
585 			   by checking allowed function */
586 			SET_SIZE_FIXED(mask, on);
587 			SETM_SIZE_FIXED(mask, 1);
588 		}
589 		else if (StrEquals(cond, "FixedPosition"))
590 		{
591 			SET_FIXED(mask, on);
592 			SETM_FIXED(mask, 1);
593 		}
594 		else if (StrEquals(cond,"HasHandles"))
595 		{
596 			SET_HAS_HANDLES(mask, on);
597 			SETM_HAS_HANDLES(mask, 1);
598 		}
599 		else if (StrEquals(cond,"Iconifiable"))
600 		{
601 			SET_IS_UNICONIFIABLE(mask, !on);
602 			SETM_IS_UNICONIFIABLE(mask, 1);
603 		}
604 		else if (StrEquals(cond,"Maximizable"))
605 		{
606 			SET_IS_UNMAXIMIZABLE(mask, !on);
607 			SETM_IS_UNMAXIMIZABLE(mask, 1);
608 		}
609 		else if (StrEquals(cond,"Closable"))
610 		{
611 			SET_IS_UNCLOSABLE(mask, !on);
612 			SETM_IS_UNCLOSABLE(mask, 1);
613 		}
614 		else if (StrEquals(cond,"Shaded"))
615 		{
616 			SET_SHADED(mask, on);
617 			SETM_SHADED(mask, 1);
618 		}
619 		else if (StrEquals(cond,"Transient"))
620 		{
621 			SET_TRANSIENT(mask, on);
622 			SETM_TRANSIENT(mask, 1);
623 		}
624 		else if (StrEquals(cond,"PlacedByFvwm"))
625 		{
626 			SET_PLACED_BY_FVWM(mask, on);
627 			SETM_PLACED_BY_FVWM(mask, 1);
628 		}
629 		else if (StrEquals(cond,"CurrentDesk"))
630 		{
631 			mask->my_flags.needs_current_desk = on;
632 			mask->my_flags.do_check_desk = 1;
633 		}
634 		else if (StrEquals(cond,"CurrentPage"))
635 		{
636 			mask->my_flags.needs_current_desk_and_page = on;
637 			mask->my_flags.do_check_desk_and_page = 1;
638 		}
639 		else if (StrEquals(cond,"CurrentGlobalPage"))
640 		{
641 			mask->my_flags.needs_current_desk_and_global_page = on;
642 			mask->my_flags.do_check_desk_and_global_page = 1;
643 		}
644 		else if (StrEquals(cond,"CurrentPageAnyDesk") ||
645 			 StrEquals(cond,"CurrentScreen"))
646 		{
647 			mask->my_flags.needs_current_page = on;
648 			mask->my_flags.do_check_page = 1;
649 		}
650 		else if (StrEquals(cond,"AnyScreen"))
651 		{
652 			mask->my_flags.do_not_check_screen = on;
653 		}
654 		else if (StrEquals(cond,"CurrentGlobalPageAnyDesk"))
655 		{
656 			mask->my_flags.needs_current_global_page = on;
657 			mask->my_flags.do_check_global_page = 1;
658 		}
659 		else if (StrEquals(cond,"CirculateHit"))
660 		{
661 			mask->my_flags.use_circulate_hit = on;
662 		}
663 		else if (StrEquals(cond,"CirculateHitIcon"))
664 		{
665 			mask->my_flags.use_circulate_hit_icon = on;
666 		}
667 		else if (StrEquals(cond,"CirculateHitShaded"))
668 		{
669 			mask->my_flags.use_circulate_hit_shaded = on;
670 		}
671 		else if (StrEquals(cond,"State"))
672 		{
673 			if (sscanf(tmp, "%u", &state) && state <= 31)
674 			{
675 				state = (1 << state);
676 				if (on)
677 				{
678 					SET_USER_STATES(mask, state);
679 				}
680 				else
681 				{
682 					CLEAR_USER_STATES(mask, state);
683 				}
684 				SETM_USER_STATES(mask, state);
685 				tmp = SkipNTokens(tmp, 1);
686 			}
687 		}
688 		else if (StrEquals(cond, "Layer"))
689 		{
690 			if (sscanf(tmp, "%d", &mask->layer))
691 			{
692 				tmp = SkipNTokens(tmp, 1);
693 				if (mask->layer < 0)
694 				{
695 					/* silently ignore invalid layers */
696 					mask->layer = -2;
697 				}
698 			}
699 			else
700 			{
701 				/* needs current layer */
702 				mask->layer = -1;
703 			}
704 			mask->my_flags.needs_same_layer = on;
705 		}
706 		else if (StrEquals(cond, "Desk"))
707 		{
708 			if (sscanf(tmp, "%d", &mask->desk)) {
709 				tmp = SkipNTokens(tmp, 1);
710 			}
711 			mask->my_flags.do_check_cond_desk = on;
712 		}
713 		else if (StrEquals(cond, "Screen"))
714 		{
715 			if (sscanf(tmp, "%d", &mask->screen)) {
716 				tmp = SkipNTokens(tmp, 1);
717 			}
718 			mask->my_flags.do_check_screen = 1;
719 
720 			if (!on)
721 				mask->my_flags.do_not_check_screen = 1;
722 		}
723 		else
724 		{
725 			struct name_condition *pp;
726 			struct namelist *p;
727 			char *condp = safestrdup(cond);
728 
729 			pp = (struct name_condition *)
730 				safemalloc(sizeof(struct name_condition));
731 			pp->invert = (!on ? True : False);
732 			pp->namelist = NULL;
733 			pp->next = mask->name_condition;
734 			mask->name_condition = pp;
735 			for (;;)
736 			{
737 				p = (struct namelist *)
738 					safemalloc(sizeof(struct namelist));
739 				p->name=condp;
740 				p->next=pp->namelist;
741 				pp->namelist=p;
742 				while(*condp && *condp != '|')
743 				{
744 					condp++;
745 				}
746 				if(!*condp)
747 				{
748 					break;
749 				}
750 				*condp++='\0';
751 			}
752 		}
753 
754 		if (tmp && *tmp)
755 		{
756 			fvwm_msg(OLD, "CreateConditionMask",
757 				 "Use comma instead of whitespace to "
758 				 "separate conditions");
759 		}
760 		else
761 		{
762 			if (allocated_condition != NULL)
763 			{
764 				free(allocated_condition);
765 				allocated_condition = NULL;
766 			}
767 			if (next_condition && *next_condition)
768 			{
769 				next_condition = GetNextFullOption(
770 					next_condition, &allocated_condition);
771 			}
772 			tmp = allocated_condition;
773 		}
774 		condition = PeekToken(tmp, &tmp);
775 	}
776 
777 	return;
778 }
779 
780 /*
781  * Checks whether the given window matches the mask created with
782  * CreateConditionMask.
783  * NOTE - exported via .h
784  */
MatchesConditionMask(FvwmWindow * fw,WindowConditionMask * mask)785 Bool MatchesConditionMask(FvwmWindow *fw, WindowConditionMask *mask)
786 {
787 	int does_match;
788 	int is_on_desk;
789 	int is_on_page;
790 	int is_on_global_page;
791 	FvwmWindow *sf = get_focus_window();
792 	struct name_condition *pp;
793 	struct namelist *p;
794 	char *name;
795 
796 	/* match FixedSize conditional */
797 	/* special treatment for FixedSize, because more than just
798 	   the is_size_fixed flag makes a window unresizable (width and height
799 	   hints etc.) */
800 	if (IS_SIZE_FIXED(mask) &&
801 	    mask->flag_mask.common.s.is_size_fixed &&
802 	    is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False))
803 	{
804 	        return False;
805 	}
806 	if (!IS_SIZE_FIXED(mask) &&
807 	    mask->flag_mask.common.s.is_size_fixed &&
808 	    !is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False))
809 	{
810 	        return False;
811 	}
812 	if (IS_FIXED(mask) &&
813 	    mask->flag_mask.common.s.is_fixed &&
814 	    is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
815 	{
816 	        return False;
817 	}
818 	if (!IS_FIXED(mask) &&
819 	    mask->flag_mask.common.s.is_fixed &&
820 	    !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
821 	{
822 	        return False;
823 	}
824 	if (IS_UNICONIFIABLE(mask) &&
825 	    mask->flag_mask.common.s.is_uniconifiable &&
826 	    is_function_allowed(F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False))
827 	{
828 	        return False;
829 	}
830 	if (
831 		!IS_UNICONIFIABLE(mask) &&
832 		mask->flag_mask.common.s.is_uniconifiable &&
833 		!is_function_allowed(
834 			F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False))
835 	{
836 	        return False;
837 	}
838 	if (
839 		IS_UNMAXIMIZABLE(mask) &&
840 		mask->flag_mask.common.s.is_unmaximizable &&
841 		is_function_allowed(
842 			F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
843 	{
844 	        return False;
845 	}
846 	if (
847 		!IS_UNMAXIMIZABLE(mask) &&
848 		mask->flag_mask.common.s.is_unmaximizable &&
849 		!is_function_allowed(
850 			F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
851 	{
852 	        return False;
853 	}
854 	if (
855 		IS_UNCLOSABLE(mask) &&
856 		mask->flag_mask.common.s.is_unclosable &&
857 		(is_function_allowed(
858 			 F_CLOSE, NULL, fw, RQORIG_PROGRAM_US,False) ||
859 		 is_function_allowed(
860 			 F_DELETE, NULL, fw, RQORIG_PROGRAM_US,False) ||
861 	     is_function_allowed(
862 		     F_DESTROY, NULL, fw, RQORIG_PROGRAM_US, False)))
863 	{
864 	        return False;
865 	}
866 	if (
867 		!IS_UNCLOSABLE(mask) &&
868 		mask->flag_mask.common.s.is_unclosable &&
869 		(!is_function_allowed(
870 			 F_CLOSE, NULL, fw, RQORIG_PROGRAM_US,False) &&
871 		 !is_function_allowed(
872 			 F_DELETE, NULL, fw, RQORIG_PROGRAM_US,False) &&
873 		 !is_function_allowed(
874 			 F_DESTROY, NULL, fw, RQORIG_PROGRAM_US,False)))
875 	{
876 	        return False;
877 	}
878 	if (!blockcmpmask((char *)&(fw->flags), (char *)&(mask->flags),
879 			  (char *)&(mask->flag_mask), sizeof(fw->flags)))
880 	{
881 		return False;
882 	}
883 	if (!mask->my_flags.use_circulate_hit && DO_SKIP_CIRCULATE(fw))
884 	{
885 		return False;
886 	}
887 	if (!mask->my_flags.use_circulate_hit_icon && IS_ICONIFIED(fw) &&
888 	    DO_SKIP_ICON_CIRCULATE(fw))
889 	{
890 		return False;
891 	}
892 	if (!mask->my_flags.use_circulate_hit_shaded && IS_SHADED(fw) &&
893 	    DO_SKIP_SHADED_CIRCULATE(fw))
894 	{
895 		return False;
896 	}
897 	if (IS_ICONIFIED(fw) && IS_TRANSIENT(fw) && IS_ICONIFIED_BY_PARENT(fw))
898 	{
899 		return False;
900 	}
901 
902 	/* desk and page matching */
903 	is_on_desk = 1;
904 	if (mask->my_flags.do_check_desk ||
905 	    mask->my_flags.do_check_desk_and_page ||
906 	    mask->my_flags.do_check_desk_and_global_page)
907 	{
908 		is_on_desk = (fw->Desk == Scr.CurrentDesk);
909 	}
910 	is_on_page = 1;
911 	if (mask->my_flags.do_check_page ||
912 	    mask->my_flags.do_check_desk_and_page)
913 	{
914 		if (FScreenIsEnabled() && !mask->my_flags.do_not_check_screen)
915 		{
916 			is_on_page = !!FScreenIsRectangleOnScreen(
917 				NULL, FSCREEN_CURRENT, &(fw->g.frame));
918 		}
919 		else
920 		{
921 			is_on_page = !!IsRectangleOnThisPage(
922 				&(fw->g.frame), Scr.CurrentDesk);
923 		}
924 	}
925 	is_on_global_page = 1;
926 	if (mask->my_flags.do_check_global_page ||
927 	    mask->my_flags.do_check_desk_and_global_page)
928 	{
929 		is_on_global_page = !!IsRectangleOnThisPage(
930 			&(fw->g.frame), Scr.CurrentDesk);
931 	}
932 
933 	if (mask->my_flags.do_check_desk_and_page)
934 	{
935 		int is_on_desk_and_page;
936 
937 		is_on_desk_and_page = (is_on_desk && is_on_page);
938 		if (mask->my_flags.needs_current_desk_and_page !=
939 		    is_on_desk_and_page)
940 		{
941 			return False;
942 		}
943 	}
944 	else if (mask->my_flags.do_check_desk_and_global_page)
945 	{
946 		int is_on_desk_and_global_page;
947 
948 		is_on_desk_and_global_page = (is_on_desk && is_on_global_page);
949 		if (mask->my_flags.needs_current_desk_and_global_page !=
950 		    is_on_desk_and_global_page)
951 		{
952 			return False;
953 		}
954 	}
955 	if (mask->my_flags.do_check_desk &&
956 	    mask->my_flags.needs_current_desk != is_on_desk)
957 	{
958 		return False;
959 	}
960 	if (mask->my_flags.do_check_page)
961 	{
962 		if (mask->my_flags.needs_current_page != is_on_page)
963 		{
964 			return False;
965 		}
966 	}
967 	else if (mask->my_flags.do_check_global_page)
968 	{
969 		if (mask->my_flags.needs_current_global_page !=
970 		    is_on_global_page)
971 		{
972 			return False;
973 		}
974 	}
975 
976 	for (pp = mask->name_condition; pp; pp = pp->next)
977 	{
978 		does_match = 0;
979 		for (p = pp->namelist; p; p = p->next)
980 		{
981 			name=p->name;
982 			does_match |= matchWildcards(name, fw->name.name);
983 			does_match |= matchWildcards(name, fw->icon_name.name);
984 			if(fw->class.res_class)
985 				does_match |= matchWildcards(name,
986 					fw->class.res_class);
987 			if(fw->class.res_name)
988 				does_match |= matchWildcards(name,
989 					fw->class.res_name);
990 		}
991 		if(( pp->invert &&  does_match) ||
992 		   (!pp->invert && !does_match))
993 		{
994 			return False;
995 		}
996 	}
997 
998 	if (mask->my_flags.do_check_fullscreen)
999 	{
1000 		return IS_EWMH_FULLSCREEN(fw);
1001 	}
1002 
1003 	if (mask->layer == -1 && sf)
1004 	{
1005 		int is_same_layer;
1006 
1007 		is_same_layer = (fw->layer == sf->layer);
1008 		if (mask->my_flags.needs_same_layer != is_same_layer)
1009 		{
1010 			return False;
1011 		}
1012 	}
1013 	if (mask->layer >= 0)
1014 	{
1015 		int is_same_layer;
1016 
1017 		is_same_layer = (fw->layer == mask->layer);
1018 		if (mask->my_flags.needs_same_layer != is_same_layer)
1019 		{
1020 			return False;
1021 		}
1022 	}
1023 	if (mask->placed_by_button_set_mask)
1024 	{
1025 		if (!((mask->placed_by_button_set_mask &
1026 			(1<<(fw->placed_by_button - 1))) ==
1027 		     (mask->placed_by_button_set_mask &
1028 		      mask->placed_by_button_mask)))
1029 		{
1030 			return False;
1031 		}
1032 	}
1033 	if (GET_USER_STATES(mask) !=
1034 	    (mask->flag_mask.common.user_states & GET_USER_STATES(fw)))
1035 	{
1036 		return False;
1037 	}
1038 	if (mask->my_flags.use_do_accept_focus)
1039 	{
1040 		Bool f;
1041 
1042 		f = focus_does_accept_input_focus(fw);
1043 		if (fw && !FP_DO_FOCUS_BY_FUNCTION(FW_FOCUS_POLICY(fw)))
1044 		{
1045 			f = False;
1046 		}
1047 		else if (fw && FP_IS_LENIENT(FW_FOCUS_POLICY(fw)))
1048 		{
1049 			f = True;
1050 		}
1051 		if (!f != !mask->my_flags.do_accept_focus)
1052 		{
1053 			return False;
1054 		}
1055 	}
1056 	if (mask->my_flags.needs_focus != NEEDS_ANY)
1057 	{
1058 		int is_focused;
1059 
1060 		is_focused = (fw == get_focus_window());
1061 		if (!is_focused && mask->my_flags.needs_focus == NEEDS_TRUE)
1062 		{
1063 			return False;
1064 		}
1065 		else if (is_focused &&
1066 			 mask->my_flags.needs_focus == NEEDS_FALSE)
1067 		{
1068 			return False;
1069 		}
1070 	}
1071 	if (mask->my_flags.needs_pointer != NEEDS_ANY)
1072 	{
1073 		int has_pointer;
1074 		FvwmWindow *t;
1075 
1076 		t = get_pointer_fvwm_window();
1077 		if (t != NULL && t == fw)
1078 		{
1079 			has_pointer = 1;
1080 		}
1081 		else
1082 		{
1083 			has_pointer = 0;
1084 		}
1085 		if (!has_pointer && mask->my_flags.needs_pointer == NEEDS_TRUE)
1086 		{
1087 			return False;
1088 		}
1089 		else if (has_pointer &&
1090 			 mask->my_flags.needs_pointer == NEEDS_FALSE)
1091 		{
1092 			return False;
1093 		}
1094 	}
1095 	if (mask->my_flags.do_check_overlapped)
1096 	{
1097 		int is_o;
1098 
1099 		is_o = (is_on_top_of_layer(fw) == False);
1100 		if (is_o != mask->my_flags.needs_overlapped)
1101 		{
1102 			return False;
1103 		}
1104 	}
1105 	if (mask->my_flags.do_check_cond_desk)
1106 	{
1107 		if (fw->Desk == mask->desk)
1108 			return True;
1109 		else
1110 			return False;
1111 
1112 		if (fw->Desk != mask->desk)
1113 			return True;
1114 		else
1115 			return False;
1116 	}
1117 	if (mask->my_flags.do_check_screen)
1118 	{
1119 		rectangle	 g;
1120 		int		 scr;
1121 
1122 		get_unshaded_geometry(fw, &g);
1123 		scr = FScreenOfPointerXY(g.x, g.y);
1124 
1125 		if (mask->my_flags.do_not_check_screen) {
1126 			/* Negation of (!screen n) specified. */
1127 			return (scr != mask->screen);
1128 		} else
1129 			return (scr == mask->screen);
1130 	}
1131 
1132 	return True;
1133 }
1134 
direction_cmd(F_CMD_ARGS,Bool is_scan)1135 static void direction_cmd(F_CMD_ARGS, Bool is_scan)
1136 {
1137 	rectangle my_g;
1138 	rectangle his_g;
1139 	int my_cx;
1140 	int my_cy;
1141 	int his_cx;
1142 	int his_cy;
1143 	int cross = 0;
1144 	int offset = 0;
1145 	int distance = 0;
1146 	int cycle = False;
1147 	int forward = False;
1148 	int score;
1149 	int best_cross = 0;
1150 	int best_score;
1151 	int worst_score = -1;
1152 	FvwmWindow *tfw;
1153 	FvwmWindow *fw_best;
1154 	int dir;
1155 	int dir2;
1156 	Bool right_handed=False;
1157 	char *flags;
1158 	char *restofline;
1159 	char *tmp;
1160 	float tx;
1161 	float ty;
1162 	WindowConditionMask mask;
1163 	Bool is_pointer_relative;
1164 	FvwmWindow * const fw = exc->w.fw;
1165 
1166 	/* Parse the direction. */
1167 	tmp = PeekToken(action, &action);
1168 	if (StrEquals(tmp, "FromPointer"))
1169 	{
1170 		is_pointer_relative = True;
1171 		tmp = PeekToken(action, &action);
1172 	}
1173 	else
1174 	{
1175 		is_pointer_relative = False;
1176 	}
1177 	dir = gravity_parse_dir_argument(tmp, NULL, -1);
1178 	if (dir == -1 || dir > DIR_ALL_MASK)
1179 	{
1180 		fvwm_msg(ERR, "Direction", "Invalid direction %s",
1181 			 (tmp) ? tmp : "");
1182 		if (cond_rc != NULL)
1183 		{
1184 			cond_rc->rc = COND_RC_ERROR;
1185 		}
1186 		return;
1187 	}
1188 	if (is_scan)
1189 	{
1190 		cycle = True;
1191 		tmp = PeekToken(action, &action);
1192 		if ( ! tmp )
1193 		{
1194 			fvwm_msg(
1195 				ERR, "Direction", "Missing minor direction %s",
1196 				(tmp) ? tmp : "");
1197 			if (cond_rc != NULL)
1198 			{
1199 				cond_rc->rc = COND_RC_ERROR;
1200 			}
1201 			return;
1202 		}
1203 		dir2 = gravity_parse_dir_argument(tmp, NULL, -1);
1204 		/* if enum direction_t changes, this is trashed. */
1205 		if (dir2 == -1 || dir2 > DIR_NW ||
1206 		    (dir < 4) != (dir2 < 4) || (abs(dir - dir2) & 1) != 1)
1207 		{
1208 			fvwm_msg(
1209 				ERR, "Direction", "Invalid minor direction %s",
1210 				(tmp) ? tmp : "");
1211 			if (cond_rc != NULL)
1212 			{
1213 				cond_rc->rc = COND_RC_ERROR;
1214 			}
1215 			return;
1216 		}
1217 		else if (dir2 - dir == 1 || dir2 - dir == -3)
1218 		{
1219 			right_handed=True;
1220 		}
1221 	}
1222 
1223 	/* Create the mask for flags */
1224 	flags = CreateFlagString(action, &restofline);
1225 	if (!restofline)
1226 	{
1227 		if (flags)
1228 		{
1229 			free(flags);
1230 		}
1231 		if (cond_rc != NULL)
1232 		{
1233 			cond_rc->rc = COND_RC_NO_MATCH;
1234 		}
1235 		return;
1236 	}
1237 	DefaultConditionMask(&mask);
1238 	CreateConditionMask(flags, &mask);
1239 	if (flags)
1240 	{
1241 		free(flags);
1242 	}
1243 
1244 	/* If there is a focused window, use that as a starting point.
1245 	 * Otherwise we use the pointer as a starting point. */
1246 	if (fw && is_pointer_relative == False)
1247 	{
1248 		get_visible_window_or_icon_geometry(fw, &my_g);
1249 		my_cx = my_g.x + my_g.width / 2;
1250 		my_cy = my_g.y + my_g.height / 2;
1251 	}
1252 	else
1253 	{
1254 		if (FQueryPointer(
1255 			    dpy, Scr.Root, &JunkRoot, &JunkChild, &my_g.x,
1256 			    &my_g.y, &JunkX, &JunkY, &JunkMask) == False)
1257 		{
1258 			/* pointer is on a different screen */
1259 			my_g.x = 0;
1260 			my_g.y = 0;
1261 		}
1262 		my_g.width = 1;
1263 		my_g.height = 1;
1264 		my_cx = my_g.x;
1265 		my_cy = my_g.y;
1266 	}
1267 
1268 	/* Next we iterate through all windows and choose the closest one in
1269 	 * the wanted direction. */
1270 	fw_best = NULL;
1271 	best_score = -1;
1272 	for (tfw = Scr.FvwmRoot.next; tfw != NULL; tfw = tfw->next)
1273 	{
1274 		/* Skip every window that does not match conditionals.  Also
1275 		 * skip the currently focused window.  That would be too
1276 		 * close. :) */
1277 		if (tfw == fw || !MatchesConditionMask(tfw, &mask))
1278 		{
1279 			continue;
1280 		}
1281 
1282 		/* Calculate relative location of the window. */
1283 		get_visible_window_or_icon_geometry(tfw, &his_g);
1284 		his_g.x -= my_cx;
1285 		his_g.y -= my_cy;
1286 		his_cx = his_g.x + his_g.width / 2;
1287 		his_cy = his_g.y + his_g.height / 2;
1288 
1289 		if (dir > DIR_MAJOR_MASK && dir <= DIR_MINOR_MASK)
1290 		{
1291 			int tx;
1292 			/* Rotate the diagonals 45 degrees counterclockwise. To
1293 			 * do this, multiply the matrix /+h +h\ with the vector
1294 			 * (x y).                       \-h +h/
1295 			 * h = sqrt(0.5). We can set h := 1 since absolute
1296 			 * distance doesn't * matter here. */
1297 			tx = his_cx + his_cy;
1298 			his_cy = -his_cx + his_cy;
1299 			his_cx = tx;
1300 		}
1301 		/* Arrange so that distance and offset are positive in desired
1302 		 * direction. */
1303 		switch (dir)
1304 		{
1305 		case DIR_S:
1306 		case DIR_SW:
1307 			forward = True;
1308 		case DIR_N:
1309 		case DIR_NE:
1310 			cross = -his_cx;
1311 			offset = (his_cx < 0) ? -his_cx : his_cx;
1312 			distance = (dir == DIR_N || dir == DIR_NE) ?
1313 				-his_cy : his_cy;
1314 			break;
1315 		case DIR_E: /* E */
1316 		case DIR_SE: /* SE */
1317 			forward = True;
1318 		case DIR_W: /* W */
1319 		case DIR_NW: /* NW */
1320 			cross = his_cy;
1321 			offset = (his_cy < 0) ? -his_cy : his_cy;
1322 			distance = (dir == DIR_W || dir == DIR_NW) ?
1323 				-his_cx : his_cx;
1324 			break;
1325 		case DIR_C:
1326 			offset = 0;
1327 			tx = (float)his_cx;
1328 			ty = (float)his_cy;
1329 			distance = (int)sqrt(tx * tx + ty * ty);
1330 			break;
1331 		}
1332 
1333 		if (cycle)
1334 		{
1335 			offset=0;
1336 		}
1337 		else if (distance < 0)
1338 		{
1339 			/* Target must be in given direction. */
1340 			continue;
1341 		}
1342 		else if (distance == 0 && dir != DIR_C)
1343 		{
1344 			continue;
1345 		}
1346 
1347 		/* Calculate score for this window.  The smaller the better. */
1348 		score = distance + offset;
1349 		if (!right_handed)
1350 		{
1351 			cross= -cross;
1352 		}
1353 		if (cycle)
1354 		{
1355 			int ordered = (forward == (cross < best_cross));
1356 
1357 			if (distance < 0 && best_score == -1 &&
1358 				(score < worst_score ||
1359 				(score == worst_score && ordered)))
1360 			{
1361 				fw_best = tfw;
1362 				worst_score = score;
1363 				best_cross = cross;
1364 			}
1365 			if (score == 0 && forward == (cross < 0) &&
1366 			    dir != DIR_C)
1367 			{
1368 				continue;
1369 			}
1370 			if (distance >= 0 &&
1371 				(best_score == -1 || score < best_score ||
1372 				 (score == best_score && ordered)))
1373 			{
1374 				fw_best = tfw;
1375 				best_score = score;
1376 				best_cross = cross;
1377 			}
1378 		}
1379 		else
1380 		{
1381 			/* windows more than 45 degrees off the direction are
1382 			 * heavily penalized and will only be chosen if nothing
1383 			 * else within a million pixels */
1384 			if (offset > distance)
1385 			{
1386 				score += 1000000;
1387 			}
1388 			if (best_score == -1 || score < best_score ||
1389 			    (score == best_score && dir == DIR_C))
1390 			{
1391 				fw_best = tfw;
1392 				best_score = score;
1393 			}
1394 		}
1395 	} /* for */
1396 
1397 	if (fw_best)
1398 	{
1399 		if (cond_rc != NULL)
1400 		{
1401 			cond_rc->rc = COND_RC_OK;
1402 		}
1403 		execute_function_override_window(
1404 			cond_rc, exc, restofline, 0, fw_best);
1405 	}
1406 	else if (cond_rc != NULL)
1407 	{
1408 		cond_rc->rc = COND_RC_NO_MATCH;
1409 	}
1410 	FreeConditionMask(&mask);
1411 
1412 	return;
1413 }
1414 
__rc_matches_rcstring_consume(char ** ret_rest,cond_rc_t * cond_rc,char * action)1415 static int __rc_matches_rcstring_consume(
1416 	char **ret_rest, cond_rc_t *cond_rc, char *action)
1417 {
1418 	cond_rc_enum match_rc;
1419 	char *orig_flags;
1420 	char *flags;
1421 	int is_not_reversed = 1;
1422 	int ret;
1423 
1424 	/* Create window mask */
1425 	orig_flags = CreateFlagString(action, ret_rest);
1426 	flags = orig_flags;
1427 	if (flags == NULL)
1428 	{
1429 		match_rc = COND_RC_NO_MATCH;
1430 	}
1431 	else
1432 	{
1433 		if (*flags == '!')
1434 		{
1435 			is_not_reversed = 0;
1436 			flags++;
1437 		}
1438 		if (StrEquals(flags, "1") || StrEquals(flags, "match"))
1439 		{
1440 			match_rc = COND_RC_OK;
1441 		}
1442 		else if (StrEquals(flags, "0") || StrEquals(flags, "nomatch"))
1443 		{
1444 			match_rc = COND_RC_NO_MATCH;
1445 		}
1446 		else if (StrEquals(flags, "-1") || StrEquals(flags, "error"))
1447 		{
1448 			match_rc = COND_RC_ERROR;
1449 		}
1450 		else if (StrEquals(flags, "-2") || StrEquals(flags, "break"))
1451 		{
1452 			match_rc = COND_RC_BREAK;
1453 		}
1454 		else
1455 		{
1456 			match_rc = COND_RC_NO_MATCH;
1457 			/* Does anyone check for other numerical returncode
1458 			 * values? If so, this might have to be changed. */
1459 			fprintf(
1460 				stderr, "Unrecognised condition \"%s\" in"
1461 				" TestRc command.\n", flags);
1462 		}
1463 	}
1464 	if (orig_flags != NULL)
1465 	{
1466 		free(orig_flags);
1467 	}
1468 	ret = ((cond_rc->rc == match_rc) == is_not_reversed);
1469 
1470 	return ret;
1471 }
1472 
1473 /* ---------------------------- builtin commands --------------------------- */
1474 
CMD_Prev(F_CMD_ARGS)1475 void CMD_Prev(F_CMD_ARGS)
1476 {
1477 	circulate_cmd(F_PASS_ARGS, C_WINDOW, -1, True, True);
1478 
1479 	return;
1480 }
1481 
CMD_Next(F_CMD_ARGS)1482 void CMD_Next(F_CMD_ARGS)
1483 {
1484 	circulate_cmd(F_PASS_ARGS, C_WINDOW, 1, True, True);
1485 
1486 	return;
1487 }
1488 
CMD_None(F_CMD_ARGS)1489 void CMD_None(F_CMD_ARGS)
1490 {
1491 	circulate_cmd(F_PASS_ARGS, C_ROOT, 1, False, False);
1492 	/* invert return code */
1493 	switch (cond_rc->rc)
1494 	{
1495 	case COND_RC_OK:
1496 		cond_rc->rc = COND_RC_NO_MATCH;
1497 		break;
1498 	case COND_RC_NO_MATCH:
1499 		cond_rc->rc = COND_RC_OK;
1500 		break;
1501 	default:
1502 		break;
1503 	}
1504 
1505 	return;
1506 }
1507 
CMD_Any(F_CMD_ARGS)1508 void CMD_Any(F_CMD_ARGS)
1509 {
1510 	circulate_cmd(F_PASS_ARGS, exc->w.wcontext, 1, False, True);
1511 
1512 	return;
1513 }
1514 
CMD_Current(F_CMD_ARGS)1515 void CMD_Current(F_CMD_ARGS)
1516 {
1517 	circulate_cmd(F_PASS_ARGS, C_WINDOW, 0, True, True);
1518 
1519 	return;
1520 }
1521 
CMD_PointerWindow(F_CMD_ARGS)1522 void CMD_PointerWindow(F_CMD_ARGS)
1523 {
1524 	exec_context_changes_t ecc;
1525 
1526 	ecc.w.fw = get_pointer_fvwm_window();
1527 	exc = exc_clone_context(exc, &ecc, ECC_FW);
1528 	select_cmd(F_PASS_ARGS);
1529 	exc_destroy_context(exc);
1530 
1531 	return;
1532 }
1533 
CMD_ThisWindow(F_CMD_ARGS)1534 void CMD_ThisWindow(F_CMD_ARGS)
1535 {
1536 	select_cmd(F_PASS_ARGS);
1537 
1538 	return;
1539 }
1540 
CMD_Pick(F_CMD_ARGS)1541 void CMD_Pick(F_CMD_ARGS)
1542 {
1543 	select_cmd(F_PASS_ARGS);
1544 
1545 	return;
1546 }
1547 
CMD_All(F_CMD_ARGS)1548 void CMD_All(F_CMD_ARGS)
1549 {
1550 	FvwmWindow *t, **g;
1551 	char *restofline;
1552 	WindowConditionMask mask;
1553 	char *flags;
1554 	int num, i;
1555 	Bool does_any_window_match = False;
1556 	char *token;
1557 	Bool do_reverse = False;
1558 	Bool use_stack = False;
1559 
1560 	while (True) /* break when a non-option is found */
1561 	{
1562 		token = PeekToken(action, &restofline);
1563 		if (StrEquals(token, "Reverse"))
1564 		{
1565 			if (!*restofline)
1566 			{
1567 				/* if not any more actions, then Reverse
1568 				 * probably is some user function, so ignore
1569 				 * it and do the old  behaviour */
1570 				break;
1571 			}
1572 			else
1573 			{
1574 				do_reverse = True;
1575 				action = restofline;
1576 			}
1577 		}
1578 		else if (StrEquals(token, "UseStack"))
1579 		{
1580 			if (!*restofline)
1581 			{
1582 				/* if not any more actions, then UseStack
1583 				 * probably is some user function, so ignore
1584 				 * it and do the old behaviour */
1585 				break;
1586 			}
1587 			else
1588 			{
1589 				use_stack = True;
1590 				action = restofline;
1591 			}
1592 		}
1593 		else
1594 		{
1595 			/* No more options -- continue with flags and
1596 			 * commands */
1597 			break;
1598 		}
1599 	}
1600 
1601 	flags = CreateFlagString(action, &restofline);
1602 	DefaultConditionMask(&mask);
1603 	mask.my_flags.use_circulate_hit = 1;
1604 	mask.my_flags.use_circulate_hit_icon = 1;
1605 	mask.my_flags.use_circulate_hit_shaded = 1;
1606 	CreateConditionMask(flags, &mask);
1607 	if (flags)
1608 	{
1609 		free(flags);
1610 	}
1611 
1612 	num = 0;
1613 	for (t = Scr.FvwmRoot.next; t; t = t->next)
1614 	{
1615 		num++;
1616 	}
1617 	g = (FvwmWindow **)safemalloc(num * sizeof(FvwmWindow *));
1618 	num = 0;
1619 	if (!use_stack)
1620 	{
1621 		for (t = Scr.FvwmRoot.next; t; t = t->next)
1622 		{
1623 			if (MatchesConditionMask(t, &mask))
1624 			{
1625 				g[num++] = t;
1626 				does_any_window_match = True;
1627 			}
1628 		}
1629 	}
1630 	else
1631 	{
1632 		for (t = Scr.FvwmRoot.stack_next; t && t != &Scr.FvwmRoot;
1633 		     t = t->stack_next)
1634 		{
1635 			if (MatchesConditionMask(t, &mask))
1636 			{
1637 				g[num++] = t;
1638 				does_any_window_match = True;
1639 			}
1640 		}
1641 	}
1642 	if (do_reverse)
1643 	{
1644 		for (i = num-1; i >= 0; i--)
1645 		{
1646 			execute_function_override_window(
1647 				cond_rc, exc, restofline, 0, g[i]);
1648 		}
1649 	}
1650 	else
1651 	{
1652 		for (i = 0; i < num; i++)
1653 		{
1654 			execute_function_override_window(
1655 				cond_rc, exc, restofline, 0, g[i]);
1656 		}
1657 	}
1658 	if (cond_rc != NULL && cond_rc->rc != COND_RC_BREAK)
1659 	{
1660 		cond_rc->rc = (does_any_window_match == False) ?
1661 			COND_RC_NO_MATCH : COND_RC_OK;
1662 	}
1663 	free(g);
1664 	FreeConditionMask(&mask);
1665 
1666 	return;
1667 }
1668 
1669 /*
1670  * Execute a function to the closest window in the given
1671  * direction.
1672  */
CMD_Direction(F_CMD_ARGS)1673 void CMD_Direction(F_CMD_ARGS)
1674 {
1675 	direction_cmd(F_PASS_ARGS,False);
1676 }
1677 
CMD_ScanForWindow(F_CMD_ARGS)1678 void CMD_ScanForWindow(F_CMD_ARGS)
1679 {
1680 	direction_cmd(F_PASS_ARGS,True);
1681 }
1682 
CMD_WindowId(F_CMD_ARGS)1683 void CMD_WindowId(F_CMD_ARGS)
1684 {
1685 	FvwmWindow *t;
1686 	char *token;
1687 	char *naction;
1688 	unsigned long win;
1689 	Bool use_condition = False;
1690 	Bool use_screenroot = False;
1691 	WindowConditionMask mask;
1692 	char *flags, *restofline;
1693 
1694 	/* Get window ID */
1695 	action = GetNextToken(action, &token);
1696 
1697 	if (token && StrEquals(token, "root"))
1698 	{
1699 		int screen = Scr.screen;
1700 
1701 		free(token);
1702 		token = PeekToken(action, &naction);
1703 		if (!token || GetIntegerArguments(token, NULL, &screen, 1) != 1)
1704 		{
1705 			screen = Scr.screen;
1706 		}
1707 		else
1708 		{
1709 			action = naction;
1710 		}
1711 		use_screenroot = True;
1712 		if (screen < 0 || screen >= Scr.NumberOfScreens)
1713 		{
1714 			screen = 0;
1715 		}
1716 		win = XRootWindow(dpy, screen);
1717 		if (win == None)
1718 		{
1719 			if (cond_rc != NULL)
1720 			{
1721 				cond_rc->rc = COND_RC_ERROR;
1722 			}
1723 			return;
1724 		}
1725 	}
1726 	else if (token)
1727 	{
1728 		/* SunOS doesn't have strtoul */
1729 		win = (unsigned long)strtol(token, NULL, 0);
1730 		free(token);
1731 	}
1732 	else
1733 	{
1734 		win = 0;
1735 	}
1736 
1737 	/* Look for condition - CreateFlagString returns NULL if no '(' or '['
1738 	 */
1739 	if (!use_screenroot)
1740 	{
1741 		flags = CreateFlagString(action, &restofline);
1742 		if (flags)
1743 		{
1744 			/* Create window mask */
1745 			use_condition = True;
1746 			DefaultConditionMask(&mask);
1747 
1748 			/* override for Current [] */
1749 			mask.my_flags.use_circulate_hit = 1;
1750 			mask.my_flags.use_circulate_hit_icon = 1;
1751 			mask.my_flags.use_circulate_hit_icon = 1;
1752 
1753 			CreateConditionMask(flags, &mask);
1754 			free(flags);
1755 
1756 			/* Relocate action */
1757 			action = restofline;
1758 		}
1759 	}
1760 
1761 	/* Search windows */
1762 	for (t = Scr.FvwmRoot.next; t; t = t->next)
1763 	{
1764 		if (FW_W(t) == win)
1765 		{
1766 			/* do it if no conditions or the conditions match */
1767 			if (action && (!use_condition ||
1768 				       MatchesConditionMask(t, &mask)))
1769 			{
1770 				if (cond_rc != NULL)
1771 				{
1772 					cond_rc->rc = COND_RC_OK;
1773 				}
1774 				execute_function_override_window(
1775 					cond_rc, exc, action, 0, t);
1776 			}
1777 			else if (cond_rc != NULL)
1778 			{
1779 				cond_rc->rc = COND_RC_NO_MATCH;
1780 			}
1781 			break;
1782 		}
1783 	}
1784 	if (!t)
1785 	{
1786 		/* The window is not managed by fvwm. Still some functions may
1787 		 * work on it. */
1788 		if (use_condition)
1789 		{
1790 			if (cond_rc != NULL)
1791 			{
1792 				cond_rc->rc = COND_RC_ERROR;
1793 			}
1794 		}
1795 		else if (XGetGeometry(
1796 				 dpy, win, &JunkRoot, &JunkX, &JunkY,
1797 				 (unsigned int*)&JunkWidth,
1798 				 (unsigned int*)&JunkHeight,
1799 				 (unsigned int*)&JunkBW,
1800 				 (unsigned int*)&JunkDepth) != 0)
1801 		{
1802 			if (cond_rc != NULL)
1803 			{
1804 				cond_rc->rc = COND_RC_OK;
1805 			}
1806 			if (action != NULL)
1807 			{
1808 				const exec_context_t *exc2;
1809 				exec_context_changes_t ecc;
1810 
1811 				ecc.w.fw = NULL;
1812 				ecc.w.w = win;
1813 				ecc.w.wcontext = C_UNMANAGED;
1814 				exc2 = exc_clone_context(
1815 					exc, &ecc,
1816 					ECC_FW | ECC_W | ECC_WCONTEXT);
1817 				execute_function(
1818 					cond_rc, exc2, action,
1819 					FUNC_IS_UNMANAGED);
1820 				exc_destroy_context(exc2);
1821 			}
1822 		}
1823 		else
1824 		{
1825 			/* window id does not exist */
1826 			if (cond_rc != NULL)
1827 			{
1828 				cond_rc->rc = COND_RC_ERROR;
1829 			}
1830 		}
1831 	}
1832 
1833 	/* Tidy up */
1834 	if (use_condition)
1835 	{
1836 		FreeConditionMask(&mask);
1837 	}
1838 
1839 	return;
1840 }
1841 
CMD_TestRc(F_CMD_ARGS)1842 void CMD_TestRc(F_CMD_ARGS)
1843 {
1844 	char *rest;
1845 
1846 	if (cond_rc == NULL)
1847 	{
1848 		/* useless if no return code to compare to is given */
1849 		return;
1850 	}
1851 	if (__rc_matches_rcstring_consume(&rest, cond_rc, action) &&
1852 	    rest != NULL)
1853 	{
1854 		/* execute the command in root window context; overwrite the
1855 		 * return code with the return code of the command */
1856 		execute_function(cond_rc, exc, rest, 0);
1857 	}
1858 
1859 	return;
1860 }
1861 
CMD_Break(F_CMD_ARGS)1862 void CMD_Break(F_CMD_ARGS)
1863 {
1864 	int rc;
1865 
1866 	if (cond_rc == NULL)
1867 	{
1868 		return;
1869 	}
1870 	rc = GetIntegerArguments(action, &action, &cond_rc->break_levels, 1);
1871 	if (rc != 1 || cond_rc->break_levels <= 0)
1872 	{
1873 		cond_rc->break_levels = -1;
1874 	}
1875 	cond_rc->rc = COND_RC_BREAK;
1876 
1877 	return;
1878 }
1879 
CMD_NoWindow(F_CMD_ARGS)1880 void CMD_NoWindow(F_CMD_ARGS)
1881 {
1882 	execute_function_override_window(cond_rc, exc, action, 0, NULL);
1883 
1884 	return;
1885 }
1886 
1887 /* ver() - convert a version string to a floating-point number that
1888  * can be used to compare different versions.
1889  * ie. converts "2.5.11" to 2005011 */
ver(char * str)1890 static int ver (char *str)
1891 {
1892 	char *n;
1893 	int v;
1894 
1895 	str = DoPeekToken(str, &n, NULL, ".", NULL);
1896 	if (!n)
1897 	{
1898 		return -1.0;
1899 	}
1900 	v = atoi(n) * 1000000;
1901 	str = DoPeekToken(str, &n, NULL, ".", NULL);
1902 	if (!n)
1903 	{
1904 		return -1.0;
1905 	}
1906 	v += atoi(n) * 1000;
1907 	str = DoPeekToken(str, &n, NULL, ".", NULL);
1908 	if (!n)
1909 	{
1910 		return -1.0;
1911 	}
1912 	v += atoi(n);
1913 
1914 	return v;
1915 }
1916 
1917 /* match_version() - compare $version against this version of fvwm
1918  * using the operator specified by $operator. */
match_version(char * version,char * operator)1919 static Bool match_version(char *version, char *operator)
1920 {
1921 	static int fvwm_version = -1;
1922 	const int v = ver(version);
1923 
1924 	if (fvwm_version < 0)
1925 	{
1926 		char *tmp = safestrdup(VERSION);
1927 		fvwm_version = ver(tmp);
1928 		free(tmp);
1929 	}
1930 	if (v < 0)
1931 	{
1932 		fprintf(
1933 			stderr, "match_version: Invalid version: %s\n",
1934 			version);
1935 		return False;
1936 	}
1937 	if (strcmp(operator, ">=") == 0)
1938 	{
1939 		return fvwm_version >= v;
1940 	}
1941 	else if (strcmp(operator, ">") == 0)
1942 	{
1943 		return fvwm_version > v;
1944 	}
1945 	else if (strcmp(operator, "<=") == 0)
1946 	{
1947 		return fvwm_version <= v;
1948 	}
1949 	else if (strcmp(operator, "<") == 0)
1950 	{
1951 		return fvwm_version < v;
1952 	}
1953 	else if (strcmp(operator, "==") == 0)
1954 	{
1955 		return (v == fvwm_version);
1956 	}
1957 	else if (strcmp(operator, "!=") == 0)
1958 	{
1959 		return (v != fvwm_version);
1960 	}
1961 	else
1962 	{
1963 		fprintf(
1964 			stderr, "match_version: Invalid operator: %s\n",
1965 			operator);
1966 	}
1967 
1968 	return False;
1969 }
1970 
CMD_Test(F_CMD_ARGS)1971 void CMD_Test(F_CMD_ARGS)
1972 {
1973 	char *restofline;
1974 	char *flags;
1975 	char *condition;
1976 	char *flags_ptr;
1977 	int match;
1978 	int error;
1979 
1980 	flags = CreateFlagString(action, &restofline);
1981 
1982 	/* Next parse the flags in the string. */
1983 	flags_ptr = flags;
1984 	flags_ptr = GetNextSimpleOption(flags_ptr, &condition);
1985 
1986 	match = 1;
1987 	error = 0;
1988 	while (condition)
1989 	{
1990 		char *cond;
1991 		int reverse;
1992 
1993 		cond = condition;
1994 		reverse = 0;
1995 		if (*cond == '!')
1996 		{
1997 			reverse = 1;
1998 			cond++;
1999 		}
2000 		if (StrEquals(cond, "True"))
2001 		{
2002 			match = 1;
2003 		}
2004 		else if (StrEquals(cond, "False"))
2005 		{
2006 			match = 0;
2007 		}
2008 		else if (StrEquals(cond, "Version"))
2009 		{
2010 			char *pattern;
2011 			flags_ptr = GetNextSimpleOption(flags_ptr, &pattern);
2012 			if (pattern)
2013 			{
2014 				char *ver;
2015 				flags_ptr = GetNextSimpleOption(
2016 					flags_ptr, &ver);
2017 				if (ver == NULL)
2018 				{
2019 					match = matchWildcards(
2020 						pattern, VERSION);
2021 				}
2022 				else
2023 				{
2024 					match = match_version(ver, pattern);
2025 					free(ver);
2026 				}
2027 				free(pattern);
2028 			}
2029 			else
2030 			{
2031 				error = 1;
2032 			}
2033 		}
2034 		else if (StrEquals(cond, "Start"))
2035 		{
2036 			match = exc->type == EXCT_INIT ||
2037 				exc->type == EXCT_RESTART;
2038 		}
2039 		else if (StrEquals(cond, "Init"))
2040 		{
2041 			match = exc->type == EXCT_INIT;
2042 		}
2043 		else if (StrEquals(cond, "Restart"))
2044 		{
2045 			match = exc->type == EXCT_RESTART;
2046 		}
2047 		else if (StrEquals(cond, "Exit"))
2048 		{
2049 			match = exc->type == EXCT_QUIT ||
2050 				exc->type == EXCT_TORESTART;
2051 		}
2052 		else if (StrEquals(cond, "Quit"))
2053 		{
2054 			match = exc->type == EXCT_QUIT;
2055 		}
2056 		else if (StrEquals(cond, "ToRestart"))
2057 		{
2058 			match = exc->type == EXCT_TORESTART;
2059 		}
2060 		else if (StrEquals(cond, "x") || StrEquals(cond, "r") ||
2061 			 StrEquals(cond, "w") || StrEquals(cond, "f") ||
2062 			 StrEquals(cond, "i"))
2063 		{
2064 			char *pattern;
2065 			int type = X_OK;
2066 			Bool im = 0;
2067 
2068 			switch(cond[0])
2069 			{
2070 			case 'X':
2071 			case 'x':
2072 				type = X_OK;
2073 				break;
2074 			case 'R':
2075 			case 'r':
2076 				type = R_OK;
2077 				break;
2078 			case 'W':
2079 			case 'w':
2080 				type = W_OK;
2081 				break;
2082 			case 'f':
2083 			case 'F':
2084 				type = F_OK;
2085 				break;
2086 			case 'i':
2087 			case 'I':
2088 				im = True;
2089 				type = R_OK;
2090 				break;
2091 			default:
2092 				/* cannot happen */
2093 				break;
2094 			}
2095 			flags_ptr = GetNextSimpleOption(flags_ptr, &pattern);
2096 			if (pattern)
2097 			{
2098 				match = cond_check_access(pattern, type, im);
2099 				free(pattern);
2100 			}
2101 			else
2102 			{
2103 				error = 1;
2104 			}
2105 		}
2106 		else if (StrEquals(cond, "EnvIsSet"))
2107 		{
2108 			char *var_name;
2109 
2110 			flags_ptr = GetNextSimpleOption(flags_ptr, &var_name);
2111 			if (var_name)
2112 			{
2113 				const char *value = getenv(var_name);
2114 
2115 				match = (value != NULL) ? 1 : 0;
2116 			}
2117 			else
2118 			{
2119 				error = 1;
2120 			}
2121 		}
2122 		else if (StrEquals(cond, "EnvMatch"))
2123 		{
2124 			char *var_name;
2125 
2126 			flags_ptr = GetNextSimpleOption(flags_ptr, &var_name);
2127 			if (var_name)
2128 			{
2129                 		const char *value;
2130 				if ( (strlen(var_name) > 10) && (memcmp(var_name,"infostore.",10) == 0)  )
2131 				{
2132 					value = get_metainfo_value(var_name+10);
2133 				}
2134 				else
2135 				{
2136 					value = getenv(var_name);
2137 				}
2138 				char *pattern;
2139 				/* unfortunately, GetNextSimpleOption is
2140 				 * broken, does not accept quoted empty ""
2141 				 *
2142 				 * DV (2-Sep-2014):  It is *not* broken.  The
2143 				 * parsing functions never return empty tokens
2144 				 * by design.
2145 				 */
2146 				flags_ptr = GetNextSimpleOption(
2147 					flags_ptr, &pattern);
2148 				if (!value)
2149 				{
2150 					value = "";
2151 				}
2152 				if (pattern)
2153 				{
2154 					match =
2155 						/* include empty string case */
2156 						(!pattern[0] && !value[0]) ||
2157 						matchWildcards(pattern, value);
2158 				}
2159 				else
2160 				{
2161 					error = 1;
2162 				}
2163 			}
2164 			else
2165 			{
2166 				error = 1;
2167 			}
2168 		}
2169 		else if (StrEquals(cond, "EdgeIsActive"))
2170 		{
2171 			direction_t dir= DIR_NONE;
2172 			char *dirname;
2173 			char *next;
2174 			next = GetNextSimpleOption(flags_ptr, &dirname);
2175 			if (dirname)
2176 			{
2177 				dir = gravity_parse_dir_argument(
2178 					dirname, NULL, DIR_NONE);
2179 				if (dir == DIR_NONE)
2180 				{
2181 					if (!StrEquals(dirname, "Any"))
2182 					{
2183 						next = flags_ptr;
2184 					}
2185 				}
2186 				else if (dir > DIR_MAJOR_MASK)
2187 				{
2188 					error = 1;
2189 				}
2190 				free(dirname);
2191 			}
2192 
2193 			if (!error)
2194 			{
2195 				if (((dir == DIR_W || dir == DIR_NONE) &&
2196 					Scr.PanFrameLeft.isMapped) ||
2197 				    ((dir == DIR_N || dir == DIR_NONE) &&
2198 					Scr.PanFrameTop.isMapped) ||
2199 				    ((dir == DIR_S || dir == DIR_NONE) &&
2200 					Scr.PanFrameBottom.isMapped) ||
2201 				    ((dir == DIR_E || dir == DIR_NONE) &&
2202  				    	Scr.PanFrameRight.isMapped))
2203 				{
2204 				  	match = 1;
2205 				}
2206 				else
2207 				{
2208 				  	match = 0;
2209 				}
2210 			}
2211 			flags_ptr = next;
2212 		}
2213 		else if (StrEquals(cond, "EdgeHasPointer"))
2214 		{
2215 			int x,y;
2216 			Window win;
2217 			direction_t dir = DIR_NONE;
2218 			char *dirname;
2219 			char *next;
2220 			next = GetNextSimpleOption(flags_ptr, &dirname);
2221 			if (dirname)
2222 			{
2223 				dir = gravity_parse_dir_argument(
2224 					dirname, NULL, DIR_NONE);
2225 				if (dir == DIR_NONE)
2226 				{
2227 					if (!StrEquals(dirname, "Any"))
2228 					{
2229 						next = flags_ptr;
2230 					}
2231 				}
2232 				else if (dir > DIR_MAJOR_MASK)
2233 				{
2234 					error = 1;
2235 				}
2236 				free(dirname);
2237 			}
2238 
2239 			if (!error)
2240 			{
2241 				if (FQueryPointer(
2242 					dpy, Scr.Root, &JunkRoot, &win,
2243 					&JunkX, &JunkY,	&x, &y, &JunkMask)
2244  					== False)
2245 				{
2246 					/* pointer is on a different screen */
2247 					match = 0;
2248 				}
2249 				else if (is_pan_frame(win))
2250 				{
2251 					if (dir == DIR_NONE ||
2252 					    (dir == DIR_N &&
2253 					     win == Scr.PanFrameTop.win) ||
2254 					    (dir == DIR_S &&
2255 					     win == Scr.PanFrameBottom.win) ||
2256 					    (dir == DIR_E &&
2257 					     win == Scr.PanFrameRight.win) ||
2258 					    (dir == DIR_W &&
2259 					     win == Scr.PanFrameLeft.win))
2260 					{
2261 						match = 1;
2262 					}
2263 					else
2264 					{
2265 						match = 0;
2266 					}
2267 				}
2268 				else
2269 				{
2270 					match = 0;
2271 				}
2272 			}
2273 			flags_ptr = next;
2274 		}
2275 		else
2276 		{
2277 			/* unrecognized condition */
2278 			error = 1;
2279 			fprintf(
2280 				stderr, "Unrecognised condition \"%s\" in"
2281 				" Test command.\n", cond);
2282 		}
2283 
2284 		if (reverse)
2285 		{
2286 			match = !match;
2287 		}
2288 		free(condition);
2289 		if (error || !match)
2290 		{
2291 			break;
2292 		}
2293 		flags_ptr = GetNextSimpleOption(flags_ptr, &condition);
2294 	}
2295 
2296 	if (flags != NULL)
2297 	{
2298 		free(flags);
2299 	}
2300 	if (!error && match)
2301 	{
2302 		execute_function(cond_rc, exc, restofline, 0);
2303 	}
2304 	if (cond_rc != NULL)
2305 	{
2306 		if (error)
2307 		{
2308 			cond_rc->rc = COND_RC_ERROR;
2309 		}
2310 		else if (match)
2311 		{
2312 			cond_rc->rc = COND_RC_OK;
2313 		}
2314 		else
2315 		{
2316 			cond_rc->rc = COND_RC_NO_MATCH;
2317 		}
2318 	}
2319 
2320 	return;
2321 }
2322