1 
2 //////////////////////////////////////////////////////////////////////
3 //
4 //  FILE:       misc.h
5 //              Miscellaneous routines (File I/O, etc)
6 //
7 //  Part of:    Scid (Shane's Chess Information Database)
8 //  Version:    3.5
9 //
10 //  Notice:     Copyright (c) 2001-2003  Shane Hudson.  All rights reserved.
11 //              Copyright (C) 2015  Fulvio Benini
12 //
13 //  Author:     Shane Hudson (sgh@users.sourceforge.net)
14 //
15 //////////////////////////////////////////////////////////////////////
16 
17 
18 #ifndef SCID_MISC_H
19 #define SCID_MISC_H
20 
21 #include "common.h"
22 #include <algorithm>
23 #include <string>
24 #include <cstring>
25 #include <stdio.h>
26 #include <ctype.h>   // For isspace(), etc
27 #include <cstdlib>
28 #include <vector>
29 
30 /**
31  * class StrRange - parse a string interpreting its content as 1 or 2 integers
32  *                  separated by whitespace.
33  * The integers represent the min and max value of a range.
34  * If only one integer is provided it will represent both the min and max value.
35  */
36 class StrRange {
37 protected:
38 	long min_;
39 	long max_;
40 
41 protected:
StrRange()42 	StrRange()
43 	: min_(0), max_(0) {}
44 
45 public:
StrRange(const char * range)46 	explicit StrRange(const char* range) {
47 		char* next;
48 		min_ = std::strtol(range, &next, 10);
49 		char* end;
50 		max_ = std::strtol(next, &end, 10);
51 		if (next == end) max_ = min_;
52 		if (min_ > max_) std::swap(min_, max_);
53 	}
54 
55 	/// @returns true if @e val is >= min_ and <= max_
inRange(long val)56 	bool inRange(long val) const {
57 		if (val < min_ || val > max_) return false;
58 		return true;
59 	}
60 };
61 
62 
63 class Progress {
64 public:
65 	struct Impl {
~ImplImpl66 		virtual ~Impl() {}
67 		virtual bool report(size_t done, size_t total, const char* msg) = 0;
68 	};
69 
f_(f)70 	Progress(Impl* f = NULL) : f_(f) {}
71 	Progress(const Progress&) = delete;
~Progress()72 	~Progress() { delete f_; }
73 
report(size_t done,size_t total)74 	bool report(size_t done, size_t total) const {
75 		return operator()(done, total);
76 	}
operator()77 	bool operator()(size_t done, size_t total, const char* msg = NULL) const {
78 		if (f_) return f_->report(done, total, msg);
79 		return true;
80 	}
81 
82 private:
83 	Impl* f_;
84 };
85 
86 
87 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
88 // strGetFilterOp:
89 //    Converts a string value to a filter operation value.
90 enum filterOpT { FILTEROP_AND, FILTEROP_OR, FILTEROP_RESET };
91 
strGetFilterOp(const char * str)92 inline filterOpT strGetFilterOp (const char * str)
93 {
94     switch (*str) {
95         // AND:
96         case 'A': case 'a': case '0': return FILTEROP_AND;
97         // OR:
98         case 'O': case 'o': case '1': return FILTEROP_OR;
99         // RESET:
100         case 'R': case 'r': case '2': return FILTEROP_RESET;
101     }
102     // Default is RESET.
103     return FILTEROP_RESET;
104 }
105 
106 // ECO string routines
107 //
108 void eco_ToString (ecoT ecoCode, char * ecoStr, bool extensions = true);
eco_ToBasicString(ecoT ecoCode,char * ecoStr)109 inline void eco_ToBasicString (ecoT ecoCode, char * ecoStr) {
110     eco_ToString (ecoCode, ecoStr, false);
111 }
eco_ToExtendedString(ecoT ecoCode,char * ecoStr)112 inline void eco_ToExtendedString (ecoT ecoCode, char * ecoStr) {
113     eco_ToString (ecoCode, ecoStr, true);
114 }
115 ecoT eco_FromString (const char * ecoStr);
116 ecoT eco_LastSubCode (ecoT ecoCode);
117 ecoT eco_BasicCode (ecoT ecoCode);
118 ecoT eco_Reduce(ecoT eco);
119 
120 // String routines. Some are identical to ANSI standard functions, but
121 //      I have included them:
122 //      (a) to keep nice consistent naming conventions, e.g. strCopy.
123 //      (b) so stats can easily be kept by modifying the functions.
124 //      (c) so some can be made inline for speed if necessary.
125 
strStartHash(const char * str)126 inline uint32_t strStartHash(const char* str) {
127 	ASSERT(str != 0);
128 	const unsigned char* s = reinterpret_cast<const unsigned char*>(str);
129 
130 	uint32_t tmp = static_cast<unsigned char>(tolower(*s));
131 	uint32_t result = tmp << 24;
132 	if (*s == '\0') return result;
133 	tmp = static_cast<unsigned char>(tolower(*++s));
134 	result += tmp << 16;
135 	if (*s == '\0') return result;
136 	tmp = static_cast<unsigned char>(tolower(*++s));
137 	result += tmp << 8;
138 	if (*s == '\0') return result;
139 	result += static_cast<unsigned char>(tolower(*++s));
140 	return result;
141 }
142 
143 char * strDuplicate (const char * str);
144 
145 char * strAppend (char * target, const char * extra);
146 uint   strPad (char * target, const char * orig, int length, char pad);
147 const char * strFirstChar (const char * target, char matchChar);
148 const char * strLastChar (const char * target, char matchChar);
149 void   strStrip (char * str, char ch);
150 
151 const char * strTrimLeft (const char * target, const char * trimChars);
strTrimLeft(const char * target)152 inline const char * strTrimLeft (const char * target) {
153     return strTrimLeft (target, " \t\r\n");
154 }
155 uint   strTrimSuffix (char * target, char suffixChar);
156 void   strTrimDate (char * str);
157 void   strTrimMarkCodes (char * str);
158 void   strTrimMarkup (char * str);
159 const char * strFirstWord (const char * str);
160 const char * strNextWord (const char * str);
161 
162 // strPlural:
163 //    Returns the empty string if its parameter is 1, or "s" otherwise.
164 inline const char *
strPlural(uint x)165 strPlural (uint x) {
166     return (x == 1 ? "" : "s");
167 }
168 
169 bool   strIsUnknownName (const char * str);
170 
171 // strIsSurnameOnly: returns true if a string appears to only
172 //    contain a surname.
173 bool   strIsSurnameOnly (const char * name);
174 
175 bool   strGetBoolean (const char * str);
176 
177 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
178 // strGetInteger():
179 //    Extracts a signed base-10 value from a string.
180 //    Defaults to zero (as strtol does) for non-numeric strings.
181 inline int
strGetInteger(const char * str)182 strGetInteger(const char * str)
183 {
184 	return std::strtol(str, NULL, 10);
185 }
186 
187 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
188 // strGetUnsigned():
189 //    Extracts an unsigned base-10 value from a string.
190 //    Defaults to zero (as strtoul does) for non-numeric strings.
191 //
strGetUnsigned(const char * str)192 inline uint32_t strGetUnsigned(const char* str) {
193 	ASSERT(str != NULL);
194 	return static_cast<uint32_t>(std::strtoul(str, NULL, 10));
195 }
196 
strCaseCompare(const char * str1,const char * str2)197 inline int strCaseCompare(const char* str1, const char* str2) {
198 	ASSERT(str1 != NULL && str2 != NULL);
199 	const unsigned char* s1 = reinterpret_cast<const unsigned char*>(str1);
200 	const unsigned char* s2 = reinterpret_cast<const unsigned char*>(str2);
201 	int c1, c2;
202 	do {
203 		c1 = tolower(*s1++);
204 		c2 = tolower(*s2++);
205 		if (c1 == '\0')
206 			break;
207 	} while (c1 == c2);
208 
209 	return c1 - c2;
210 }
211 
strCompareRound(const char * str1,const char * str2)212 inline int strCompareRound(const char* str1, const char* str2) {
213 	ASSERT(str1 != NULL && str2 != NULL);
214 	uint32_t a = strGetUnsigned(str1);
215 	uint32_t b = strGetUnsigned(str2);
216 	if (a == b)
217 		return strCaseCompare(str1, str2);
218 	return (a < b) ? -1 : 1;
219 }
220 
strEqual(const char * str1,const char * str2)221 inline bool strEqual(const char* str1, const char* str2) {
222 	ASSERT(str1 != NULL && str2 != NULL);
223 	return (std::strcmp(str1, str2) == 0);
224 }
225 
226 void   strGetIntegers (const char * str, int * results, uint nResults);
227 void   strGetUnsigneds (const char * str, uint * results, uint nResults);
228 resultT strGetResult (const char * str);
229 
230 typedef uint flagT;
231 const flagT FLAG_EMPTY = 0;
232 const flagT FLAG_YES = 1;
233 const flagT FLAG_NO = 2;
234 const flagT FLAG_BOTH = 3;
flag_Yes(flagT t)235 inline bool flag_Yes (flagT t) { return (t & FLAG_YES); }
flag_No(flagT t)236 inline bool flag_No (flagT t) { return (t & FLAG_NO); }
237 flagT  strGetFlag (const char * str);
238 
239 squareT strGetSquare (const char * str);
240 
241 inline uint
strTrimFileSuffix(char * target)242 strTrimFileSuffix (char * target) { return strTrimSuffix (target, '.'); }
243 
244 inline const char *
strFileSuffix(const char * target)245 strFileSuffix (const char * target) { return strLastChar (target, '.'); }
246 
247 
248 
249 int strUniqueExactMatch (const char * keyStr, const char ** strTable,
250                          bool exact);
251 
strUniqueMatch(const char * keyStr,const char ** strTable)252 inline int strUniqueMatch (const char * keyStr, const char ** strTable) {
253     return strUniqueExactMatch (keyStr, strTable, false);
254 }
strExactMatch(const char * keyStr,const char ** strTable)255 inline int strExactMatch (const char * keyStr, const char ** strTable) {
256     return strUniqueExactMatch (keyStr, strTable, true);
257 }
258 
259 inline bool
strContainsChar(const char * str,char ch)260 strContainsChar (const char * str, char ch)
261 {
262     while (*str) {
263         if (*str == ch) { return true; }
264         str++;
265     }
266     return false;
267 }
268 
269 // WARNING: Avoid this function!
270 // Considering that the sign of a char is implementation-defined [3.9.1], the
271 // int conversion ("[4.7.3] If the destination type is signed, the value is
272 // unchanged if it can be represented in the destination type") can yield
273 // different results on different architectures or compilers.
274 // A better alternative is the standard function strcmp() that returns the
275 // difference between the values of the first pair of characters (both
276 // interpreted as unsigned char) that differ in the strings being compared.
strCompare(const char * s1,const char * s2)277 inline int strCompare(const char* s1, const char* s2)
278 {
279     ASSERT (s1 != NULL  &&  s2 != NULL);
280     while (1) {
281         if (*s1 != *s2) {
282             return ((int) *s1) - ((int) *s2);
283         }
284         if (*s1 == 0)
285             break;
286         s1++; s2++;
287     }
288     return 0;
289 }
290 
291 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
292 // strCopy(): same as strcpy().
293 //
294 inline void
strCopy(char * target,const char * original)295 strCopy (char * target, const char * original)
296 {
297     ASSERT (target != NULL  &&  original != NULL);
298     while (*original != 0) {
299         *target = *original;
300         target++;
301         original++;
302     }
303     *target = 0;
304 }
305 
306 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
307 // strPrefix():
308 //       Returns the length of the common prefix of two strings.
309 //
310 inline uint
strPrefix(const char * s1,const char * s2)311 strPrefix (const char * s1, const char * s2)
312 {
313     ASSERT (s1 != NULL  &&  s2 != NULL);
314     uint count = 0;
315     while (*s1 == *s2) {
316         if (*s1 == 0) { // seen end of string, strings are identical
317             return count;
318         }
319         count++; s1++; s2++;
320     }
321     return count;
322 }
323 
324 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
325 // strIsPrefix():
326 //      Returns true if the prefix string is a prefix of longStr.
327 inline bool
strIsPrefix(const char * prefix,const char * longStr)328 strIsPrefix (const char * prefix, const char * longStr)
329 {
330     while (*prefix) {
331         if (*longStr == 0) { return false; }
332         if (*prefix != *longStr) { return false; }
333         prefix++;
334         longStr++;
335     }
336     return true;
337 }
338 
339 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
340 // strIsCasePrefix():
341 //      Returns true if the prefix string is a case-insensitive
342 //      prefix of longStr.
343 inline bool
strIsCasePrefix(const char * prefix,const char * longStr)344 strIsCasePrefix (const char * prefix, const char * longStr)
345 {
346     typedef unsigned char U;
347     while (*prefix) {
348         if (*longStr == 0) { return false; }
349         if (tolower(U(*prefix)) != tolower(U(*longStr))) { return false; }
350         prefix++;
351         longStr++;
352     }
353     return true;
354 }
355 
356 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
357 // strIsAlphaPrefix():
358 //      Returns true if the prefix string is a prefix of longStr.
359 //      Unlike strIsPrexix(), this version is case-insensitive and
360 //      spaces are ignored.
361 //      Example: strIsAlphaPrefix ("smith,j", "Smith, John") == true.
362 inline bool
strIsAlphaPrefix(const char * prefix,const char * longStr)363 strIsAlphaPrefix (const char * prefix, const char * longStr)
364 {
365     typedef unsigned char U;
366     while (*prefix) {
367         while (*prefix == ' ') { prefix++; }
368         while (*longStr == ' ') { longStr++; }
369         if (*longStr == 0) { return false; }
370         if (tolower(U(*prefix)) != tolower(U(*longStr))) { return false; }
371         prefix++;
372         longStr++;
373     }
374     return true;
375 }
376 
377 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
378 // strContains():
379 //      Returns true if longStr contains an occurrence of keyStr,
380 //      case-sensitive and NOT ignoring any characters such as spaces.
381 inline bool
strContains(const char * longStr,const char * keyStr)382 strContains (const char * longStr, const char * keyStr)
383 {
384     while (*longStr) {
385         if (strIsPrefix (keyStr, longStr)) { return true; }
386         longStr++;
387     }
388     return false;
389 }
390 
391 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
392 // strAlphaContains():
393 //      Returns true if longStr contains an occurrence of keyStr,
394 //      case-insensitive and ignoring spaces.
395 //      Example: strAlphaContains ("Smith, John", "th,j") == true.
396 //
397 inline bool
strAlphaContains(const char * longStr,const char * keyStr)398 strAlphaContains (const char * longStr, const char * keyStr)
399 {
400     while (*longStr) {
401         if (strIsAlphaPrefix (keyStr, longStr)) { return true; }
402         longStr++;
403     }
404     return false;
405 }
406 
407 inline uint
strLength(const char * str)408 strLength (const char * str)
409 {
410     ASSERT(str != NULL);
411     uint len = 0;
412     while (*str != 0) { len++; str++; }
413     return len;
414 }
415 
416 
417 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
418 // strTrimRight():
419 //      Trims the provided string in-place, removing the
420 //      end characters that match the trimChars.
421 //      Returns the number of characters trimmed.
422 //      E.g., strTrimRight("abcyzyz", "yz") would leave the string
423 //      as "abc".
strTrimRight(char * target,const char * trimChars,size_t nTrimCh)424 inline void strTrimRight(char* target, const char* trimChars, size_t nTrimCh) {
425 	const char* endTrim = trimChars + nTrimCh;
426 	size_t iCh = strlen(target);
427 	for (; iCh > 0; --iCh) {
428 		if (std::find(trimChars, endTrim, target[iCh - 1]) == endTrim)
429 			break;
430 	}
431 	target[iCh] = '\0';
432 }
strTrimRight(char * target)433 inline void strTrimRight(char* target) {
434 	return strTrimRight(target, " \t\r\n", 4);
435 }
436 
437 #endif  // #ifdef SCID_MISC_H
438 
439 //////////////////////////////////////////////////////////////////////
440 //  EOF: misc.h
441 //////////////////////////////////////////////////////////////////////
442 
443