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