1 /* melder_search.cpp
2  *
3  * Copyright (C) 1992-2018,2020 Paul Boersma
4  *
5  * This code is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This code is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this work. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "melder.h"
20 
21 /********** NUMBER AND STRING COMPARISONS **********/
22 
Melder_numberMatchesCriterion(double value,kMelder_number which,double criterion)23 bool Melder_numberMatchesCriterion (double value, kMelder_number which, double criterion) {
24 	return
25 		(which == kMelder_number::EQUAL_TO && value == criterion) ||
26 		(which == kMelder_number::NOT_EQUAL_TO && value != criterion) ||
27 		(which == kMelder_number::LESS_THAN && value < criterion) ||
28 		(which == kMelder_number::LESS_THAN_OR_EQUAL_TO && value <= criterion) ||
29 		(which == kMelder_number::GREATER_THAN && value > criterion) ||
30 		(which == kMelder_number::GREATER_THAN_OR_EQUAL_TO && value >= criterion);
31 }
32 
str32str_word_optionallyCaseSensitive(conststring32 string,conststring32 find,bool ink,bool caseSensitive,bool startFree,bool endFree)33 inline static char32 * str32str_word_optionallyCaseSensitive (conststring32 string, conststring32 find,
34 	bool ink, bool caseSensitive, bool startFree, bool endFree) noexcept
35 {
36 	integer length = str32len (find);
37 	if (length == 0)
38 		return (char32 *) string;
39 	conststring32 movingString = string;
40 	do {
41 		conststring32 movingFind = find;
42 		char32 firstCharacter = ( caseSensitive ? * movingFind ++ : Melder_toLowerCase (* movingFind ++) );   // optimization
43 		do {
44 			char32 kar;
45 			do {
46 				kar = ( caseSensitive ? * movingString ++ : Melder_toLowerCase (* movingString ++) );
47 				if (kar == U'\0') return nullptr;
48 			} while (kar != firstCharacter);
49 		} while (caseSensitive ? str32ncmp (movingString, movingFind, length - 1) : str32ncmp_caseInsensitive (movingString, movingFind, length - 1));
50 		/*
51 			We found a match.
52 		*/
53 		movingString --;
54 		if ((startFree || movingString == string || ( ink ? Melder_isHorizontalOrVerticalSpace (movingString [-1]) : ! Melder_isWordCharacter (movingString [-1]) )) &&
55 			(endFree || movingString [length] == U'\0' || (ink ? Melder_isHorizontalOrVerticalSpace (movingString [length]) : ! Melder_isWordCharacter (movingString [length]) )))
56 		{
57 			return (char32 *) movingString;
58 		} else {
59 			movingString ++;
60 		}
61 	} while (true);
62 	return nullptr;   // can never occur
63 }
64 
Melder_stringMatchesCriterion(conststring32 value,kMelder_string which,conststring32 criterion,bool caseSensitive)65 bool Melder_stringMatchesCriterion (conststring32 value, kMelder_string which, conststring32 criterion, bool caseSensitive) {
66 	if (! value)
67 		value = U"";   // regard null strings as empty strings, as is usual in Praat
68 	if (! criterion)
69 		criterion = U"";   // regard null strings as empty strings, as is usual in Praat
70 	switch (which)
71 	{
72 		case kMelder_string::UNDEFINED:
73 		{
74 			Melder_fatal (U"Melder_stringMatchesCriterion: unknown criterion.");
75 		}
76 		case kMelder_string::EQUAL_TO:
77 		case kMelder_string::NOT_EQUAL_TO:
78 		{
79 			bool doesMatch = str32equ_optionallyCaseSensitive (value, criterion, caseSensitive);
80 			return which == kMelder_string::EQUAL_TO ? doesMatch : ! doesMatch;
81 		}
82 		case kMelder_string::CONTAINS:
83 		case kMelder_string::DOES_NOT_CONTAIN:
84 		{
85 			bool doesMatch = !! str32str_optionallyCaseSensitive (value, criterion, caseSensitive);
86 			return which == kMelder_string::CONTAINS ? doesMatch : ! doesMatch;
87 		}
88 		case kMelder_string::STARTS_WITH:
89 		case kMelder_string::DOES_NOT_START_WITH:
90 		{
91 			bool doesMatch = str32nequ_optionallyCaseSensitive (value, criterion, str32len (criterion), caseSensitive);
92 			return which == kMelder_string::STARTS_WITH ? doesMatch : ! doesMatch;
93 		}
94 		case kMelder_string::ENDS_WITH:
95 		case kMelder_string::DOES_NOT_END_WITH:
96 		{
97 			integer criterionLength = str32len (criterion), valueLength = str32len (value);
98 			bool doesMatch = criterionLength <= valueLength &&
99 				str32equ_optionallyCaseSensitive (value + valueLength - criterionLength, criterion, caseSensitive);
100 			return which == kMelder_string::ENDS_WITH ? doesMatch : ! doesMatch;
101 		}
102 		case kMelder_string::CONTAINS_WORD:
103 		case kMelder_string::DOES_NOT_CONTAIN_WORD:
104 		{
105 			bool doesMatch = !! str32str_word_optionallyCaseSensitive (value, criterion, false, caseSensitive, false, false);
106 			return which == kMelder_string::CONTAINS_WORD ? doesMatch : ! doesMatch;
107 		}
108 		case kMelder_string::CONTAINS_WORD_STARTING_WITH:
109 		case kMelder_string::DOES_NOT_CONTAIN_WORD_STARTING_WITH:
110 		{
111 			bool doesMatch = !! str32str_word_optionallyCaseSensitive (value, criterion, false, caseSensitive, false, true);
112 			return which == kMelder_string::CONTAINS_WORD_STARTING_WITH ? doesMatch : ! doesMatch;
113 		}
114 		case kMelder_string::CONTAINS_WORD_ENDING_WITH:
115 		case kMelder_string::DOES_NOT_CONTAIN_WORD_ENDING_WITH:
116 		{
117 			bool doesMatch = !! str32str_word_optionallyCaseSensitive (value, criterion, false, caseSensitive, true, false);
118 			return which == kMelder_string::CONTAINS_WORD_ENDING_WITH ? doesMatch : ! doesMatch;
119 		}
120 		case kMelder_string::CONTAINS_INK:
121 		case kMelder_string::DOES_NOT_CONTAIN_INK:
122 		{
123 			bool doesMatch = !! str32str_word_optionallyCaseSensitive (value, criterion, true, caseSensitive, false, false);
124 			return which == kMelder_string::CONTAINS_INK ? doesMatch : ! doesMatch;
125 		}
126 		case kMelder_string::CONTAINS_INK_STARTING_WITH:
127 		case kMelder_string::DOES_NOT_CONTAIN_INK_STARTING_WITH:
128 		{
129 			bool doesMatch = !! str32str_word_optionallyCaseSensitive (value, criterion, true, caseSensitive, false, true);
130 			return which == kMelder_string::CONTAINS_INK_STARTING_WITH ? doesMatch : ! doesMatch;
131 		}
132 		case kMelder_string::CONTAINS_INK_ENDING_WITH:
133 		case kMelder_string::DOES_NOT_CONTAIN_INK_ENDING_WITH:
134 		{
135 			bool doesMatch = !! str32str_word_optionallyCaseSensitive (value, criterion, true, caseSensitive, true, false);
136 			return which == kMelder_string::CONTAINS_INK_ENDING_WITH ? doesMatch : ! doesMatch;
137 		}
138 		case kMelder_string::MATCH_REGEXP:
139 		{
140 			char32 *place = nullptr;
141 			regexp *compiled_regexp = CompileRE_throwable (criterion, ! REDFLT_CASE_INSENSITIVE);
142 			if (ExecRE (compiled_regexp, nullptr, value, nullptr, 0, U'\0', U'\0', nullptr, nullptr))
143 				place = compiled_regexp -> startp [0];
144 			free (compiled_regexp);
145 			return !! place;
146 		}
147 	}
148 	return false;   // should not occur
149 }
150 
151 /* End of file melder_search.cpp */
152