summaryrefslogblamecommitdiffstats
path: root/crawl-ref/source/libutil.cc
blob: 6823a4c932b6ad289b3b7ae49e312c2204bf090d (plain) (tree)
1
2
3
4
5
6
7
8
9


                                                               


                   
 
                    
                                   

                    
                  
                  

                     
                  

                  
                   
                   
 
                        
                  
                        
              







                                      
                                                            











                                               



                                                                 
 

















                                    

                                   



                                   



                      















                                                                            

                                                                          
                      

                                                                          









                                



                                                           
                                                
                                       







                                                      
 

                                 
                   
                                                                          
                            






                                                                    

                                                             
                                                               

 
                                                      
 
                   

                             

                                                            

                          
 
                                        
                             

                                           

                          
 
                 

 








                                             












                                                       
                                

                                             

                                 


        


                 


                                                                     

               
             

 
                                      
 
                                                    
                             
 
               

 






                                       
                                      
 
                                                    
                             
 






                                           

 



                                                           
 
                                     

                                      
 


               










                                                                             
 

                      
               










                     




















                                                                         
 







                                                             


                                                          










                                                                          
                                                   
 
                                                                   
     
                                         
     
 





                                                 
 







                                                                          











                                                                         






                                                                  


                                                              

                       
 
                                             




                                                         
                        
                                          
                                







                                          
                             

  
                                                                     
                                                     


                                                 


                               


                                           
                                                                     




                                                                         


                                                         
     
                                                                   
     
 
                              
     

                                                       
     

                                                                  
     

                                                                  
     
                                   
     

                                                                       
     
                                        
     
                                                        
     
                                  
     

                          
                                       







                                                             
     
                                   
     

                                                         
     
                                      
     
                          

                                                         
                                                            
     
                                                           
                                                         
     
                                     
     

                                                       
     
                                                                 
                                                                    
     

                                                                 
     
                                                           



                                                                                
                           
     
                                           
     

                                                       
     
                                       
     

                                                       
     

                            



                      

































                                                                           
                                 
                                                                



                                                                           

                                 
 
                               















                                                                       
                            


                                                                    


                                                                

 





                                                
 








                                                              

                                                                       
                                         

                                                          


                                     
 

                                                                         
                                           



















                                                                    





                                           
                        
                                            
 

                                                       



                 





                                                       



                                                        







                                   




                                                                  

                                      
                                        

                               
                                                               
     

                                                          
 
                                           
 

                        
     



                                                                       


                    
                                                                   










                                                                        
                                      





                                        
                

                                             
                                             
 

                             

                   
                   
     




                                                                             
                   

                                        


                                                                         

                                        







                                                                         




                              
      


                                                                       

                                       









                                                                            
                




                                        


                            






                                                  
         
                                                                   
                                                                  
                              
 




                                                                           
                           


     








                                                                    
                                                           























                                                                             



                                                                    

                                                      


                                         
                                





                                   

                                    







                                                                        


                                                                         







                                                                    

                                                      








                                         

           





                    



                                    













                                                                        
 
                                                      
 
                                                

 
                                    
 
                                   



                                                                        
                                                              
 
 
      

                                                              
/*
 *  File:       libutil.cc
 *  Summary:    Functions that may be missing from some systems
 */

#include "AppHdr.h"

#include "defines.h"
#include "itemname.h" // is_vowel()
#include "libutil.h"
#include "externs.h"
#include "macro.h"
#include "stuff.h"
#include "viewgeom.h"

#include <sstream>
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>

#ifdef TARGET_OS_WINDOWS
    #undef ARRAYSZ
    #include <windows.h>
    #undef max

    #ifdef WINMM_PLAY_SOUNDS
        #include <mmsystem.h>
    #endif
#endif

#ifdef REGEX_PCRE
    // Statically link pcre on Windows
    #if defined(TARGET_OS_WINDOWS) || defined(TARGET_OS_DOS)
        #define PCRE_STATIC
    #endif

    #include <pcre.h>
#endif

#ifdef REGEX_POSIX
    // Do we still need to include sys/types.h?
    #include <sys/types.h>
    #include <regex.h>
#endif

description_level_type description_type_by_name(const char *desc)
{
    if (!desc)
        return DESC_PLAIN;

    if (!strcmp("The", desc))
        return DESC_CAP_THE;
    else if (!strcmp("the", desc))
        return DESC_NOCAP_THE;
    else if (!strcmp("A", desc))
        return DESC_CAP_A;
    else if (!strcmp("a", desc))
        return DESC_NOCAP_A;
    else if (!strcmp("Your", desc))
        return DESC_CAP_YOUR;
    else if (!strcmp("your", desc))
        return DESC_NOCAP_YOUR;
    else if (!strcmp("its", desc))
        return DESC_NOCAP_ITS;
    else if (!strcmp("worn", desc))
        return DESC_INVENTORY_EQUIP;
    else if (!strcmp("inv", desc))
        return DESC_INVENTORY;
    else if (!strcmp("none", desc))
        return DESC_NONE;
    else if (!strcmp("base", desc))
        return DESC_BASENAME;
    else if (!strcmp("qual", desc))
        return DESC_QUALNAME;

    return DESC_PLAIN;
}

std::string number_to_string(unsigned number, bool in_words)
{
    return (in_words? number_in_words(number) : make_stringf("%u", number));
}

std::string apply_description(description_level_type desc,
                              const std::string &name,
                              int quantity, bool in_words)
{
    switch (desc)
    {
    case DESC_CAP_THE:
        return ("The " + name);
    case DESC_NOCAP_THE:
        return ("the " + name);
    case DESC_CAP_A:
        return (quantity > 1 ? number_to_string(quantity, in_words) + name
                             : article_a(name, false));
    case DESC_NOCAP_A:
        return (quantity > 1 ? number_to_string(quantity, in_words) + name
                             : article_a(name, true));
    case DESC_CAP_YOUR:
        return ("Your " + name);
    case DESC_NOCAP_YOUR:
        return ("your " + name);
    case DESC_PLAIN:
    default:
        return (name);
    }
}

// Should return true if the filename contains nothing that
// the shell can do damage with.
bool shell_safe(const char *file)
{
    int match = strcspn(file, "\\`$*?|><&\n!;");
    return (match < 0 || !file[match]);
}

void play_sound( const char *file )
{
#if defined(WINMM_PLAY_SOUNDS)
    // Check whether file exists, is readable, etc.?
    if (file && *file)
        sndPlaySound(file, SND_ASYNC | SND_NODEFAULT);

#elif defined(SOUND_PLAY_COMMAND)
    char command[255];
    command[0] = 0;
    if (file && *file && (strlen(file) + strlen(SOUND_PLAY_COMMAND) < 255)
        && shell_safe(file))
    {
        snprintf(command, sizeof command, SOUND_PLAY_COMMAND, file);
        system(command);
    }
#endif
}

std::string strip_filename_unsafe_chars(const std::string &s)
{
    return replace_all_of(s, " .&`\"\'|;{}()[]<>*%$#@!~?", "");
}

std::string vmake_stringf(const char* s, va_list args)
{
    char buf1[400];
    va_list orig_args;
    va_copy(orig_args, args);
    size_t len = vsnprintf(buf1, sizeof buf1, s, orig_args);
    va_end(orig_args);
    if (len < sizeof buf1)
        return (buf1);

    char *buf2 = (char*)malloc(len + 1);
    va_copy(orig_args, args);
    vsnprintf(buf2, len + 1, s, orig_args);
    va_end(orig_args);
    std::string ret(buf2);
    free(buf2);

    return (ret);
}

std::string make_stringf(const char *s, ...)
{
    va_list args;
    va_start(args, s);
    std::string ret = vmake_stringf(s, args);
    va_end(args);
    return ret;
}

std::string &escape_path_spaces(std::string &s)
{
    std::string result;
    result.clear();
#ifdef UNIX
    for (const char* ch = s.c_str(); *ch != '\0'; ++ch)
    {
        if (*ch == ' ')
        {
            result += '\\';
        }
        result += *ch;
    }
#elif defined(TARGET_OS_WINDOWS)
    if (s.find(" ") != std::string::npos
        && s.find("\"") == std::string::npos)
    {
        result = "\"" + s + "\"";
    }
    else
    {
        return s;
    }
#else
    // Not implemented for this platform.  Assume that escaping isn't
    // necessary.
    return s;
#endif
    s = result;
    return s;
}

std::string &uppercase(std::string &s)
{
    for (unsigned i = 0, sz = s.size(); i < sz; ++i)
        s[i] = toupper(s[i]);

    return (s);
}

std::string upcase_first(std::string s)
{
    if (!s.empty())
        s[0] = toupper(s[0]);
    return (s);
}

std::string &lowercase(std::string &s)
{
    for (unsigned i = 0, sz = s.size(); i < sz; ++i)
        s[i] = tolower(s[i]);

    return (s);
}

std::string lowercase_string(std::string s)
{
    lowercase(s);
    return (s);
}

int ends_with(const std::string &s, const char *suffixes[])
{
    if (!suffixes)
        return (0);

    for (int i = 0; suffixes[i]; ++i)
        if (ends_with(s, suffixes[i]))
            return (1 + i);

    return (0);
}

#ifdef UNIX
extern "C" int stricmp(const char *str1, const char *str2)
{
    int ret = 0;

    // No need to check for *str1.  If str1 ends, then tolower(*str1) will be
    // 0, ret will be -1, and the loop will break.
    while (!ret && *str2)
    {
        unsigned char c1 = tolower(*str1);
        unsigned char c2 = tolower(*str2);

        ret = c1 - c2;
        str1++;
        str2++;
    }

    if (ret < 0)
        ret = -1;
    else if (ret > 0)
        ret = 1;

    return (ret);
}
#endif

// Returns true if s contains tag 'tag', and strips out tag from s.
bool strip_tag(std::string &s, const std::string &tag, bool skip_padding)
{
    if (s == tag)
    {
        s.clear();
        return (true);
    }

    std::string::size_type pos;

    if (skip_padding)
    {
        if ((pos = s.find(tag)) != std::string::npos)
        {
            s.erase(pos, tag.length());
            trim_string(s);
            return (true);
        }
        return (false);
    }

    if ((pos = s.find(" " + tag + " ")) != std::string::npos)
    {
        // Leave one space intact.
        s.erase(pos, tag.length() + 1);
        trim_string(s);
        return (true);
    }

    if ((pos = s.find(tag + " ")) == 0
        || ((pos = s.find(" " + tag)) != std::string::npos
            && pos + tag.length() + 1 == s.length()))
    {
        s.erase(pos, tag.length() + 1);
        trim_string(s);
        return (true);
    }

    return (false);
}

std::string strip_tag_prefix(std::string &s, const std::string &tagprefix)
{
    std::string::size_type pos = s.find(tagprefix);

    while (pos && pos != std::string::npos && !isspace(s[pos - 1]))
    {
        pos = s.find(tagprefix, pos + 1);
    }

    if (pos == std::string::npos)
        return ("");

    std::string::size_type ns = s.find(" ", pos);
    if (ns == std::string::npos)
        ns = s.length();

    const std::string argument =
        s.substr(pos + tagprefix.length(), ns - pos - tagprefix.length());

    s.erase(pos, ns - pos + 1);
    trim_string(s);

    return (argument);
}

// Get a boolean flag from embedded tags in a string, using "<flag>"
// for true and "no_<flag>" for false. If neither tag is found,
// returns the default value.
bool strip_bool_tag(std::string &s, const std::string &name, bool defval)
{
    if (strip_tag(s, name))
        return (true);
    if (strip_tag(s, "no_" + name))
        return (false);
    return (defval);
}

int strip_number_tag(std::string &s, const std::string &tagprefix)
{
    const std::string num = strip_tag_prefix(s, tagprefix);
    return (num.empty()? TAG_UNFOUND : atoi(num.c_str()));
}

// Naively prefix A/an to a noun.
std::string article_a(const std::string &name, bool lowercase)
{
    if (!name.length())
        return name;

    const char *a  = lowercase? "a "  : "A ";
    const char *an = lowercase? "an " : "An ";
    switch (name[0])
    {
        case 'a': case 'e': case 'i': case 'o': case 'u':
        case 'A': case 'E': case 'I': case 'O': case 'U':
            // XXX: Hack
            if (starts_with(name, "one-"))
                return a + name;
            return an + name;
        default:
            return a + name;
    }
}

const char *standard_plural_qualifiers[] =
{
    " of ", " labeled ", NULL
};

// Pluralises a monster or item name.  This'll need to be updated for
// correctness whenever new monsters/items are added.
std::string pluralise(const std::string &name,
                      const char *qualifiers[],
                      const char *no_qualifier[])
{
    std::string::size_type pos;

    if (qualifiers)
    {
        for (int i = 0; qualifiers[i]; ++i)
            if ((pos = name.find(qualifiers[i])) != std::string::npos
                && !ends_with(name, no_qualifier))
            {
                return pluralise(name.substr(0, pos)) + name.substr(pos);
            }
    }

    if (!name.empty() && name[name.length() - 1] == ')'
        && (pos = name.rfind(" (")) != std::string::npos)
    {
        return (pluralise(name.substr(0, pos)) + name.substr(pos));
    }

    if (ends_with(name, "us"))
    {
        // Fungus, ufetubus, for instance.
        return name.substr(0, name.length() - 2) + "i";
    }
    else if (ends_with(name, "larva") || ends_with(name, "amoeba")
          || ends_with(name, "antenna"))
    {
        // Giant amoebae sounds a little weird, to tell the truth.
        return name + "e";
    }
    else if (ends_with(name, "ex"))
    {
        // Vortex; vortexes is legal, but the classic plural is cooler.
        return name.substr(0, name.length() - 2) + "ices";
    }
    else if (ends_with(name, "cyclops"))
    {
        return name.substr(0, name.length() - 1) + "es";
    }
    else if (ends_with(name, "y"))
    {
        if (name == "y")
            return ("ys");
        // sensibility -> sensibilities
        else if (name[name.length() - 2] == 'i')
            return name.substr(0, name.length() - 1) + "es";
        // day -> days, boy -> boys, etc
        else if (is_vowel(name[name.length() - 2]))
            return name + "s";
        // jelly -> jellies
        else
            return name.substr(0, name.length() - 1) + "ies";
    }
    else if (ends_with(name, "fe"))
    {
        // knife -> knives
        return name.substr(0, name.length() - 2) + "ves";
    }
    else if (ends_with(name, "staff"))
    {
        // staff -> staves
        return name.substr(0, name.length() - 2) + "ves";
    }
    else if (ends_with(name, "f") && !ends_with(name, "ff"))
    {
        // elf -> elves, but not hippogriff -> hippogrives.
        return name.substr(0, name.length() - 1) + "ves";
    }
    else if (ends_with(name, "mage"))
    {
        // mage -> magi
        return name.substr(0, name.length() - 1) + "i";
    }
    else if (ends_with(name, "sheep") || ends_with(name, "manes")
              || ends_with(name, "fish") || ends_with(name, "folk"))
    {
        // Maybe we should generalise 'manes' to ends_with("es")?
        return name;
    }
    else if (ends_with(name, "ch") || ends_with(name, "sh")
             || ends_with(name, "x"))
    {
        // To handle cockroaches and sphinxes, and in case there's some monster
        // ending with sh (except fish, which are caught in the previous check).
        return name + "es";
    }
    else if (ends_with(name, "simulacrum"))
    {
        // simulacrum -> simulacra
        return name.substr(0, name.length() - 2) + "a";
    }
    else if (ends_with(name, "efreet"))
    {
        // efreet -> efreeti. Not sure this is correct.
        return name + "i";
    }
    else if (name == "foot")
        return "feet";

    return name + "s";
}

static std::string pow_in_words(int pow)
{
    switch (pow)
    {
    case 0:
        return "";
    case 3:
        return " thousand";
    case 6:
        return " million";
    case 9:
        return " billion";
    case 12:
    default:
        return " trillion";
    }
}

static std::string tens_in_words(unsigned num)
{
    static const char *numbers[] = {
        "", "one", "two", "three", "four", "five", "six", "seven",
        "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
        "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
    };
    static const char *tens[] = {
        "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
        "eighty", "ninety"
    };

    if (num < 20)
        return numbers[num];

    int ten = num / 10, digit = num % 10;
    return std::string(tens[ten])
             + (digit ? std::string("-") + numbers[digit] : "");
}

static std::string join_strings(const std::string &a, const std::string &b)
{
    if (!a.empty() && !b.empty())
        return (a + " " + b);

    return (a.empty() ? b : a);
}

static std::string hundreds_in_words(unsigned num)
{
    unsigned dreds = num / 100, tens = num % 100;
    std::string sdreds = dreds? tens_in_words(dreds) + " hundred" : "";
    std::string stens  = tens? tens_in_words(tens) : "";
    return join_strings(sdreds, stens);
}

std::string number_in_words(unsigned num, int pow)
{
    if (pow == 12)
        return number_in_words(num, 0) + pow_in_words(pow);

    unsigned thousands = num % 1000, rest = num / 1000;
    if (!rest && !thousands)
        return ("zero");

    return join_strings((rest? number_in_words(rest, pow + 3) : ""),
                        (thousands? hundreds_in_words(thousands)
                                    + pow_in_words(pow)
                                  : ""));
}

std::string replace_all(std::string s,
                        const std::string &find,
                        const std::string &repl)
{
    std::string::size_type start = 0;
    std::string::size_type found;

    while ((found = s.find(find, start)) != std::string::npos)
    {
        s.replace( found, find.length(), repl );
        start = found + repl.length();
    }

    return (s);
}

// Replaces all occurrences of any of the characters in tofind with the
// replacement string.
std::string replace_all_of(std::string s,
                           const std::string &tofind,
                           const std::string &replacement)
{
    std::string::size_type start = 0;
    std::string::size_type found;

    while ((found = s.find_first_of(tofind, start)) != std::string::npos)
    {
        s.replace( found, 1, replacement );
        start = found + replacement.length();
    }

    return (s);
}

int count_occurrences(const std::string &text, const std::string &s)
{
    int nfound = 0;
    std::string::size_type pos = 0;

    while ((pos = text.find(s, pos)) != std::string::npos)
    {
        ++nfound;
        pos += s.length();
    }

    return (nfound);
}

std::string trimmed_string( std::string s )
{
    trim_string(s);
    return (s);
}

// also used with macros
std::string &trim_string( std::string &str )
{
    str.erase( 0, str.find_first_not_of( " \t\n\r" ) );
    str.erase( str.find_last_not_of( " \t\n\r" ) + 1 );

    return (str);
}

std::string &trim_string_right( std::string &str )
{
    str.erase( str.find_last_not_of( " \t\n\r" ) + 1 );
    return (str);
}

static void add_segment( std::vector<std::string> &segs,
                         std::string s,
                         bool trim,
                         bool accept_empty)
{
    if (trim && !s.empty())
        trim_string(s);

    if (accept_empty || !s.empty())
        segs.push_back(s);
}

std::vector<std::string> split_string( const std::string &sep,
                                       std::string s,
                                       bool trim_segments,
                                       bool accept_empty_segments,
                                       int nsplits)
{
    std::vector<std::string> segments;
    int separator_length = sep.length();

    std::string::size_type pos;
    while (nsplits && (pos = s.find(sep)) != std::string::npos)
    {
        add_segment(segments, s.substr(0, pos),
                    trim_segments, accept_empty_segments);

        s.erase(0, pos + separator_length);

        if (nsplits > 0)
            --nsplits;
    }

    if (!s.empty())
        add_segment(segments, s, trim_segments, accept_empty_segments);

    return segments;
}

// The old school way of doing short delays via low level I/O sync.
// Good for systems like old versions of Solaris that don't have usleep.
#ifdef NEED_USLEEP

#include <sys/time.h>
#include <sys/types.h>
#include <sys/unistd.h>

void usleep(unsigned long time)
{
    struct timeval timer;

    timer.tv_sec  = (time / 1000000L);
    timer.tv_usec = (time % 1000000L);

    select(0, NULL, NULL, NULL, &timer);
}
#endif

#ifndef USE_TILE
static GotoRegion _current_region = GOTO_CRT;

void cgotoxy(int x, int y, GotoRegion region)
{
    _current_region = region;

    ASSERT(x >= 1);
    ASSERT(y >= 1);
    switch (region)
    {
    case GOTO_MLIST:
        ASSERT(x <= crawl_view.mlistsz.x);
        ASSERT(y <= crawl_view.mlistsz.y);
        gotoxy_sys(x + crawl_view.mlistp.x - 1, y + crawl_view.mlistp.y - 1);
        break;
    case GOTO_STAT:
        ASSERT(x <= crawl_view.hudsz.x);
        ASSERT(y <= crawl_view.hudsz.y);
        gotoxy_sys(x + crawl_view.hudp.x - 1, y + crawl_view.hudp.y - 1);
        break;
    case GOTO_MSG:
        ASSERT(x <= crawl_view.msgsz.x);
        ASSERT(y <= crawl_view.msgsz.y);
        gotoxy_sys(x + crawl_view.msgp.x - 1, y + crawl_view.msgp.y - 1);
        break;
    case GOTO_CRT:
    default:
        gotoxy_sys(x, y);
        break;
    }
}

GotoRegion get_cursor_region()
{
    return (_current_region);
}
#endif
///////////////////////////////////////////////////////////////////////
// Pattern matching

inline int pm_lower(int ch, bool icase)
{
    return icase? tolower(ch) : ch;
}

// Determines whether the pattern specified by 'pattern' matches the given
// text. A pattern is a simple glob, with the traditional * and ? wildcards.
static bool glob_match( const char *pattern, const char *text, bool icase )
{
    char p, t;
    bool special;

    while (true)
    {
        p = pm_lower(*pattern++, icase);
        t = pm_lower(*text++, icase);
        special = true;

        if (!p)
            return (t == 0);

        if (p == '\\' && *pattern)
        {
            p       = pm_lower(*pattern++, icase);
            special = false;
        }

        if (p == '*' && special)
        {
            // Try to match exactly at the current text position...
            if (!*pattern || glob_match(pattern, text - 1, icase))
                return (true);

            // Or skip one character in the text and try the wildcard match
            // again. If this is the end of the text, the match has failed.
            return (t ? glob_match(pattern - 1, text, icase) : false);
        }
        else if (!t || p != t && (p != '?' || !special))
            return (false);
    }
}

////////////////////////////////////////////////////////////////////
// Basic glob (always available)

struct glob_info
{
    std::string s;
    bool ignore_case;
};

void *compile_glob_pattern(const char *pattern, bool icase)
{
    // If we're using simple globs, we need to box the pattern with '*'
    std::string s = std::string("*") + pattern + "*";
    glob_info *gi = new glob_info;
    if (gi)
    {
        gi->s = s;
        gi->ignore_case = icase;
    }
    return gi;
}

void free_compiled_glob_pattern(void *compiled_pattern)
{
    delete static_cast<glob_info *>( compiled_pattern );
}

bool glob_pattern_match(void *compiled_pattern, const char *text, int length)
{
    glob_info *gi = static_cast<glob_info *>( compiled_pattern );
    return glob_match(gi->s.c_str(), text, gi->ignore_case);
}
////////////////////////////////////////////////////////////////////

#if defined(REGEX_PCRE)
////////////////////////////////////////////////////////////////////
// Perl Compatible Regular Expressions

void *compile_pattern(const char *pattern, bool icase)
{
    const char *error;
    int erroffset;
    int flags = icase? PCRE_CASELESS : 0;
    return pcre_compile(pattern,
                        flags,
                        &error,
                        &erroffset,
                        NULL);
}

void free_compiled_pattern(void *cp)
{
    if (cp)
        pcre_free(cp);
}

bool pattern_match(void *compiled_pattern, const char *text, int length)
{
    int ovector[42];
    int pcre_rc = pcre_exec(static_cast<pcre *>(compiled_pattern),
                            NULL,
                            text, length, 0, 0,
                            ovector, sizeof(ovector) / sizeof(*ovector));
    return (pcre_rc >= 0);
}

////////////////////////////////////////////////////////////////////
#elif defined(REGEX_POSIX)
////////////////////////////////////////////////////////////////////
// POSIX regular expressions

void *compile_pattern(const char *pattern, bool icase)
{
    regex_t *re = new regex_t;
    if (!re)
        return NULL;

    int flags = REG_EXTENDED | REG_NOSUB;
    if (icase)
        flags |= REG_ICASE;
    int rc = regcomp(re, pattern, flags);
    // Nonzero return code == failure
    if (rc)
    {
        delete re;
        return NULL;
    }
    return re;
}

void free_compiled_pattern(void *cp)
{
    if (cp)
    {
        regex_t *re = static_cast<regex_t *>( cp );
        regfree(re);
        delete re;
    }
}

bool pattern_match(void *compiled_pattern, const char *text, int length)
{
    regex_t *re = static_cast<regex_t *>( compiled_pattern );
    return !regexec(re, text, 0, NULL, 0);
}

////////////////////////////////////////////////////////////////////
#else

void *compile_pattern(const char *pattern, bool icase)
{
    return compile_glob_pattern(pattern, icase);
}

void free_compiled_pattern(void *cp)
{
    free_compiled_glob_pattern(cp);
}

bool pattern_match(void *compiled_pattern, const char *text, int length)
{
    return glob_pattern_match(compiled_pattern, text, length);
}

#endif

mouse_mode mouse_control::ms_current_mode = MOUSE_MODE_NORMAL;