LCOV - code coverage report
Current view: top level - src - misc.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 36 281 12.8 %
Date: 2019-01-29 11:06:41 Functions: 2 28 7.1 %

          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             : 

Generated by: LCOV version 1.13