chiark / gitweb /
vtwm (5.4.7-3) unstable; urgency=medium
[vtwm.git] / list.c
1 /*****************************************************************************/
2 /**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
3 /**                          Salt Lake City, Utah                           **/
4 /**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
5 /**                        Cambridge, Massachusetts                         **/
6 /**                                                                         **/
7 /**                           All Rights Reserved                           **/
8 /**                                                                         **/
9 /**    Permission to use, copy, modify, and distribute this software and    **/
10 /**    its documentation  for  any  purpose  and  without  fee is hereby    **/
11 /**    granted, provided that the above copyright notice appear  in  all    **/
12 /**    copies and that both  that  copyright  notice  and  this  permis-    **/
13 /**    sion  notice appear in supporting  documentation,  and  that  the    **/
14 /**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
15 /**    in publicity pertaining to distribution of the  software  without    **/
16 /**    specific, written prior permission.                                  **/
17 /**                                                                         **/
18 /**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
19 /**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
20 /**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
21 /**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
22 /**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
23 /**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
24 /**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
25 /**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
26 /*****************************************************************************/
27
28
29 /**********************************************************************
30  *
31  * $XConsortium: list.c,v 1.20 91/01/09 17:13:30 rws Exp $
32  *
33  * TWM code to deal with the name lists for the NoTitle list and
34  * the AutoRaise list
35  *
36  * 11-Apr-88 Tom LaStrange        Initial Version.
37  *
38  **********************************************************************/
39
40 /*
41  * Stolen from TVTWM pl11, updated it to conform to the POSIX 1003.2
42  * regex spec, backported VTWM 5.3's internal wildcarding code, and
43  * made it work without regex support.
44  *
45  * D. J. Hawkey Jr. - 10/20/01
46  */
47
48 #include <stdio.h>
49 #include <X11/Xatom.h>
50
51 #ifndef NO_REGEX_SUPPORT
52 #include <sys/types.h>
53 #include <regex.h>
54 #endif
55
56 #include "twm.h"
57 #include "screen.h"
58 #include "list.h"
59 #include "gram.h"
60
61 #define REGCOMP_FLAGS           (REG_EXTENDED | REG_NOSUB)
62
63 struct name_list_struct
64 {
65     name_list *next;            /* pointer to the next name */
66     char *name;                 /* the name of the window */
67 #ifndef NO_REGEX_SUPPORT
68     regex_t re;                 /* compile only once */
69 #else
70     char re;                    /* not used */
71 #endif
72     short type;                 /* what type of match */
73     Atom property;              /* if (type == property) */
74     char *ptr;                  /* list dependent data */
75 };
76
77 #ifndef NO_REGEX_SUPPORT
78 static char buffer[256];
79 #endif
80
81 int match();
82
83 /***********************************************************************
84  *
85  * Wrappers to allow code to step through a list
86  *
87  ***********************************************************************/
88
89 name_list *
90 next_entry(list)
91 name_list *list;
92 {
93     return (list->next);
94 }
95
96 char *
97 contents_of_entry(list)
98 name_list *list;
99 {
100     return (list->ptr);
101 }
102
103 /**********************************************************************/
104
105 #ifdef DEBUG
106 static void
107 printNameList(name, nptr)
108 char *name;
109 name_list *nptr;
110 {
111     printf("printNameList(): %s=[", name);
112
113     while (nptr)
114     {
115         printf(" '%s':%d", nptr->name, nptr->type);
116         nptr = nptr->next;
117     }
118
119     printf(" ]\n");
120 }
121 #endif
122
123 /***********************************************************************
124  *
125  *  Procedure:
126  *      AddToList - add a window name to the appropriate list
127  *
128  *  Inputs:
129  *      list    - the address of the pointer to the head of a list
130  *      name    - a pointer to the name of the window 
131  *      type    - a bitmask of what to match against
132  *      property- a window propery to match against
133  *      ptr     - pointer to list dependent data
134  *
135  *  Special Considerations
136  *      If the list does not use the ptr value, a non-null value 
137  *      should be placed in it.  LookInList returns this ptr value
138  *      and procedures calling LookInList will check for a non-null 
139  *      return value as an indication of success.
140  *
141  ***********************************************************************
142  */
143
144 void
145 AddToList(list_head, name, type, /*property, */ptr)
146 name_list **list_head;
147 char *name;
148 short type;
149 /* Atom property; */
150 char *ptr;
151 {
152     Atom property = None;
153     name_list *nptr;
154
155     if (!list_head) return;     /* ignore empty inserts */
156
157     nptr = (name_list *)malloc(sizeof(name_list));
158     if (nptr == NULL)
159     {
160         fprintf (stderr, "%s: unable to allocate %d bytes for name_list\n",
161                  ProgramName, sizeof(name_list));
162         Done();
163     }
164
165 #if 0
166     while (*list_head)
167         list_head = &((*list_head)->next);
168
169     nptr->next = NULL;
170 #else
171     nptr->next = *list_head;
172 #endif
173
174     nptr->name = (char *)strdup(name);
175     if (type & LTYPE_HOST)
176     {
177         nptr->type = (type & ~LTYPE_HOST) | LTYPE_PROPERTY;
178         nptr->property = XA_WM_CLIENT_MACHINE;
179     }
180     else
181     {
182         nptr->type = type;
183         nptr->property = property;
184     }
185     nptr->ptr = (ptr == NULL) ? (char *)TRUE : ptr;
186
187     *list_head = nptr;
188 }    
189
190  /********************************************************************\
191  *                                                                    *
192  * New LookInList code by RJC.                                        *
193  *                                                                    *
194  * Since we want to be able to look for multiple matches (eg, to      *
195  * check which relevant icon regions are on the screen), the basic    *
196  * procedure is now MultiLookInList and uses a (pseudo-)continuation  *
197  * to keep track of where it is.                                      *
198  *                                                                    *
199  * LookInList is a trivial specialisation of that.                    *
200  *                                                                    *
201  * Also, we now allow regular expressions in lists, so here we use    *
202  * Henry Spencer's regular expression code.  It is possible that we   *
203  * should pre-compile all the regular expressions for maximum         *
204  * speed.                                                             *
205  *                                                                    *
206  \********************************************************************/
207
208 int
209 MatchName(name, pattern, compiled, type)
210 char *name;
211 char *pattern;
212 #ifndef NO_REGEX_SUPPORT
213 regex_t *compiled;
214 #else
215 char *compiled;
216 #endif
217 short type;
218 {
219 #ifdef DEBUG
220     fprintf(stderr, "MatchName(): compare '%s' with '%s'\n", name, pattern);
221 #endif
222
223     if (type & LTYPE_ANYTHING)
224         return (0);
225
226     if (type & LTYPE_REGEXP)
227     {
228 #ifndef NO_REGEX_SUPPORT
229         regex_t re;
230         int result;
231
232         if ((result = regcomp(&re, pattern, REGCOMP_FLAGS)) != 0)
233         {
234             regerror(result, &re, buffer, sizeof(buffer));
235             regfree(&re);
236
237             fprintf(stderr, "%s: (1) regcomp(\"%s\") error: %s\n",
238                         ProgramName, pattern, buffer);
239             return (result);
240         }
241
242         result = regexec(&re, name, 0, NULL, 0);
243         regfree(&re);
244
245         return (result);
246 #else
247         fprintf(stderr, "%s: (1) no support for regcomp(\"%s\")\n",
248                         ProgramName, pattern);
249         return (1);
250 #endif
251     }
252
253     if (type & LTYPE_C_REGEXP)
254     {
255 #ifndef NO_REGEX_SUPPORT
256         return (regexec(compiled, name, 0, NULL, 0));
257 #else
258         fprintf(stderr, "%s: no support for regexec(\"%s\")\n",
259                 ProgramName, name);
260         return (1);
261 #endif
262     }
263
264     if (type & LTYPE_STRING)
265         return (match(pattern, name));
266
267     fprintf(stderr, "%s: bad list type (%d) comparing \"%s\" with \"%s\"\n",
268                 ProgramName, type, name, pattern);
269     return (1);
270 }
271
272 static char *
273 MultiLookInList(list_head, name, class, /*win, */continuation)
274 name_list *list_head;
275 char *name;
276 XClassHint *class;
277 /* Window win; */
278 name_list **continuation;
279 {
280     name_list *nptr;
281 #if 0
282     Window win = None;
283 #endif
284
285 #ifdef DEBUG
286     fprintf(stderr, "MultiLookInList(): looking for '%s'\n", name);
287 #endif
288
289     for (nptr = list_head ; nptr ; nptr = nptr->next)
290     {
291         /* pre-compile and cache the regex_t */
292         if (nptr->type & LTYPE_REGEXP)
293         {
294 #ifndef NO_REGEX_SUPPORT
295             int result;
296
297             if ((result = regcomp(&nptr->re, nptr->name, REGCOMP_FLAGS)) != 0)
298             {
299                 regerror(result, &nptr->re, buffer, sizeof(buffer));
300                 regfree(&nptr->re);
301
302                 fprintf(stderr, "%s: (2) regcomp(\"%s\") error: %s\n",
303                                 ProgramName, nptr->name, buffer);
304
305                 nptr->type |= LTYPE_NOTHING;
306             }
307             else
308                 nptr->type |= LTYPE_C_REGEXP;
309 #else
310             fprintf(stderr, "%s: (2) no support for regcomp(\"%s\")\n",
311                         ProgramName, nptr->name);
312
313             nptr->type |= LTYPE_NOTHING;
314 #endif
315
316             nptr->type &= ~LTYPE_REGEXP;
317         }
318
319         if (nptr->type & LTYPE_NOTHING) 
320             continue;                           /* skip illegal entry */
321
322         if (nptr->type & LTYPE_ANYTHING)
323         {
324             *continuation = nptr->next;
325             return (nptr->ptr);
326         }
327
328         if (nptr->type & LTYPE_NAME)
329             if (MatchName(name, nptr->name, &nptr->re, nptr->type) == 0)
330             {
331                 *continuation = nptr->next;
332                 return (nptr->ptr);
333             }
334
335         if (class)
336         {
337             if (nptr->type & LTYPE_RES_NAME)
338                 if (MatchName(class->res_name, nptr->name, &nptr->re,
339                                 nptr->type) == 0)
340                 {
341                     *continuation = nptr->next;
342                     return (nptr->ptr);
343                 }
344
345             if (nptr->type & LTYPE_RES_CLASS)
346                 if (MatchName(class->res_class, nptr->name, &nptr->re,
347                                 nptr->type) == 0)
348                 {
349                     *continuation = nptr->next;
350                     return (nptr->ptr);
351                 }
352         }
353
354 #if 0
355         if (win && (nptr->type & LTYPE_PROPERTY))
356         {
357             char *s = GetPropertyString(win, nptr->property);
358
359             if (s && MatchName(s, nptr->name, &nptr->re, nptr->type) == 0)
360             {
361                 free(s);
362
363                 *continuation = nptr->next;
364                 return (nptr->ptr);
365             }
366
367             if (s) free(s);
368         }
369 #endif
370     }
371
372     *continuation = NULL;
373     return (NULL);
374 }
375
376 char *
377 LookInList(list_head, name, class/*, win*/)
378 name_list *list_head;
379 char *name;
380 XClassHint *class;
381 /* Window win; */
382 {
383 #if 0
384     name_list *nptr;
385 #endif
386     name_list *rest;
387     char *return_name = MultiLookInList(list_head, name, class, /*win, */&rest);
388
389 #if 0
390     if ((Scr->ListRings == TRUE) && (return_name != NULL)
391         && (list_head->next != NULL)) 
392     {
393         /* To implement a ring on the linked list where we cant change the */
394         /* list_head, use a simple unlink/link-at-end alg. unless you need */
395         /* to move the first link.   In that case swap the contents of the */
396         /* first link with the contents of the second then proceed as */
397         /* normal.  */
398         name_list *tmp_namelist;
399         
400         if (list_head->ptr == return_name)
401         {
402             char *tmp_name;
403             short tmp_type;
404             char *tmp_ptr;
405             
406             tmp_name = list_head->name;
407             tmp_type = list_head->type;
408             tmp_ptr = list_head->ptr;
409             
410             list_head->name = list_head->next->name;
411             list_head->type = list_head->next->type;
412             list_head->ptr = list_head->next->ptr;
413             
414             list_head->next->name = tmp_name;
415             list_head->next->type = tmp_type;
416             list_head->next->ptr = tmp_ptr;
417         }
418         
419         for (nptr = list_head; nptr->next != NULL; nptr = nptr->next)
420         {
421             if (nptr->next->ptr == return_name)
422               break;
423         }
424         
425         if (nptr->next->next != NULL)
426         {
427             tmp_namelist = nptr->next;
428             nptr->next = nptr->next->next;
429             
430             for (nptr = nptr->next; nptr->next != NULL; nptr = nptr->next);
431             nptr->next = tmp_namelist;
432             nptr->next->next = NULL;
433         }
434     }
435 #endif
436     
437     return (return_name);
438 }
439
440 #if 0
441 static char *
442 MultiLookInNameList(list_head, name, continuation)
443 name_list *list_head;
444 char *name;
445 name_list **continuation;
446 {
447     return (MultiLookInList(list_head, name, NULL, /*None, */continuation));
448 }
449 #endif
450
451 char *
452 LookInNameList(list_head, name)
453 name_list *list_head;
454 char *name;
455 {
456     return (MultiLookInList(list_head, name, NULL, /*None, */&list_head));
457 }
458
459 /***********************************************************************
460  *
461  *  Procedure:
462  *      GetColorFromList - look through a list for a window name, or class
463  *
464  *  Returned Value:
465  *      TRUE if the name was found
466  *      FALSE if the name was not found
467  *
468  *  Inputs:
469  *      list    - a pointer to the head of a list
470  *      name    - a pointer to the name to look for
471  *      class   - a pointer to the class to look for
472  *
473  *  Outputs:
474  *      ptr     - fill in the list value if the name was found
475  *
476  ***********************************************************************
477  */
478
479 int GetColorFromList(list_head, name, class, /*win, */ptr)
480 name_list *list_head;
481 char *name;
482 XClassHint *class;
483 /* Window win; */
484 Pixel *ptr;
485 {
486     int save;
487     char *val = LookInList(list_head, name, class/*, win*/);
488
489     if (val)
490     {
491         save = Scr->FirstTime;
492         Scr->FirstTime = TRUE;
493         GetColor(Scr->Monochrome, ptr, val);
494         Scr->FirstTime = save;
495
496         return (TRUE);
497     }
498
499     return (FALSE);
500 }
501
502 /***********************************************************************
503  *
504  *  Procedure:
505  *      FreeList - free up a list
506  *
507  ***********************************************************************
508  */
509
510 void FreeList(list)
511 name_list **list;
512 {
513     name_list *nptr;
514     name_list *tmp;
515
516     for (nptr = *list; nptr != NULL; )
517     {
518         tmp = nptr->next;
519
520 #ifndef NO_REGEX_SUPPORT
521         if (nptr->type & LTYPE_C_REGEXP)
522             regfree(&nptr->re);
523 #endif
524         free(nptr->name);
525         free((char *) nptr);
526
527         nptr = tmp;
528     }
529
530     *list = NULL;
531 }
532
533 /***********************************************************************
534  *
535  * MSDOS-ish, Unix-ish, VTWM 5.3 wildcard support
536  *
537  **********************************************************************/
538
539 #if 0
540 static int is_pattern(p)
541 char *p;
542 {
543     while (*p)
544     {
545         switch (*p++)
546         {
547             case '?':
548             case '*':
549             case '[':
550                 return (TRUE);
551             case '\\':
552                 if (!*p++) return (FALSE);
553         }
554     }
555
556     return (FALSE);
557 }
558 #endif
559
560 #define ABORT 2
561
562 static int regex_match();
563
564 static int regex_match_after_star(p, t)
565 char *p, *t;
566 {
567     register int match;
568     register int nextp;
569
570     while ((*p == '?') || (*p == '*'))
571     {
572         if (*p == '?')
573             if (!*t++) return (ABORT);
574
575         p++;
576     }
577     if (!*p) return (TRUE);
578
579     nextp = *p;
580     if (nextp == '\\') nextp = p[1];
581
582     match = FALSE;
583     while (match == FALSE)
584     {
585         if (nextp == *t || nextp == '[')
586             match = regex_match(p, t);
587
588         if (!*t++) match = ABORT;
589     }
590
591     return (match);
592 }
593
594 static int regex_match(p, t)
595 char *p, *t;
596 {
597     register char range_start, range_end;
598     int invert;
599     int member_match;
600     int loop;
601
602     for (; *p; p++, t++)
603     {
604         if (!*t) return ((*p == '*' && *++p == '\0') ? TRUE : ABORT);
605
606         switch (*p)
607         {
608             case '?':
609                 break;
610             case '*':
611                 return (regex_match_after_star(p, t));
612             case '[':
613             {
614                 p++;
615                 invert = FALSE;
616                 if (*p == '!' || *p == '^')
617                 {
618                     invert = TRUE;
619                     p++;
620                 }
621
622                 if (*p == ']') return (ABORT);
623
624                 member_match = FALSE;
625                 loop = TRUE;
626                 while (loop)
627                 {
628                     if (*p == ']')
629                     {
630                         loop = FALSE;
631                         continue;
632                     }
633
634                     if (*p == '\\')
635                         range_start = range_end = *++p;
636                     else
637                         range_start = range_end = *p;
638                     if (!range_start) return (ABORT);
639
640                     if (*++p == '-')
641                     {
642                         range_end = *++p;
643                         if (range_end == '\0' || range_end == ']')
644                             return (ABORT);
645
646                         if (range_end == '\\')
647                             range_end = *++p;
648                         p++;
649                     }
650
651                     if (range_start < range_end)
652                     {
653                         if (*t >= range_start && *t <= range_end)
654                         {
655                             member_match = TRUE;
656                             loop = FALSE;
657                         }
658                     }
659                     else
660                     {
661                         if (*t >= range_end && *t <= range_start)
662                         {
663                             member_match = TRUE;
664                             loop = FALSE;
665                         }
666                     }
667                 }
668
669                 if ((invert && member_match) || !(invert || member_match))
670                     return (FALSE);
671
672                 if (member_match)
673                 {
674                     while (*p != ']')
675                     {
676                         if (!*p) return (ABORT);
677
678                         if (*p == '\\') p++;
679                         p++;
680                     }
681                 }
682                 break;
683             }
684             case '\\':
685                 p++;
686
687             default:
688                 if (*p != *t) return (FALSE);
689         }
690     }
691
692     return (!*t);
693 }
694
695 int match(p, t)
696 char *p, *t;
697 {
698     if ((p == NULL) || (t == NULL)) return (TRUE);
699
700     return ((regex_match(p, t) == TRUE) ? FALSE : TRUE);
701 }