1 
2 
3 /*
4 A* -------------------------------------------------------------------
5 B* This file contains source code for the PyMOL computer program
6 C* Copyright (c) Schrodinger, LLC.
7 D* -------------------------------------------------------------------
8 E* It is unlawful to modify or remove this copyright notice.
9 F* -------------------------------------------------------------------
10 G* Please see the accompanying LICENSE file for further information.
11 H* -------------------------------------------------------------------
12 I* Additional authors of this source file include:
13 -*
14 -*
15 -*
16 Z* -------------------------------------------------------------------
17 */
18 #include"os_python.h"
19 #include"os_predef.h"
20 #include"os_std.h"
21 
22 #include"Base.h"
23 #include"OOMac.h"
24 #include"Word.h"
25 #include"Parse.h"
26 #include"PyMOLObject.h"
27 #include"MemoryDebug.h"
28 
29 struct _CWord {
30   int no_state_at_present;
31 };
32 
33 typedef struct {
34   int match_mode;
35   int continued;
36   int literal1, literal2;       /* offsets into charVLA */
37   int numeric1, numeric2;
38   int has1, has2;
39 } MatchNode;
40 
41 struct _CWordMatcher {
42   PyMOLGlobals *G;
43   MatchNode *node;
44   int n_node;
45   char *charVLA;
46   int n_char;
47   int ignore_case;
48 };
49 
50 #define cMatchLiteral  0
51 #define cMatchNumericRange  cWordMatchOptionNumericRanges
52 #define cMatchAlphaRange  cWordMatchOptionAlphaRanges
53 #define cMatchWildcard 3
54 
WordCompare(PyMOLGlobals * G,const char * p,const char * q,int ignCase)55 int WordCompare(PyMOLGlobals * G, const char *p, const char *q, int ignCase)
56 
57 
58 /* all things equal, shorter is smaller */
59 {
60   int result = 0;
61   char cp, cq, tlp, tlq;
62   if(ignCase) {
63     while((cp = *p) && (cq = *q)) {
64       p++;
65       q++;
66       if(cp != cq) {
67         (tlp = tolower(cp));
68         (tlq = tolower(cq));
69         if(tlp < tlq)
70           return -1;
71         else if(tlp > tlq) {
72           return 1;
73         }
74       }
75     }
76   } else {
77     while((cp = *p) && (cq = *q)) {
78       p++;
79       q++;
80       if(cp != cq) {
81         if(cp < cq) {
82           return -1;
83         } else if(cp > cq) {
84           return 1;
85         }
86       }
87     }
88   }
89   if((!result) && (!*p) && (*q))
90     return -1;
91   else if((!result) && (*p) && (!*q))
92     return 1;
93   return 0;
94 }
95 
WordMatchOptionsConfigInteger(CWordMatchOptions * I)96 void WordMatchOptionsConfigInteger(CWordMatchOptions * I)
97 {
98   I->range_mode = cWordMatchOptionNumericRanges;
99   I->lists = true;
100   I->ignore_case = true;
101   I->wildcard = 0;              /* no wildcard for numbers */
102   I->allow_hyphen = true;
103   I->allow_plus = true;
104   I->space_lists = false;
105 }
106 
WordMatchOptionsConfigAlpha(CWordMatchOptions * I,char wildcard,int ignore_case)107 void WordMatchOptionsConfigAlpha(CWordMatchOptions * I, char wildcard, int ignore_case)
108 {
109   I->range_mode = cWordMatchOptionAlphaRanges;
110   I->lists = true;
111   I->ignore_case = ignore_case;
112   I->wildcard = wildcard;
113   I->allow_hyphen = false;
114   I->allow_plus = false;
115   I->space_lists = false;
116 }
117 
WordMatchOptionsConfigAlphaList(CWordMatchOptions * I,char wildcard,int ignore_case)118 void WordMatchOptionsConfigAlphaList(CWordMatchOptions * I, char wildcard,
119                                      int ignore_case)
120 {                               /* here we expect '+' to be used in lists */
121   I->range_mode = cWordMatchOptionAlphaRanges;
122   I->lists = true;
123   I->ignore_case = ignore_case;
124   I->wildcard = wildcard;
125   I->allow_hyphen = false;
126   I->allow_plus = true;
127   I->space_lists = false;
128 }
129 
WordMatchOptionsConfigMixed(CWordMatchOptions * I,char wildcard,int ignore_case)130 void WordMatchOptionsConfigMixed(CWordMatchOptions * I, char wildcard, int ignore_case)
131 {
132   I->range_mode = cWordMatchOptionNumericRanges;
133   I->lists = true;
134   I->ignore_case = ignore_case;
135   I->wildcard = wildcard;
136   I->allow_hyphen = true;
137   I->allow_plus = true;
138   I->space_lists = false;
139 }
140 
WordMatchOptionsConfigNameList(CWordMatchOptions * I,char wildcard,int ignore_case)141 void WordMatchOptionsConfigNameList(CWordMatchOptions * I, char wildcard, int ignore_case)
142 {
143   I->range_mode = cWordMatchOptionAlphaRanges;
144   I->lists = true;
145   I->ignore_case = ignore_case;
146   I->wildcard = wildcard;
147   I->allow_hyphen = false;
148   I->allow_plus = false;
149   I->space_lists = true;
150 }
151 
WordMatcherNew(PyMOLGlobals * G,const char * st,CWordMatchOptions * option,int force)152 CWordMatcher *WordMatcherNew(PyMOLGlobals * G, const char *st, CWordMatchOptions * option,
153                              int force)
154 {
155 
156   CWordMatcher *result = NULL;
157   int needed = force;
158   char wildcard = option->wildcard;
159 
160   if(wildcard == 32)
161     wildcard = 0;               /* space as wildcard means no wildcard */
162 
163   if(!st)
164     return NULL;
165   {                             /* first determine if we need to incur the overhead of the matcher */
166     int escape = false;
167     const char *p = st;
168     while((*p) && (!needed)) {
169       if(!escape) {
170         switch (*p) {
171         case '\\':
172           escape = true;
173           needed = true;
174           break;
175         case '+':
176           if((option->lists) && (option->allow_plus))
177             needed = true;
178           break;
179         case ',':              /* list operators */
180           if(option->lists)
181             needed = true;
182           break;
183         case '-':              /* range operators */
184           if(option->allow_hyphen)
185             needed = true;
186           break;
187         case ':':
188           if(option->range_mode)
189             needed = true;
190           break;
191         case ' ':
192           if(option->space_lists)
193             needed = true;
194           break;
195         default:
196           if(*p == wildcard)
197             needed = true;
198           break;
199         }
200       } else
201         escape = false;
202       p++;
203     }
204   }
205 
206   if(needed) {                  /* if so, then convert the expression into a match tree */
207     int n_char = 0;
208     int n_node = 0;
209     OOCalloc(G, CWordMatcher);
210     I->charVLA = VLACalloc(char, 10);   /* auto_zeroing... */
211     I->node = VLACalloc(MatchNode, 10);
212     I->ignore_case = option->ignore_case;
213     I->G = G;
214     /* build up the matcher structure... */
215     {
216       const char *p = st;
217       char c, *q;
218       int escape = false;
219       int token_active = false;
220       int node_active = false;
221       int char_handled = false;
222       int cur_node = 0;
223       int expectation = 1;
224 
225       while(1) {
226         c = *p;
227         char_handled = false;
228         if(!escape) {
229           switch (c) {
230           case '\\':
231             escape = true;
232             char_handled = true;
233             break;
234           case 0:
235             if(option->lists) {
236               char_handled = true;
237               node_active = false;
238               token_active = false;
239             }
240             break;
241           case '+':            /* list operator */
242             if(option->lists && option->allow_plus) {
243               if(n_node < expectation) {        /* create empty node */
244                 VLACheck(I->node, MatchNode, n_node);
245                 n_node++;
246               } else {
247                 expectation = n_node + 1;
248               }
249               char_handled = true;
250               node_active = false;
251               token_active = false;
252             }
253             break;
254           case ',':            /* list operator */
255             if(option->lists) {
256               if(n_node < expectation) {        /* create empty node */
257                 VLACheck(I->node, MatchNode, n_node);
258                 n_node++;
259               } else {
260                 expectation = n_node + 1;
261               }
262               char_handled = true;
263               node_active = false;
264               token_active = false;
265             }
266             break;
267           case ' ':            /* space list */
268             if(option->space_lists) {
269               if(n_node < expectation) {        /* create empty node */
270                 VLACheck(I->node, MatchNode, n_node);
271                 n_node++;
272               } else {
273                 expectation = n_node + 1;
274               }
275               char_handled = true;
276               node_active = false;
277               token_active = false;
278             }
279             break;
280           case '-':            /* range operators */
281             if(option->allow_hyphen && option->range_mode) {
282               if(!node_active) {
283                 cur_node = n_node;
284                 VLACheck(I->node, MatchNode, cur_node);
285                 node_active = true;
286                 n_node++;
287               }
288               I->node[cur_node].match_mode = option->range_mode;
289               token_active = false;
290               char_handled = true;
291             }
292             break;
293           case ':':
294             if(option->range_mode) {
295               if(!node_active) {
296                 cur_node = n_node;
297                 VLACheck(I->node, MatchNode, cur_node);
298                 node_active = true;
299                 n_node++;
300               }
301               I->node[cur_node].match_mode = option->range_mode;
302               token_active = false;
303               char_handled = true;
304             }
305             break;
306           default:
307             if(c == wildcard) {
308               if(node_active) {
309                 I->node[cur_node].continued = true;
310               }
311               VLACheck(I->node, MatchNode, n_node);
312               cur_node = n_node;
313               I->node[cur_node].match_mode = cMatchWildcard;
314               n_node++;
315               node_active = true;
316               token_active = false;
317               char_handled = true;
318             }
319             break;
320           }
321         } else
322           escape = false;
323         if(!char_handled) {
324           if(!token_active) {
325             n_char++;
326             VLACheck(I->charVLA, char, n_char);
327             token_active = true;
328 
329             if((!node_active) || (I->node[cur_node].match_mode == cMatchWildcard)) {
330               if(node_active)   /* must be extending after a wildcard */
331                 I->node[cur_node].continued = true;
332               else
333                 node_active = true;
334               VLACheck(I->node, MatchNode, n_node);
335               cur_node = n_node;
336               I->node[cur_node].literal1 = n_char;      /* the first literal */
337               n_node++;
338             } else {
339               I->node[cur_node].literal2 = n_char;      /* must be the second literal */
340             }
341           }
342 
343           /* copy character into auto-terminated string */
344           VLACheck(I->charVLA, char, n_char + 1);
345           q = I->charVLA + n_char;
346           (*q++) = c;
347           n_char++;
348         }
349         if(c)
350           p++;
351         else
352           break;
353       }
354       if(n_node < expectation) {        /* create empty node */
355         VLACheck(I->node, MatchNode, n_node);
356         n_node++;
357       }
358 
359     }
360 
361     {
362       int a;
363       int tmp;
364       MatchNode *node = I->node;
365       for(a = 0; a < n_node; a++) {
366         switch (node->match_mode) {
367         case cMatchLiteral:
368           if(option->range_mode == cWordMatchOptionNumericRanges) {
369             if(node->literal1) {
370               if(sscanf(I->charVLA + node->literal1, "%d", &tmp) == 1) {
371                 node->numeric1 = tmp;
372                 node->has1 = true;
373               }
374             }
375           }
376           break;
377         case cMatchAlphaRange:
378           if(node->literal1)
379             node->has1 = true;
380           if(node->literal2)
381             node->has2 = true;
382 
383           break;
384         case cMatchNumericRange:
385           if(node->literal1) {
386             if(sscanf(I->charVLA + node->literal1, "%d", &tmp) == 1) {
387               node->numeric1 = tmp;
388               node->has1 = true;
389             }
390           }
391           if(node->literal2) {
392             if(sscanf(I->charVLA + node->literal2, "%d", &tmp) == 1) {
393               node->numeric2 = tmp;
394               node->has2 = true;
395             }
396           }
397           break;
398         }
399         node++;
400       }
401     }
402     I->n_char = n_char;
403     I->n_node = n_node;
404     /*        WordMatcherDump(I); */
405     result = I;
406   }
407   return result;
408 }
409 
recursive_match(CWordMatcher * I,MatchNode * cur_node,const char * text,int * value_ptr)410 static int recursive_match(CWordMatcher * I, MatchNode * cur_node, const char *text,
411                            int *value_ptr)
412 {
413   int ignore_case = I->ignore_case;
414   switch (cur_node->match_mode) {
415   case cMatchLiteral:
416     {
417       char *q = I->charVLA + cur_node->literal1;
418       const char *p = text;
419       while((*p) && (*q)) {
420         if(*p != *q) {
421           if(!ignore_case)
422             return false;
423           else if(tolower(*p) != tolower(*q))
424             return false;
425         }
426         p++;
427         q++;
428       }
429 
430       if(!*q) {
431         if(cur_node->continued)
432           return recursive_match(I, cur_node + 1, p, value_ptr);
433         if(!*p)
434           return true;
435       }
436     }
437     break;
438   case cMatchWildcard:
439     {
440       const char *p;
441       p = text;
442       if(!cur_node->continued)
443         return true;
444       else {
445         while(*p) {
446           if(recursive_match(I, cur_node + 1, p, value_ptr))
447             return 1;
448           p++;
449         }
450       }
451     }
452     break;
453   case cMatchAlphaRange:
454     {
455       char *l1 = I->charVLA + cur_node->literal1;
456       char *l2 = I->charVLA + cur_node->literal2;
457       if(((!cur_node->has1) ||
458           (WordCompare(I->G, l1, text, ignore_case) <= 0)) &&
459          ((!cur_node->has2) || (WordCompare(I->G, l2, text, ignore_case) >= 0)))
460         return true;
461       else
462         return false;
463     }
464     break;
465   case cMatchNumericRange:
466     if(value_ptr) {
467       int value = *value_ptr;
468 
469       if(((!cur_node->has1) ||
470           (cur_node->numeric1 <= value)) &&
471          ((!cur_node->has2) || (cur_node->numeric2 >= value)))
472         return true;
473     } else {
474       int value;
475       if(sscanf(text, "%d", &value) == 1)
476         if(((!cur_node->has1) ||
477             (cur_node->numeric1 <= value)) &&
478            ((!cur_node->has2) || (cur_node->numeric2 >= value)))
479           return true;
480     }
481     break;
482   }
483   return false;
484 }
485 
WordMatcherMatchAlpha(CWordMatcher * I,const char * text)486 int WordMatcherMatchAlpha(CWordMatcher * I, const char *text)
487 {
488   MatchNode *cur_node = I->node;
489   int n_node = I->n_node;
490 
491   while((n_node--) > 0) {
492     if(recursive_match(I, cur_node, text, NULL))
493       return true;
494     else {
495       while(cur_node->continued) {
496         cur_node++;
497         n_node--;
498       }
499       cur_node++;
500     }
501   }
502   return false;
503 }
504 
WordMatcherMatchMixed(CWordMatcher * I,const char * text,int value)505 int WordMatcherMatchMixed(CWordMatcher * I, const char *text, int value)
506 {
507   MatchNode *cur_node = I->node;
508   int n_node = I->n_node;
509 
510   while((n_node--) > 0) {
511     if(recursive_match(I, cur_node, text, &value))
512       return true;
513     else {
514       while(cur_node->continued) {
515         cur_node++;
516         n_node--;
517       }
518       cur_node++;
519     }
520   }
521   return false;
522 }
523 
integer_match(CWordMatcher * I,MatchNode * cur_node,int value)524 static int integer_match(CWordMatcher * I, MatchNode * cur_node, int value)
525 {
526   switch (cur_node->match_mode) {
527   case cMatchLiteral:
528     if((cur_node->has1) && (cur_node->numeric1 == value))
529       return true;
530     break;
531   case cMatchNumericRange:
532     if(((!cur_node->has1) ||
533         (cur_node->numeric1 <= value)) &&
534        ((!cur_node->has2) || (cur_node->numeric2 >= value)))
535       return true;
536     break;
537   }
538   return false;
539 }
540 
WordMatcherMatchInteger(CWordMatcher * I,int value)541 int WordMatcherMatchInteger(CWordMatcher * I, int value)
542 {
543   MatchNode *cur_node = I->node;
544   int n_node = I->n_node;
545 
546   while((n_node--) > 0) {
547     if(integer_match(I, cur_node, value))
548       return true;
549     else {
550       while(cur_node->continued) {
551         cur_node++;
552         n_node--;
553       }
554       cur_node++;
555     }
556   }
557   return false;
558 }
559 
WordMatcherFree(CWordMatcher * I)560 void WordMatcherFree(CWordMatcher * I)
561 {
562   if(I) {
563     VLAFreeP(I->node);
564     VLAFreeP(I->charVLA);
565   }
566   OOFreeP(I);
567 }
568 
WordListNew(PyMOLGlobals * G,const char * st)569 CWordList *WordListNew(PyMOLGlobals * G, const char *st)
570 {
571   int n_word = 0;
572   const char *p;
573   int len = 0;
574   OOCalloc(G, CWordList);
575 
576   if(I) {
577     p = st;
578     /* first, count how many words we have */
579     while(*p) {
580       if(*p > 32) {
581         n_word++;
582         while((*p) > 32) {
583           len++;
584           p++;
585         }
586         len++;
587       } else
588         p++;
589     }
590     /* allocate the storage we'll need to hold the words */
591     {
592       I->word = pymol::malloc<char>(len);
593       I->start = pymol::malloc<char *>(n_word);
594 
595       /* and copy the words */
596 
597       if(I->word && I->start) {
598         char *q = I->word;
599         char **q_ptr = I->start;
600         p = st;
601         while(*p) {
602           if(*p > 32) {
603             *(q_ptr++) = q;
604             while((*p) > 32) {
605               *(q++) = *(p++);
606             }
607             *(q++) = 0;
608             len++;
609           } else
610             p++;
611         }
612         I->n_word = n_word;
613       }
614     }
615   }
616   return I;
617 }
618 
WordListFreeP(CWordList * I)619 void WordListFreeP(CWordList * I)
620 {
621   if(I) {
622     FreeP(I->word);
623     FreeP(I->start);
624     OOFreeP(I);
625   }
626 }
627 
WordListDump(CWordList * I,const char * prefix)628 void WordListDump(CWordList * I, const char *prefix)
629 {
630   if(I) {
631     int a;
632     printf(" %s: n_word %d\n", prefix, I->n_word);
633     for(a = 0; a < I->n_word; a++) {
634       printf(" %s: word %d=[%s]\n", prefix, a, I->start[a]);
635     }
636   }
637 }
638 
WordListIterate(PyMOLGlobals * G,CWordList * I,const char ** ptr,int * hidden)639 int WordListIterate(PyMOLGlobals * G, CWordList * I, const char **ptr, int *hidden)
640 {
641   int result = true;
642   if(*hidden >= 0) {
643     if(*hidden < I->n_word) {
644       (*ptr) = I->start[(*hidden)++];
645     } else {
646       result = false;
647     }
648   }
649   return result;
650 }
651 
WordListMatch(PyMOLGlobals * G,CWordList * I,const char * name,int ignore_case)652 int WordListMatch(PyMOLGlobals * G, CWordList * I, const char *name, int ignore_case)
653 {
654   int result = -1;
655   if(I) {
656     int a;
657     for(a = 0; a < I->n_word; a++) {
658       if(WordMatch(G, I->start[a], name, ignore_case)) {
659         result = a;
660         break;
661       }
662     }
663   }
664   return result;
665 }
666 
WordInit(PyMOLGlobals * G)667 int WordInit(PyMOLGlobals * G)
668 {
669   CWord *I = NULL;
670 
671   I = (G->Word = pymol::calloc<CWord>(1));
672   if(I) {
673     return 1;
674   } else
675     return 0;
676 
677 }
678 
WordFree(PyMOLGlobals * G)679 void WordFree(PyMOLGlobals * G)
680 {
681   FreeP(G->Word);
682 }
683 
WordPrimeCommaMatch(PyMOLGlobals * G,char * p)684 void WordPrimeCommaMatch(PyMOLGlobals * G, char *p)
685 {                               /* replace '+' with ',' */
686   while(*p) {                   /* this should not be done here... */
687     if(*p == '+')
688       if(!((*(p + 1) == 0) || (*(p + 1) == ',') || (*(p + 1) == '+')))
689         *p = ',';
690     p++;
691   }
692 }
693 
WordMatchExact(PyMOLGlobals * G,const char * p,const char * q,int ignCase)694 int WordMatchExact(PyMOLGlobals * G, const char *p, const char *q, int ignCase)
695 
696 
697 /* 0 = no match
698    non-zero = perfect match  */
699 {
700   while((*p) && (*q)) {
701     if(*p != *q) {
702       if(!ignCase)
703         return 0;
704       else if(tolower(*p) != tolower(*q))
705         return 0;
706     }
707     p++;
708     q++;
709   }
710   if((*p) != (*q))
711     return 0;
712   return 1;
713 }
714 
WordMatchNoWild(PyMOLGlobals * G,const char * p,const char * q,int ignCase)715 int WordMatchNoWild(PyMOLGlobals * G, const char *p, const char *q, int ignCase)
716 
717 
718 /* allows for p to match when shorter than q.
719 
720 Returns:
721 0 = no match
722 positive = match out to N characters
723 negative = perfect match  */
724 {
725   int i = 1;
726   while((*p) && (*q)) {
727     if(*p != *q) {
728       if(ignCase) {
729         if(tolower(*p) != tolower(*q)) {
730           i = 0;
731           break;
732         }
733       } else {
734         i = 0;
735         break;
736       }
737     }
738     i++;
739     p++;
740     q++;
741   }
742   if((*p) && (!*q))
743     i = 0;
744   if(i && ((!*p) && (!*q)))     /*exact match gives negative value */
745     i = -i;
746   return (i);
747 }
748 
WordMatch(PyMOLGlobals * G,const char * p,const char * q,int ignCase)749 int WordMatch(PyMOLGlobals * G, const char *p, const char *q, int ignCase)
750 
751 
752 /* allows for terminal wildcard (*) in p
753  * and allows for p to match when shorter than q.
754 
755 Returns:
756 0 = no match
757 positive = match out to N characters
758 negative = perfect/wildcard match  */
759 {
760   int i = 1;
761   char WILDCARD = '*';
762   while((*p) && (*q)) {
763     if(*p != *q) {
764       if(*p == WILDCARD) {
765         i = -i;
766         break;
767       }
768       if(ignCase) {
769         if(tolower(*p) != tolower(*q)) {
770           i = 0;
771           break;
772         }
773       } else {
774         i = 0;
775         break;
776       }
777     }
778     i++;
779     p++;
780     q++;
781   }
782   if((!*q) && (*p == WILDCARD))
783     i = -i;
784   if(*p != WILDCARD) {
785     if((*p) && (!*q))
786       i = 0;
787   }
788   if(i && ((!*p) && (!*q)))     /*exact match gives negative value */
789     i = -i;
790   return (i);
791 }
792 
WordMatchComma(PyMOLGlobals * G,const char * pp,const char * qq,int ignCase)793 int WordMatchComma(PyMOLGlobals * G, const char *pp, const char *qq, int ignCase)
794 
795 
796 /* allows for comma list in p, also allows wildcards (*) in p */
797 {
798   const char *p = pp, *q = qq;
799   int i = 0;
800   char WILDCARD = '*';
801   char pc, qc;
802   int ic = ignCase;
803   int best_i = 0;
804   const char *q_copy;
805   int blank;
806   int trailing_comma = 0;
807 
808   blank = (!*p);
809   q_copy = q;
810   while(((*p) || (blank)) && (best_i >= 0)) {
811     blank = 0;
812     i = 1;
813     q = q_copy;
814     while((pc = (*p)) && (qc = (*q))) {
815       if(pc == ',')
816         break;
817       if(pc != qc) {
818         if(pc == WILDCARD) {
819           i = -i;
820           break;
821         }
822         if(ic) {
823           if(tolower(pc) != tolower(qc)) {
824             i = 0;
825             break;
826           }
827         } else {
828           i = 0;
829           break;
830         }
831       }
832       p++;
833       q++;
834       i++;
835     }
836     if((!*q) && ((*p == WILDCARD) || (*p == ',')))
837       i = -i;
838     if((*p != WILDCARD) && (*p != ','))
839       if((*p) && (!*q))
840         i = 0;
841     if(i && ((!*p) && (!*q)))   /*exact match */
842       i = -i;
843 
844     if(i < 0)
845       best_i = i;
846     else if((best_i >= 0))
847       if(i > best_i)
848         best_i = i;
849     if(best_i >= 0) {
850       while(*p) {
851         if(*p == ',')
852           break;
853         p++;
854       }
855       if(*p == ',') {           /* handle special case, trailing comma */
856         if(*(p + 1))
857           p++;
858         else if(!trailing_comma)
859           trailing_comma = 1;
860         else
861           p++;
862       }
863     }
864   }
865   return (best_i);
866 }
867 
WordMatchCommaExact(PyMOLGlobals * G,const char * p,const char * q,int ignCase)868 int WordMatchCommaExact(PyMOLGlobals * G, const char *p, const char *q, int ignCase)
869 
870 
871 /* allows for comma list in p, no wildcards */
872 {
873   int i = 0;
874   int best_i = 0;
875   const char *q_copy;
876   int blank;
877   int trailing_comma = 0;
878 
879   /*  printf("match? [%s] [%s] ",p,q); */
880 
881   blank = (!*p);
882   q_copy = q;
883   while(((*p) || (blank)) && (best_i >= 0)) {
884     blank = 0;
885     i = 1;
886     q = q_copy;
887     while((*p) && (*q)) {
888       if(*p == ',')
889         break;
890       if(*p != *q) {
891         if(ignCase) {
892           if(tolower(*p) != tolower(*q)) {
893             i = 0;
894             break;
895           }
896         } else {
897           i = 0;
898           break;
899         }
900       }
901       i++;
902       p++;
903       q++;
904     }
905     if((!*q) && (*p == ','))
906       i = -i;
907     if(*p != ',')
908       if((*p) && (!*q))
909         i = 0;
910     if(i && ((!*p) && (!*q)))   /*exact match */
911       i = -i;
912 
913     if(i < 0)
914       best_i = i;
915     else if((best_i >= 0))
916       if(i > best_i)
917         best_i = i;
918     if(best_i >= 0) {
919       while(*p) {
920         if(*p == ',')
921           break;
922         p++;
923       }
924       if(*p == ',') {           /* handle special case, trailing comma */
925         if(*(p + 1))
926           p++;
927         else if(!trailing_comma)
928           trailing_comma = 1;
929         else
930           p++;
931       }
932     }
933   }
934   /*  printf("result: %d\n",best_i); */
935 
936   return (best_i);
937 }
938 
WordMatchCommaInt(PyMOLGlobals * G,const char * p,int number)939 int WordMatchCommaInt(PyMOLGlobals * G, const char *p, int number)
940 {
941   WordType buffer;
942   sprintf(buffer, "%d", number);
943   return (WordMatchComma(G, p, buffer, 1));
944 }
945 
WordIndex(PyMOLGlobals * G,WordType * list,const char * word,int minMatch,int ignCase)946 int WordIndex(PyMOLGlobals * G, WordType * list, const char *word, int minMatch, int ignCase)
947 {
948   int c, i, mi, mc;
949   int result = -1;
950   c = 0;
951   mc = -1;
952   mi = -1;
953   while(list[c][0]) {
954     i = WordMatch(G, word, list[c], ignCase);
955     if(i > 0) {
956       if(mi < i) {
957         mi = i;
958         mc = c;
959       }
960     } else if(i < 0) {
961       if((-i) < minMatch)
962         mi = minMatch + 1;      /*exact match always matches */
963       else
964         mi = (-i);
965       mc = c;
966     }
967     c++;
968   }
969   if((mi > minMatch))
970     result = mc;
971   return (result);
972 
973 }
974 
WordKey(PyMOLGlobals * G,WordKeyValue * list,const char * word,int minMatch,int ignCase,int * exact)975 int WordKey(PyMOLGlobals * G, WordKeyValue * list, const char *word, int minMatch, int ignCase,
976             int *exact)
977 {
978   int c, i, mi, mc;
979   int result = 0;
980   c = 0;
981   mc = -1;
982   mi = -1;
983   *exact = false;
984   while(list[c].word[0]) {
985     i = WordMatchNoWild(G, word, list[c].word, ignCase);
986     if(i > 0) {
987       if(mi < i) {
988         mi = i;
989         mc = list[c].value;
990       }
991     } else if(i < 0) {
992       *exact = true;
993       if((-i) <= minMatch) {
994         mi = minMatch + 1;      /*exact match always matches */
995       } else
996         mi = (-i);
997       mc = list[c].value;
998     }
999     c++;
1000   }
1001   if((mi >= minMatch))
1002     result = mc;
1003   return (result);
1004 }
1005