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