123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
/*******************************************************************************

        copyright:      Copyright (c) 2006 Kris Bell. All rights reserved

        license:        BSD style: $(LICENSE)

        version:        Dec 2006: Initial release

        author:         Kris

        Placeholder for a selection of ASCII utilities. These generally will
        not work with utf8, and cannot be easily extended to utf16 or utf32
        
*******************************************************************************/

module tango.text.Ascii;

version (Win32)
        {
        private extern (C) int memicmp (in char *, in char *, uint);
        private extern (C) int memcmp (in char *, in char *, uint);
        }

version (Posix)
        {
        private extern (C) int memcmp (in char *, in char *, uint);
        private extern (C) int strncasecmp (in char *, in char*, uint);
        private alias strncasecmp memicmp;
        }

/******************************************************************************

        Convert to lowercase in-place.

******************************************************************************/

char[] toLower (char[] src)
{
        foreach (ref c; src)
                 if (c>= 'A' && c <= 'Z')
                     c = cast(char)(c + 32);
        return src;
}

/******************************************************************************

        Convert to lowercase. Returns the converted content in dst.

******************************************************************************/

char[] toLower (const(char[]) src, char[] dst)
{
        assert (dst.length >= src.length);
        dst[0 .. src.length] = src [0 .. $];

        return toLower(dst [0  .. src.length]);
}

/******************************************************************************

        Convert to uppercase in-place.

******************************************************************************/

char[] toUpper (char[] src)
{
        foreach (ref c; src)
                 if (c>= 'a' && c <= 'z')
                     c = cast(char)(c - 32);
        return src;
}

/******************************************************************************

        Convert to uppercase. Returns the converted content in dst.

******************************************************************************/

char[] toUpper (const(char[]) src, char[] dst)
{
        assert (dst.length >= src.length);
        dst[0 .. src.length] = src [0 .. $];

        return toUpper(dst [0  .. src.length]);
}

/******************************************************************************

        Compare two char[] ignoring case. Returns 0 if equal
        
******************************************************************************/

int icompare (const(char[]) s1, const(char[]) s2)
{
        auto len = s1.length;
        if (s2.length < len)
            len = s2.length;

        auto result = memicmp (s1.ptr, s2.ptr, cast(int)len);

        if (result is 0)
            result = cast(int)s1.length - cast(int)s2.length;
        return result;
}


/******************************************************************************

        Compare two char[] with case. Returns 0 if equal
        
******************************************************************************/

int compare (const(char[]) s1, const(char[]) s2)
{
        auto len = s1.length;
        if (s2.length < len)
            len = s2.length;

        auto result = memcmp (s1.ptr, s2.ptr, cast(int)len);

        if (result is 0)
            result = cast(int)s1.length - cast(int)s2.length;
        return result;
}



/******************************************************************************

        Return the index position of a text pattern within src, or
        src.length upon failure.

        This is a case-insensitive search (with thanks to Nietsnie)
        
******************************************************************************/

size_t isearch (in char[] src, in char[] pattern)
{
        enum  char[] _caseMap = 
                [ 
                '\000','\001','\002','\003','\004','\005','\006','\007',
                '\010','\011','\012','\013','\014','\015','\016','\017',
                '\020','\021','\022','\023','\024','\025','\026','\027',
                '\030','\031','\032','\033','\034','\035','\036','\037',
                '\040','\041','\042','\043','\044','\045','\046','\047',
                '\050','\051','\052','\053','\054','\055','\056','\057',
                '\060','\061','\062','\063','\064','\065','\066','\067',
                '\070','\071','\072','\073','\074','\075','\076','\077',
                '\100','\141','\142','\143','\144','\145','\146','\147',
                '\150','\151','\152','\153','\154','\155','\156','\157',
                '\160','\161','\162','\163','\164','\165','\166','\167',
                '\170','\171','\172','\133','\134','\135','\136','\137',
                '\140','\141','\142','\143','\144','\145','\146','\147',
                '\150','\151','\152','\153','\154','\155','\156','\157',
                '\160','\161','\162','\163','\164','\165','\166','\167',
                '\170','\171','\172','\173','\174','\175','\176','\177',
                '\200','\201','\202','\203','\204','\205','\206','\207',
                '\210','\211','\212','\213','\214','\215','\216','\217',
                '\220','\221','\222','\223','\224','\225','\226','\227',
                '\230','\231','\232','\233','\234','\235','\236','\237',
                '\240','\241','\242','\243','\244','\245','\246','\247',
                '\250','\251','\252','\253','\254','\255','\256','\257',
                '\260','\261','\262','\263','\264','\265','\266','\267',
                '\270','\271','\272','\273','\274','\275','\276','\277',
                '\300','\341','\342','\343','\344','\345','\346','\347',
                '\350','\351','\352','\353','\354','\355','\356','\357',
                '\360','\361','\362','\363','\364','\365','\366','\367',
                '\370','\371','\372','\333','\334','\335','\336','\337',
                '\340','\341','\342','\343','\344','\345','\346','\347',
                '\350','\351','\352','\353','\354','\355','\356','\357',
                '\360','\361','\362','\363','\364','\365','\366','\367',
                '\370','\371','\372','\373','\374','\375','\376','\377',
                ];  


        assert(src.ptr);
        assert(pattern.ptr);

        for (int i1=0, i2; i1 <= cast(int)(src.length - pattern.length); ++i1)
            {   
            for (i2=0; i2 < pattern.length; ++i2)
                 if (_caseMap[src[i1 + i2]] != _caseMap[pattern[i2]])
                     break;

            if (i2 is pattern.length)
                return i1;
            }   
        return src.length;
}



/******************************************************************************

******************************************************************************/

debug (UnitTest)
{       
        unittest
        {
        char[20] tmp;
        
        assert (toLower("1bac", tmp) == "1bac");
        assert (toLower("1BAC", tmp) == "1bac");
        assert (toUpper("1bac", tmp) == "1BAC");
        assert (toUpper("1BAC", tmp) == "1BAC");
        assert (icompare ("ABC", "abc") is 0);
        assert (icompare ("abc", "abc") is 0);
        assert (icompare ("abcd", "abc") > 0);
        assert (icompare ("abc", "abcd") < 0);
        assert (icompare ("ACC", "abc") > 0);

        assert (isearch ("ACC", "abc") is 3);
        assert (isearch ("ACC", "acc") is 0);
        assert (isearch ("aACC", "acc") is 1);
        }
}

debug (Ascii)
{
        void main() {}
}