Line data Source code
1 : //////////////////////////////////////////////////////////////////////
2 : //
3 : // FILE: misc.cpp
4 : // Miscellaneous routines (File I/O, etc)
5 : //
6 : // Part of: Scid (Shane's Chess Information Database)
7 : // Version: 3.5
8 : //
9 : // Notice: Copyright (c) 2001-2003 Shane Hudson. All rights reserved.
10 : //
11 : // Author: Shane Hudson (sgh@users.sourceforge.net)
12 : //
13 : //////////////////////////////////////////////////////////////////////
14 :
15 : #include "common.h"
16 : #include "misc.h"
17 : #include <stdio.h>
18 : #include <ctype.h> // For isspace() function.
19 : #include <cmath>
20 :
21 : //////////////////////////////////////////////////////////////////////
22 : // ECO Code Routines
23 :
24 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 : // eco_FromString():
26 : // Extract an ECO code from a string.
27 : //
28 : // Eco code numbering: no eco = 0, A00 = 1, A01 = 132, etc.
29 : // That is, each basic ECO code = previous + 131.
30 : // The extra 130 subcodes are the extended code:
31 : // a, a1, a2, a3, a4, b, b1, .... z, z1, z2, z3, z4. (130 in total).
32 : //
33 : // Improvement, March 2000: now case-insensitive for first letter,
34 : // for example, a41 == A41.
35 : ecoT
36 37 : eco_FromString (const char * ecoStr)
37 : {
38 37 : ecoT eco = ECO_None;
39 : // Get the basic Eco code from the first 3 characters: they MUST be in
40 : // the range "A00" to "E99" or the eco code will be considered empty.
41 : // Changed, June 1999: now accepts partial ECO codes, e.g. "C1" -> C10
42 :
43 37 : if (*ecoStr >= 'A' && *ecoStr <= 'E') {
44 37 : eco = (*ecoStr - 'A') * 13100;
45 0 : } else if (*ecoStr >= 'a' && *ecoStr <= 'e') {
46 0 : eco = (*ecoStr - 'a') * 13100;
47 : } else {
48 0 : return 0;
49 : }
50 37 : ecoStr++;
51 37 : if (! *ecoStr) { return eco + 1; }
52 :
53 37 : if (*ecoStr < '0' || *ecoStr > '9') { return 0; }
54 37 : eco += (*ecoStr - '0') * 1310;
55 37 : ecoStr++;
56 37 : if (! *ecoStr) { return eco + 1; }
57 :
58 37 : if (*ecoStr < '0' || *ecoStr > '9') { return 0; }
59 37 : eco += (*ecoStr - '0') * 131;
60 37 : ecoStr++;
61 :
62 : // Now check for the optional extended code: a, a1, ... z2, z3, z4.
63 37 : if (*ecoStr >= 'a' && *ecoStr <= 'z') {
64 8 : eco++;
65 8 : eco += (*ecoStr - 'a') * 5;
66 8 : ecoStr++;
67 8 : if (*ecoStr >= '1' && *ecoStr <= '4') {
68 0 : eco += *ecoStr - '0';
69 : }
70 : }
71 37 : return eco + 1;
72 : }
73 :
74 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75 : // eco_ToString():
76 : // Convert an ECO code to its string representation.
77 : void
78 31 : eco_ToString (ecoT ecoCode, char * ecoStr, bool extensions)
79 : {
80 31 : char * s = ecoStr;
81 31 : if (ecoCode == ECO_None) { *s = 0; return; }
82 31 : ecoCode--;
83 :
84 : // First the base code value:
85 :
86 31 : ecoT basicCode = ecoCode / 131; // 131 = 26 * 5 + 1 subcodes.
87 31 : *s++ = basicCode / 100 + 'A';
88 31 : *s++ = (basicCode % 100) / 10 + '0';
89 31 : *s++ = (basicCode % 10) + '0';
90 :
91 : // Now the optional extensions:
92 31 : if (extensions) {
93 31 : ecoCode = ecoCode % 131;
94 31 : if (ecoCode > 0) {
95 2 : ecoCode--;
96 2 : *s++ = (ecoCode / 5) + 'a';
97 2 : ecoCode = ecoCode % 5;
98 2 : if (ecoCode > 0) { *s++ = (ecoCode + '0'); }
99 : }
100 31 : *s = 0;
101 : }
102 31 : return;
103 : }
104 :
105 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106 : // eco_BasicCode():
107 : // Converts an ECO code to its basic form, without any
108 : // Scid-specific extensions.
109 : ecoT
110 0 : eco_BasicCode (ecoT eco)
111 : {
112 0 : if (eco == ECO_None) { return ECO_None; }
113 :
114 0 : eco--;
115 0 : eco /= 131;
116 0 : eco *= 131;
117 0 : return eco + 1;
118 : }
119 :
120 : /**
121 : * ecoReduce() - maps eco to a smaller set
122 : * @param eco: the eco value to convert (must be != 0)
123 : *
124 : * Scid ECO subcodes use 131 values for each canonical ECO.
125 : * For example A00 is divided in A00,A00a,A00a1,A00a2,A00a3,A00a4,A00b...A00z4
126 : * corresponding to eco values 1,2,3,4,5,6,7...131 (value 0 means no ECO).
127 : * This functions will map subECOs like A00a1...A00a4 into A00a, reducing
128 : * the 131 values to 27. The previous sequence will became 0,1,1,1,1,1,2...26
129 : */
130 0 : ecoT eco_Reduce(ecoT eco) {
131 0 : ASSERT(eco != 0);
132 :
133 0 : eco--;
134 0 : ecoT res = (eco / 131) * 27;
135 0 : return res + static_cast<ecoT>(std::ceil((eco % 131) / 5.0));
136 : }
137 :
138 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
139 : // eco_LastSubCode():
140 : // Converts an ECO code to the deepest subcode it could contain.
141 : // Examples: B91a -> B91a4 and B91 -> B91z4.
142 : ecoT
143 0 : eco_LastSubCode (ecoT eco)
144 : {
145 0 : if (eco == ECO_None) { return ECO_None; }
146 :
147 : // if just a basic ECO code (1 letter, 2 digits), add the "z":
148 0 : eco--;
149 0 : if ((eco % 131) == 0) { eco += 126; } // 126 = 5 * 25 + 1.
150 :
151 : // Now if no final digit, add the "4":
152 0 : if (((eco % 131) % 5) == 1) { eco += 4; }
153 0 : return eco + 1;
154 : }
155 :
156 : //////////////////////////////////////////////////////////////////////
157 : // String Routines
158 :
159 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
160 : // strCopyExclude(): copies original to target, filtering out any
161 : // characters in excludeChars.
162 : void
163 0 : strCopyExclude (char * target, const char * original,
164 : const char * excludeChars)
165 : {
166 0 : while (*original != 0) {
167 0 : int exclude = 0;
168 0 : for (char * s = (char *) excludeChars; *s; s++) {
169 0 : if (*original == *s) {
170 0 : exclude = 1;
171 0 : break;
172 : }
173 : }
174 0 : if (!exclude) {
175 0 : *target = *original;
176 0 : target++;
177 : }
178 0 : original++;
179 : }
180 0 : *target = 0;
181 0 : }
182 :
183 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
184 : // strAppend():
185 : // Appends extra to the end of target, and returns a pointer
186 : // to the new END of the string target.
187 : //
188 : char *
189 0 : strAppend (char * target, const char * extra)
190 : {
191 0 : ASSERT (target != NULL && extra != NULL);
192 0 : while (*target != 0) { target++; } // get to end of target string
193 0 : while (*extra != 0) {
194 0 : *target = *extra;
195 0 : target++;
196 0 : extra++;
197 : }
198 0 : *target = 0;
199 0 : return target;
200 : }
201 :
202 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
203 : // strDuplicate(): Duplicates a string using new[] operator.
204 : //
205 : char *
206 0 : strDuplicate (const char * original)
207 : {
208 0 : ASSERT (original != NULL);
209 0 : char * newStr = new char [strLength(original) + 1];
210 0 : if (newStr == NULL) return NULL;
211 0 : char *s = newStr;
212 0 : while (*original != 0) {
213 0 : *s = *original;
214 0 : s++; original++;
215 : }
216 0 : *s = 0; // Add trailing '\0'.
217 : //printf ("Dup: %p: %s\n", newStr, newStr);
218 0 : return newStr;
219 : }
220 :
221 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
222 : // strPad():
223 : // Copies original string to target, but copies *exactly* 'width'
224 : // bytes. If the original is longer than specified width, not all of
225 : // original will be copied to target. If original is shorter, then
226 : // target will be padded out to 'width' bytes with the padding char.
227 : //
228 : // If the width is negative, no trimming or padding is done and
229 : // the result is just a regular string copy.
230 : //
231 : // The return value is the length copied: always 'width' if
232 : // width is >= 0, or the length of original if 'width' is negative.
233 : //
234 : uint
235 0 : strPad (char * target, const char * original, int width, char padding)
236 : {
237 0 : ASSERT (target != NULL && original != NULL);
238 0 : if (width < 0) {
239 0 : strCopy (target, original);
240 0 : return strLength (original);
241 : }
242 0 : int len = width;
243 0 : while (len > 0) {
244 0 : if (*original == 0) {
245 0 : break;
246 : }
247 0 : *target = *original;
248 0 : target++;
249 0 : original++;
250 0 : len--;
251 : }
252 0 : while (len--) { *target++ = padding; }
253 0 : *target = 0;
254 0 : return width;
255 : }
256 :
257 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
258 : // strFirstChar():
259 : // Returns the pointer into the provided string where the
260 : // FIRST occurrence of matchChar is, or NULL if the string
261 : // does not contain matchChar at all.
262 : // Equivalent to strchr().
263 : const char *
264 0 : strFirstChar (const char * target, char matchChar)
265 : {
266 0 : const char * s = target;
267 0 : while (*s != 0) {
268 0 : if (*s == matchChar) { return s; }
269 0 : s++;
270 : }
271 0 : return NULL;
272 : }
273 :
274 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275 : // strLastChar():
276 : // Returns the pointer into the provided string where the
277 : // LAST occurrence of matchChar is, or NULL if the string
278 : // does not contain matchChar at all.
279 : // Equivalent to strrchr().
280 : const char *
281 0 : strLastChar (const char * target, char matchChar)
282 : {
283 0 : const char * s = target;
284 0 : const char * last = NULL;
285 0 : while (*s != 0) {
286 0 : if (*s == matchChar) { last = s; }
287 0 : s++;
288 : }
289 0 : return last;
290 : }
291 :
292 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293 : // strStrip():
294 : // Removes all occurrences of the specified char from the string.
295 : void
296 0 : strStrip (char * str, char ch)
297 : {
298 0 : char * s = str;
299 0 : while (*str != 0) {
300 0 : if (*str != ch) {
301 0 : if (s != str) { *s = *str; }
302 0 : s++;
303 : }
304 0 : str++;
305 : }
306 0 : *s = 0;
307 0 : }
308 :
309 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
310 : // strTrimLeft():
311 : // Returns the pointer into the provided string where the first
312 : // character that does NOT equal a trimChar occurs.
313 : const char *
314 0 : strTrimLeft (const char * target, const char * trimChars)
315 : {
316 0 : const char * s = target;
317 0 : while (*s != 0) {
318 0 : if (! strContainsChar (trimChars, *s)) { break; }
319 0 : s++;
320 : }
321 0 : return s;
322 : }
323 :
324 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
325 : // strTrimSuffix():
326 : // Trims the provided string in-place, at the last
327 : // occurrence of the provided suffix character.
328 : // Returns the number of characters trimmed.
329 : // E.g., strTrimSuffix ("file.txt", '.') would leave the
330 : // string as "file" and return 4.
331 : uint
332 0 : strTrimSuffix (char * target, char suffixChar)
333 : {
334 0 : uint trimCount = 0;
335 0 : char * lastSuffixPtr = NULL;
336 0 : char * s = target;
337 0 : while (*s) {
338 0 : if (*s == suffixChar) {
339 0 : lastSuffixPtr = s;
340 0 : trimCount = 0;
341 : }
342 0 : trimCount++;
343 0 : s++;
344 : }
345 0 : if (lastSuffixPtr == NULL) { return 0; }
346 0 : *lastSuffixPtr = 0;
347 0 : return trimCount;
348 : }
349 :
350 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
351 : // strTrimDate():
352 : // Takes a date string ("xxxx.xx.xx" format) and trims
353 : // the day part if it is ".??", and also the month part
354 : // if it too is ".??".
355 : void
356 0 : strTrimDate (char * str)
357 : {
358 0 : if (str[7] == '.' && str[8] == '?' && str[9] == '?') {
359 0 : str[7] = 0;
360 0 : if (str[4] == '.' && str[5] == '?' && str[6] == '?') {
361 0 : str[4] = 0;
362 : }
363 : }
364 0 : }
365 :
366 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
367 : // strTrimMarkCodes():
368 : // Trims in-place all Scid-recognised board mark codes
369 : // in a comment string, such as "[%mark ...]" and "[%arrow ...]"
370 : void
371 0 : strTrimMarkCodes (char * str)
372 : {
373 0 : char * in = str;
374 0 : char * out = str;
375 0 : bool inCode = false;
376 0 : char * startLocation = NULL;
377 :
378 : while (1) {
379 0 : char ch = *in;
380 0 : if (inCode) {
381 : // If we see end-of-string or code-starting '[', there is some
382 : // error so go back to the start of this code and treat it
383 : // normally.
384 0 : if (ch == 0 || ch == '[') {
385 0 : *out++ = *startLocation;
386 0 : inCode = false;
387 0 : in = startLocation;
388 0 : } else if (ch == ']') {
389 : // See a code-ending ']', so end the code.
390 0 : inCode = false;
391 : }
392 : // For all other characters in a code, just ignore it.
393 : } else {
394 : // Stop at end-of-string:
395 0 : if (ch == 0) { break; }
396 : // Look for the start of a code that is to be stripped:
397 0 : if (ch == '[' && in[1] == '%') {
398 0 : inCode = true;
399 0 : startLocation = in;
400 : } else {
401 0 : *out++ = ch;
402 : }
403 : }
404 0 : in++;
405 0 : }
406 : // Terminate the modified string:
407 0 : *out = 0;
408 0 : }
409 :
410 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
411 : // strTrimMarkup():
412 : // Trims in-place all HTML-like markup codes (<b>, </i>, etc)
413 : // from the provided string.
414 : void
415 0 : strTrimMarkup (char * str)
416 : {
417 0 : char * in = str;
418 0 : char * out = str;
419 0 : bool inTag = false;
420 :
421 0 : while (*in != 0) {
422 0 : char ch = *in;
423 0 : if (inTag) {
424 0 : if (ch == '>') { inTag = false; }
425 : } else {
426 0 : if (ch == '<') { inTag = true; } else { *out++ = ch; }
427 : }
428 0 : in++;
429 : }
430 0 : *out = 0;
431 0 : }
432 :
433 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
434 : // strFirstWord:
435 : // Skips over all whitespace at the start of the
436 : // string to reach the first word.
437 : const char *
438 0 : strFirstWord (const char * str)
439 : {
440 0 : ASSERT (str != NULL);
441 0 : while (*str != 0 && isspace(static_cast<unsigned char>(*str))) { str++; }
442 0 : return str;
443 : }
444 :
445 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
446 : // strNextWord:
447 : // Skips over all successive non-whitespace characters
448 : // in the string, then all successive whitespace chars,
449 : // to reach the next word in the string.
450 : const char *
451 0 : strNextWord (const char * str)
452 : {
453 0 : ASSERT (str != NULL);
454 0 : while (*str != 0 && !isspace(static_cast<unsigned char>(*str))) { str++; }
455 0 : while (*str != 0 && isspace(static_cast<unsigned char>(*str))) { str++; }
456 0 : return str;
457 : }
458 :
459 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
460 : // strIsUnknownName():
461 : // Returns true if the string is an "unknown" name: the empty
462 : // string, "?" or "-". Used primarily to test if an event, site
463 : // or round name string contains information worth printing.
464 : bool
465 0 : strIsUnknownName (const char * str)
466 : {
467 0 : if (str[0] == 0) { return true; }
468 0 : if (str[0] == '-' && str[1] == 0) { return true; }
469 0 : if (str[0] == '?' && str[1] == 0) { return true; }
470 0 : return false;
471 : }
472 :
473 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
474 : // strIsSurnameOnly():
475 : // Returns true if the name appears to be a surname only.
476 : bool
477 0 : strIsSurnameOnly (const char * name)
478 : {
479 0 : uint capcount = 0;
480 0 : const char * s = name;
481 0 : while (*s != 0) {
482 0 : unsigned char c = *s;
483 0 : if (! isalpha(c)) { return false; }
484 0 : if (isupper(c)) {
485 0 : capcount++;
486 0 : if (capcount > 1) { return false; }
487 : }
488 0 : s++;
489 : }
490 0 : return true;
491 : }
492 :
493 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
494 : // strGetBoolean():
495 : // Extracts a boolean value from a string.
496 : // True strings start with one of "TtYy1", false strings with
497 : // one of "FfNn0".
498 : // Returns false if the string does not contain a boolean value.
499 : bool
500 0 : strGetBoolean (const char * str)
501 : {
502 : static const char * sTrue[] = {
503 : "true", "yes", "on", "1", "ja", "si", "oui", NULL
504 : };
505 : static const char * sFalse[] = {
506 : "false", "no", "off", "0", NULL
507 : };
508 0 : if (str[0] == 0) { return false; }
509 :
510 0 : bool matchedTrue = false;
511 0 : bool matchedFalse = false;
512 :
513 0 : const char ** next = sTrue;
514 0 : while (*next != NULL) {
515 0 : if (strIsCasePrefix (str, *next) || strIsCasePrefix (*next, str)) {
516 0 : matchedTrue = true;
517 : }
518 0 : next++;
519 : }
520 0 : next = sFalse;
521 0 : while (*next != NULL) {
522 0 : if (strIsCasePrefix (str, *next) || strIsCasePrefix (*next, str)) {
523 0 : matchedFalse = true;
524 : }
525 0 : next++;
526 : }
527 0 : if (matchedTrue && !matchedFalse) { return true; }
528 0 : if (matchedFalse && !matchedTrue) { return false; }
529 :
530 : // default: return false.
531 0 : return false;
532 : }
533 :
534 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
535 : // strGetIntegers:
536 : // Extracts the specified number of signed integers in a
537 : // whitespace-separated string to an array.
538 : void
539 0 : strGetIntegers (const char * str, int * results, uint nResults)
540 : {
541 0 : for (uint i=0; i < nResults; i++) {
542 0 : while (*str != 0 && isspace(static_cast<unsigned char>(*str))) { str++; }
543 0 : results[i] = strGetInteger (str);
544 0 : while (*str != 0 && !isspace(static_cast<unsigned char>(*str))) { str++; }
545 : }
546 0 : }
547 :
548 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
549 : // strGetUnsigneds:
550 : // Extracts the specified number of unsigned integers in a
551 : // whitespace-separated string to an array.
552 : void
553 0 : strGetUnsigneds (const char * str, uint * results, uint nResults)
554 : {
555 0 : for (uint i=0; i < nResults; i++) {
556 0 : while (*str != 0 && isspace(static_cast<unsigned char>(*str))) { str++; }
557 0 : results[i] = strGetUnsigned (str);
558 0 : while (*str != 0 && !isspace(static_cast<unsigned char>(*str))) { str++; }
559 : }
560 0 : }
561 :
562 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
563 : // strGetResult:
564 : // Extracts a game result value from a string.
565 : resultT
566 0 : strGetResult (const char * str)
567 : {
568 0 : switch (*str) {
569 0 : case '1':
570 : // Check for "1/2"-style draw result:
571 0 : if (str[1] == '/' && str[2] == '2') {
572 0 : return RESULT_Draw;
573 : }
574 0 : return RESULT_White;
575 0 : case '=': return RESULT_Draw;
576 0 : case '0': return RESULT_Black;
577 0 : case '*': return RESULT_None;
578 : }
579 0 : return RESULT_None;
580 : }
581 :
582 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
583 : // strGetFlag():
584 : // Extracts a flag (FLAG_YES, FLAG_NO or FLAG_BOTH) value from
585 : // a string. Defaults to FLAG_EMPTY.
586 : flagT
587 0 : strGetFlag (const char * str)
588 : {
589 0 : char c = *str;
590 0 : switch (c) {
591 0 : case 'T': case 't':
592 : case 'Y': case 'y':
593 : case 'J': case 'j':
594 : case 'O': case 'o':
595 : case 'S': case 's':
596 : case '1':
597 0 : return FLAG_YES;
598 0 : case 'F': case 'f':
599 : case 'N': case 'n':
600 : case '0':
601 0 : return FLAG_NO;
602 0 : case 'B': case 'b':
603 : case '2':
604 0 : return FLAG_BOTH;
605 : }
606 : // default: return empty.
607 0 : return FLAG_EMPTY;
608 : }
609 :
610 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
611 : // strGetSquare():
612 : // Extracts a square value from a string, such as "a2".
613 : squareT
614 0 : strGetSquare (const char * str)
615 : {
616 0 : char chFyle = str[0];
617 0 : if (chFyle < 'a' || chFyle > 'h') { return NULL_SQUARE; }
618 0 : char chRank = str[1];
619 0 : if (chRank < '1' || chRank > '8') { return NULL_SQUARE; }
620 0 : return square_Make (fyle_FromChar(chFyle), rank_FromChar(chRank));
621 : }
622 :
623 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
624 : // strUniqueExactMatch():
625 : // Given a string <keyStr> and a null-terminated array of strings
626 : // <strTable>, returns the index of the unique match of the key
627 : // string in the string table. If no match was found, or there was
628 : // more than one match, -1 is returned.
629 : //
630 : // If the flag <exact> is true, only complete matches are considered.
631 : // Otherwise, unique abbreviations are accepted.
632 : // Example: looking up "repl" in {"repeat", "replace", NULL} would
633 : // return 1 (matching "replace") but looking up "rep" would
634 : // return -1 because its match is ambiguous.
635 : //
636 : // The array "strTable" does NOT need to be in any order, but the last
637 : // entry must be NULL.
638 : int
639 0 : strUniqueExactMatch (const char * keyStr, const char ** strTable, bool exact)
640 : {
641 0 : int index = -1;
642 0 : int abbrevMatches = 0;
643 : const char * s1;
644 : const char * s2;
645 0 : const char ** entryPtr = strTable;
646 :
647 : // If keyStr or strTable are null, return no match:
648 0 : if (keyStr == NULL || strTable == NULL) { return -1; }
649 :
650 : // Check each entry in turn:
651 0 : for (int i=0; *entryPtr != NULL; entryPtr++, i++) {
652 : // Check the key against this entry, character by character:
653 0 : for (s1 = keyStr, s2 = *entryPtr; *s1 == *s2; s1++, s2++) {
654 : // If *s1 is 0, we found an EXACT match, so return it now:
655 0 : if (*s1 == 0) {
656 0 : return i;
657 : }
658 : }
659 : // If *s1 == 0 now, key is an abbreviation of this entry:
660 0 : if (*s1 == 0) {
661 0 : index = i;
662 0 : abbrevMatches++;
663 : }
664 : }
665 :
666 : // If we reach here, there is no exact match. If an exact match was
667 : // required, or there is not exactly one abbreviation, return no match:
668 0 : if (exact || abbrevMatches != 1) {
669 0 : return -1;
670 : }
671 : // Otherwise, return the match found:
672 0 : return index;
673 : }
674 :
675 : //////////////////////////////////////////////////////////////////////
676 : // EOF: misc.cpp
677 : //////////////////////////////////////////////////////////////////////
678 :
|