/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@vovida.org.
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */


static const char* const mstring_cxx_Version =
    "$Id: mstring.cxx,v 1.9 2001/08/10 04:02:11 icahoon Exp $";



#include "global.h"
#include <cstdio>
#include "mstring.hxx"
#include "vsock.hxx"


namespace std
{
//--------------------------------------------------------------------------
mstring & mstring::assigni(int val, const char *format)
{
    char buf[64];
    sprintf(buf, format, val);
    return *this = buf;
}


//--------------------------------------------------------------------------
mstring & mstring::assignd(double val, const char *format)
{
    char buf[64];
    sprintf(buf, format, val);
    return *this = buf;
}


//--------------------------------------------------------------------------
mstring & mstring::ltrims(const string &s)
{
    erase(0, find_first_not_of(s));
    return *this;
}

//--------------------------------------------------------------------------
mstring & mstring::rtrims(const string &s)
{
    size_type pos = find_last_not_of(s);
    if (pos == string::npos) pos = 0;
    else pos++;
    erase(pos);
    return *this;
}


//--------------------------------------------------------------------------
mstring & mstring::toleft(unsigned newlen, char fill, bool cut)
{
    unsigned lstr = length();
    if (lstr < newlen)
    {
        insert(lstr, newlen - lstr, fill);
    }
    if (cut && newlen < lstr)
    {
        erase(newlen, string::npos);
    }
    return *this;
}



//--------------------------------------------------------------------------
mstring & mstring::toright(unsigned newlen, char fill, bool cut)
{
    unsigned lstr = length();
    if (lstr < newlen)
    {
        insert(size_type(0), newlen - lstr, fill);
    }
    if (cut && newlen < lstr)
    {
        erase(size_type(0), lstr - newlen);
    }
    return *this;
}



//--------------------------------------------------------------------------
unsigned mstring::replace_all(const string &find_str, const string &repl_str)
{
    size_type pos = 0;
    size_type find_len = find_str.length();
    size_type repl_len = repl_str.length();
    unsigned nrepl = 0;

    while ((pos = find(find_str, pos)) != string::npos)
    {
        replace(pos, find_len, repl_str);
        pos += repl_len;
        nrepl++;
    }
    return nrepl;
}



//--------------------------------------------------------------------------
int mstring::compare(const string &str, size_type len) const
{
    if (len == string::npos)
    {
        return strcmp(c_str(), str.c_str());
    }
    return strncmp(c_str(), str.c_str(), len);
}




//--------------------------------------------------------------------------
int mstring::comparei(const string &str, size_type len) const
{
    if (len == string::npos)
    {
        return strcasecmp(c_str(), str.c_str());
    }
    return strncasecmp(c_str(), str.c_str(), len);
}




//--------------------------------------------------------------------------
mstring & mstring::tab2sp(unsigned tab_size)
{
    size_type pos = 0;

    while ((pos = find('\t')) != string::npos)
    {
        size_type n_sp = tab_size - pos % tab_size;
        replace(pos, 1, n_sp, ' ');
        pos += n_sp;
    }
    return *this;
}




//---------------------------------------------------------------------------------
mstring & mstring::ins_pair(const string &chrset, char pair_symb)
{
    string::size_type i;
    char prev = 0;

    for (i = 0; i < length(); ++i)
    {
        if ((chrset.find((*this)[i]) != string::npos) == (prev != pair_symb))
        {
            insert(i++, 1, pair_symb);
        }
        prev = (*this)[i];
    }
    if (i > 0 && (*this)[i - 1] == pair_symb) append(1, pair_symb);
    return *this;


}




//---------------------------------------------------------------------------------
mstring & mstring::del_pair(char pair_symb)
{
    string::size_type i;

    for (i = 0; i < length(); i++)
    {
        if ((*this)[i] == pair_symb)
        {
            erase(i, 1);
            if ((*this)[i] == 0) break;
        }
    }
    return *this;
}



//---------------------------------------------------------------------------------
mstring & mstring::ins_escaped(const string &chrset, char escaped_symb)
{
    ///!!! for further implementation
    return *this;
}

//---------------------------------------------------------------------------------
mstring & mstring::del_escaped(char escaped_symb)
{
    ///!!! for further implementation
    return *this;
}


//---------------------------------------------------------------------------------
mstring & mstring::quote(const string &q_start, const string &q_end)
{
    insert(0, q_start);
    append(q_end);
    return *this;
}



//---------------------------------------------------------------------------------
/**
Get the next token from string

@return:  the next position in string to be continue or 
          string::npos if no more tokens

@param start              Start position of the string - 0 or 
                          the previous returned value

@param sep                String with possible separators. Every symbol
                          of this string is separator

@param quote              String with quotation symbols, for example, "'"
                          Inside quote string is not divided. Quotation can
                          start with any of these symbols, but finish with
                          the same symbol only.

@param pair_chr           Usually '\' symbol to include quotation symbols
                          inside the quote parts

@param sep_flag           Flag sep_single or sep_multiple to determine, 
                          whether the separators, going successively are 
                          nerpreted as detached empty tokens (sep_single) 
                          or one token (sep_multiple), as strtok does.
                          sep_single:   string "123,,456" will be "123", "", "456"
                          sep_multiple: string "123,,456" will be "123", "456"
\\================================================================================*/
string::size_type mstring::next_token(size_type start, const string &sep,
                                      const string &quote, char pair_chr,
                                      sep_flag flag) const
{
    size_type count = 0;
    char quote_chr = 0;

    if (start == string::npos) return string::npos;

    const char *pstr = c_str() + start;

    if (*pstr == 0) return string::npos;


    if (flag == sep_multiple)
    {
        //Pass all the separators symbols at the begin of the string
        while (*pstr && sep.find(*pstr) != string::npos)
        {
            ++pstr;
            ++start;
        }
    }

    for (count = 0; ; count++)
    {
        char c = *pstr++;

        if (c == 0)
        {
            start = string::npos;
            break;
        }

        //Outside quote find one of separator symbols
        if (quote_chr == 0 && sep.find(c) != string::npos)
        {
            start += count;
            break;
        }

        //Switch quote. If it is not a quote yet, try to check any of
        //quote symbols. Otherwise quote must be finished with quote_symb
        if (quote_chr == 0)
        {
            if (quote.find(c) != string::npos)
            {
                quote_chr = c;
                continue;
            }
        }
        else
        {
            //Inside quote pass all the pair symbols
            if (pair_chr && c == pair_chr)
            {
                if (*pstr)
                {
                    ++count;
                    ++pstr;
                }
                continue;
            }
            if (c == quote_chr)
            {
                quote_chr = 0;
                continue;
            }
        }
    }
    return start;
}



//---------------------------------------------------------------------------------
/**
Get the next token from string

@return:  the next position in string to be continue or 
          string::npos if no more tokens


@param dst                Pointer to the destination string. 
                          If this parameter is null
                          the function may be used to counting 
                          tokens

@param start              Start position of the string - 0 or 
                          the previous returned value

@param sep                String with possible separators. Every symbol
                          of this string is separator

@param quote              String with quotation symbols, for example, "'"
                          Inside quote string is not divided. Quotation can
                          start with any of these symbols, but finish with
                          the same symbol only.

@param pair_chr           Usually '\' symbol to include quotation symbols
                          inside the quote parts

@param sep_flag           Flag sep_single or sep_multiple to determine, 
                          whether the separators, going successively are 
                          nerpreted as detached empty tokens (sep_single) 
                          or one token (sep_multiple), as strtok does.
                          sep_single:   string "123,,456" will be "123", "", "456"
                          sep_multiple: string "123,,456" will be "123", "456"
\\================================================================================*/
string::size_type mstring::token(string *dst, size_type start, const string &sep,
                                 const string &quote, char pair_chr,
                                 sep_flag flag) const
{
    size_type count = 0;
    char quote_chr = 0;

    if (dst) dst->erase();

    if (start == string::npos) return string::npos;

    const char *pstr = c_str() + start;
    if (*pstr == 0) return string::npos;

    size_type sep_len = 1;
    if (flag == sep_whole_str) sep_len = sep.length();
    const char *psep = sep.c_str();

    if (flag == sep_multiple)
    {
        //Pass all the separators symbols at the begin of the string
        while (*pstr && sep.find(*pstr) != string::npos)
        {
            ++pstr;
            ++start;
        }
    }

    for (count = 0; ; count++)
    {
        char c = *pstr;
        bool found = false;

        //Outside quote find one of separator symbols
        if (quote_chr == 0)
        {
            if (sep_len == 1)
            {
                found = sep.find(c) != string::npos;
            }
            else
            {
                found = strncmp(psep, pstr, sep_len) == 0;  //sep.compare(0, sep_len, pstr, sep_len) == 0;
            }

        }

        ++pstr;

        if (c == 0 || found)
        {
            if (dst) dst->assign(*this, start, count);

            //The next time it will be the next separator character
            //But we must check, whether it is the end of the string.
            start += count;
            if (c) start += sep_len;
            break;
        }

        //Switch quote. If it is not a quote yet, try to check any of
        //quote symbols. Otherwise quote must be finished with quote_symb
        if (quote_chr == 0)
        {
            if (quote.find(c) != string::npos)
            {
                quote_chr = c;
                continue;
            }
        }
        else
        {
            //Inside quote pass all the pair symbols
            if (pair_chr && c == pair_chr)
            {
                if (*pstr)
                {
                    ++count;
                    ++pstr;
                }
                continue;
            }
            if (c == quote_chr)
            {
                quote_chr = 0;
                continue;
            }
        }
    }
    return start;
}


//-------------------------------------------------------------------------------------
/**
Split up the string into the many pieces. This function uses token function.

@return  Number of found tokens

@param str_vec             Pointer to the destination vector of strings. 
                           If this parameter is null
                           the function may be used to counting 
                           tokens

@param sep                 String with possible separators. Every symbol
                           of this string is separator

@param quote               String with quotation symbols, for example, "'"
                           Inside quote string is not divided. Quotation can
                           start with any of this symbol, but finish with
                           the same symbol only.

@param pair_chr            Usually '\' symbol to include quotation symbols
                           inside the quote parts

@param flag                Flag sep_single or sep_multiple to determine, 
                           whether the separators, going successively are 
                           nerpreted as detached empty tokens (sep_single) 
                           or one token (sep_multiple), as strtok does.
                           sep_single:   string "123,,456" will be "123", "", "456"
                           sep_multiple: string "123,,456" will be "123", "456"
                           
\\================================================================================*/
unsigned mstring::split(mstr_vector *str_vec, const string &sep,
                        const string &quote, char pair_chr, sep_flag flag) const
{
    if (str_vec) str_vec->clear();

    unsigned count = 0;
    size_type start = 0;
    mstring tmp;

    while ((start = token(&tmp, start, sep, quote, pair_chr, flag)) != string::npos)
    {
        if (str_vec) str_vec->push_back(tmp);
        count++;
    }
    return count;
}




//-------------------------------------------------------------------------------------
/**
Check quote parts balance.

@return true = balance OK, false - no balance
   

@param quote               String with quotation symbols, for example, "'"
                           Quotation can start with any of this symbol, 
                           but finish with
                           the same symbol only.

@param pair_chr            Usually '\' symbol to include quotation symbols
                           inside the quote parts
\\================================================================================*/
bool mstring::chkquote(const string &quote, char pair_chr) const
{
    char quote_chr = 0;

    const char *pstr = c_str();
    char c;

    while ((c = *pstr++) != 0)
    {
        //Switch quote. If it is not a quote yet, try to check any of
        //quote symbols. Otherwise quote must be finished with quote_symb
        if (quote_chr == 0)
        {
            if (quote.find(c) != string::npos)
            {
                quote_chr = c;
                continue;
            }
        }
        else
        {
            //Inside quote pass all the pair symbols
            if (pair_chr && c == pair_chr)
            {
                if (*pstr)
                {
                    ++pstr;
                }
                continue;
            }
            if (c == quote_chr)
            {
                quote_chr = 0;
                continue;
            }
        }
    }
    return quote_chr == 0;
}



//-------------------------------------------------------------------------------------
/**
    Get the next BRACKET token from string. 
    Parsing nested brackets can be done recursively. For example:	
    \begin{verbatim}
    void print_brackets(int ident, std::mstring str, std::string &quote, char pair)
    {
        std::mstring tmp;
        std::string::size_type start = 0;
        int bal;
  
       while((start = str.brtok(&tmp, start, &bal, 
                      '+', '-', quote, pair)) != 
              std::string::npos)
       {
           for(unsigned i = 0; i < ident; i++) cout << '*';
           cout << tmp.c_str() << " = " << bal << "\n";
           print_brackets(ident+1, tmp, quote, pair);
       }
    }
    \end{verbatim}
  @return  the next position in string to be continue or 
           string::npos if no more tokens
 
 
  @param dst                 Pointer to the destination string. 
                             If this parameter is null
                             the function may be used to counting 
                             tokens
 
  @param start               Start position in the string - 0 or 
                             the previous returned value
 
  @param balance             Returns bracket balance
                             positive - a lake of closing brackets
                             negatine - an overage of closing brackets
 
  @param br_open             Open bracket character
 
  @param br_close            Close bracket character
 
  @param quote               String with quotation symbols, for example, "'"
                             Inside quote string is not divided. Quotation can
                             start with any of this symbol, but finish with
                             the same symbol only.
 
  @param pair_chr            Usually '\' symbol to include quotation symbols
                             inside the quote parts
\\================================================================================*/
string::size_type mstring::brtok(string *dst, size_type *start, int *balance,
                                 char br_open, char br_close,
                                 const string &quote, char pair_chr) const
{
    int brbal = 0;
    char quote_chr = 0;
    size_type count = 0;

    size_type start_pos = string::npos;
    size_type end_pos = string::npos;

    if (dst) dst->erase();

    if (*start == string::npos) return string::npos;

    const char *pstr = c_str() + *start;

    if (*pstr == 0) return string::npos;

    char c;
    for (count = 0; (c = *pstr++) != 0; count++)
    {
        //Outside quote find a bracket
        if (quote_chr == 0)
        {
            if (c == br_open)
            {
                if (start_pos == string::npos && brbal == 0) start_pos = count;
                brbal++;
            }

            if (c == br_close)
            {
                brbal--;
                if (end_pos == string::npos && brbal == 0) end_pos = count;
            }
        }

        //Switch quote. If it is not a quote yet, try to check any of
        //quote symbols. Otherwise quote must be finished with quote_symb
        if (quote_chr == 0)
        {
            if (quote.find(c) != string::npos)
            {
                quote_chr = c;
                continue;
            }
        }
        else
        {
            //Inside quote pass all the pair symbols
            if (pair_chr && c == pair_chr)
            {
                if (*pstr)
                {
                    ++count;
                    ++pstr;
                }
                continue;
            }
            if (c == quote_chr)
            {
                quote_chr = 0;
                continue;
            }
        }
    }

    if (start_pos != string::npos && end_pos != string::npos)
    {
        if (dst) dst->assign(*this, *start + start_pos + 1,
                                 end_pos - start_pos - 1);
        ++end_pos;
    }

    if (balance) *balance = brbal;

    if (end_pos == string::npos) return npos;

    end_pos += *start;
    *start += start_pos;
    return end_pos;
}


}


syntax highlighted by Code2HTML, v. 0.9.1