//------------------------------------------------------------
// $Id: myString.cc,v 1.3 2000/05/21 18:43:03 yamauchi Exp $
//------------------------------------------------------------
//  myString.cc and strnew implementation
// 	IBM の IString クラスは string.h, ctype.h を拡張した形のもので
//	あって、gnu のように awk の関数を実現するという考えではない。
// 	しかし、gnu の String クラスの Regex という正規表現部分を xlC 
// 	によってコンパイルすることは困難なようである。そこで既に GNU 
// 	の String クラスを使っている私としては、ばかばかしい感はあるが、
//	非常に限定された myString クラスを書くことにした。
//	GNU のものが利用できるのならばこのクラスは無用である。
//	また利用できる部分は GNU libg++-2.7.1 を利用させていただいた。
//	
//	Because in sp2 with AIX3.2, I could not use g++ and GNU libg++,
//	but I want to use them. So, I wrote some code for sp2 based on 
//	GNU gcc and libg++.
//	If you can use gcc and libg++, please use them. Gnu code is
//	best, I think.
//	
//	This class is poor.
//
// 	last change 平成８年８月２４日(土)
// Copyright (C) Hitoshi Yamauchi 1995-1997
// 	* 1997年9月2日(火) : splitWithWhite の返す数値にバグがあり fix 
//	* 1998年4月 4日(土): splitWithWhite の maxn == 1 の時に動作が
//	  おかしくなるバグがあり fix 
//	  #define DEBUG でエラーを出力するように修正	
//------------------------------------------------------------
// 次の DEBUG を定義するとエラーを出力する。消すと返り値のみとなる
#define DEBUG

// boolian
#ifndef YorN 
enum YorN {
    NO  = 0,
    YES = 1
};
#endif

#include <assert.h>
#include "myString.h"

//------------------------------------------------------------
// strnew 
//------------------------------------------------------------
#include <stdio.h>
#include <iostream.h>
#include <string.h>
#include <stdlib.h>

//------------------------------
// 文字配列の獲得
//------------------------------
char *strnew(const char *str)
{
    if(str == NULL)
    {
	cerr << "Error! : can't new (null) at strnew." << endl;
	abort();
	exit(1);
    }
    char *r = NULL;
    r = new char [ strlen(str) + 1 ];
    strcpy(r, str);
    return(r);
}

//------------------------------
// 文字配列の領域の解放
//     NULL を渡されても大丈夫
//------------------------------
void strdel(char *delstr)
{
    if(delstr != NULL)		//// NULL の場合にはなにもしない
    {
	delete [] delstr;
    }
}

//------------------------------------------------------------
// myString
//------------------------------------------------------------
// white space?
//------------------------------
inline static YorN isWhiteSpace(const char ch)
{
    if((ch == ' ') || (ch == '\t') || (ch == '\n'))
    {
        return(YES);
    }else
    {
        return(NO);     
    }
}

// 行の最後にある white space を削除する
static void deleteLastWhiteSpace(char *pWork)
{
    int lastIdx = strlen(pWork) - 1;
    while(lastIdx >= 0)
    {
	if(isWhiteSpace(pWork[lastIdx]) == YES)
	{
	    pWork[lastIdx] = '\0';
	}else
	{
	    break;
	}
	lastIdx--;
    }
}

// 行の最初にある white space を skip したポインタを返す
static char * getFirstNonWSchar(char *pStr)
{
    char *pIdx = pStr;
    // cout << "debug: In     getFirstNonWSchar :" << pIdx << ":end" <<	endl;
    while((*pIdx) != '\0')
    {
	if(isWhiteSpace(*pIdx) == NO)
	{
	    break;
	}
	pIdx++;
    }
    // cout << "debug: return getFirstNonWSchar :" << pIdx << ":end" <<	endl;
    return(pIdx);
}

//------------------------------
// char *getOneWord(char **ppStr)
//	文字列の最初の空白を除き、次の空白部分に '\0' を入れる
//	return はその '\0' の次に文字が続いていればそこへのポインタ
//	
//"  I_Like    Emi Shinohara" -> "  I_Like\0    Emi Shinohara"
//^*ppStr                           ^*ppStr ^return
//------------------------------
static char *getOneWord(char **ppStr)
{
    assert(ppStr     != NULL);
    assert((*ppStr)  != NULL);
    assert((**ppStr) != '\0');
    
    char *pIdx = getFirstNonWSchar(*ppStr);    
    (*ppStr)   = pIdx;

    while((isWhiteSpace(*pIdx) == NO) && ((*pIdx) != '\0'))
    {
	pIdx++;			// 空白でも '\0' でもない = word; skip
    }
    if((*pIdx) == '\0')
    {
	return(NULL);		// すでに最後
    }else
    {
	(*pIdx) = '\0';
	pIdx++;
	if((*pIdx) != '\0')
	{
	    return(pIdx);	// まだある
	}else
	{
	    return(NULL);	// 最後に空白がひとつあった
	}
    }
}
    
//------------------------------
// split string into array res at separators; return number of elements
//	split の white space のみ版
//-----------------------------
int splitWithWhite(const myString& x, myString res[], int maxn)
{
    if(maxn <= 0)
    {
#ifdef DEBUG
	cout << "Error! : maxn [" << maxn << "] must be > 0." << endl;
#endif
	return(0);
    }

    char *pOrg = strnew(x.mpStr); // pOrg[*] の内容は変更される
    deleteLastWhiteSpace(pOrg);
    char *pIdx = pOrg;

    if(strlen(pOrg) == 0)
    {
#ifdef DEBUG
	cout << "Warning! : string has no contensts." << endl;
#endif
	return(0);		// 何もなかった
    }

    int i;
    // maxn == 1 の時には次の loop を通らず(i = 0 の初期化はある)に最
    // 後に全てを代入する
    char *pNext = pIdx;		
    for(i = 0; i < (maxn - 1); i++) // 一つ手前まで
    {
	pNext  = getOneWord(&pIdx);
	res[i] = myString(pIdx);
	if(pNext == NULL)
	{
	    strdel(pOrg);
	    return(i + 1);	// もう次はない i は index なので個数は + 1
	}else
	{
	    pIdx = pNext;
	}
    }
    // 最後に全部入れる
    assert(pNext != NULL);
    res[i] = myString(getFirstNonWSchar(pNext));
    return(i + 1);
}

//-----------------------------
// get number of words (separator is white space)
//	単語数を得る 
//-----------------------------
int getNumOfWords(const myString& s)
{
    YorN inWord = NO;
    int i; 
    int wc = 0;
    for(i = 0; i < s.mLen; i++)
    {
	if(isWhiteSpace(s.mpStr[i]) == YES)
	{
	    inWord = NO;	// 単語の中ではない	    
	}else if(inWord == NO)
	{
	    inWord = YES;	// これまで out word かつ 今は単語の中 
	    wc++;
	}else
	{
	    // これまで in word かつ 今も単語の中 : なにもしない
	}
    }
    return(wc);
}

//------------------------------------------------------------
// compare (通常の strcmp は s1 が短い時にあってしまうことがある)
//------------------------------------------------------------
static int mystrncmp(const char *str1, const char *str2)
{
    static int len1, len2;
    len1 = strlen(str1);
    len2 = strlen(str2);

    if(len1 != len2)
    {
	return(len2 - len1);	// 0 でなければ良い
    }else
    {
	return(strncmp(str1, str2, len1));
    }
}

int operator==(const myString& x, const myString& y)
{
    return(mystrncmp(x.mpStr, y.mpStr) == 0);
}
int operator!=(const myString& x, const myString& y)
{
    return(mystrncmp(x.mpStr, y.mpStr) != 0);
}
int operator==(const myString& x, const char* t) 
{
    return(mystrncmp(x.mpStr, t) == 0);        
}
int operator!=(const myString& x, const char* t) 
{
    return(mystrncmp(x.mpStr, t) != 0);        
}
int operator==(const char* t, const myString& x) 
{
    return(mystrncmp(x.mpStr, t) == 0);        
}
int operator!=(const char* t, const myString& x) 
{
    return(mystrncmp(x.mpStr, t) != 0);        
}

// concatenation
myString operator+(const myString& x, const myString& y)
{
    myString r = x;  
    r += y;
    return(r);
}

//------------------------------
// output
//-----------------------------
ostream& operator<<(ostream& s, const myString& x)
{
    s << x.chars(); 
    return s;
}

//------------------------------
// test 
//-----------------------------
#ifdef TEST_MYSTRING
int main()
{
    myString s = "    I like   	Megumi Hayashibara and 		Makoto Kino\n";
    cout << "ORG String:::" << s << ":::" << endl;

    int num_of_words = getNumOfWords(s);
    
    myString *pWords = new myString[num_of_words];
    int i;
    cout << "split " << splitWithWhite(s, pWords, num_of_words) << endl;

    for(i = 0; i < num_of_words; i++)
    {
	cout << "split[" << i << "]:::" << pWords[i] << ":::" << endl;
    }
    delete[] pWords;

    myString s1 = s;
    if(s1 == s)
    {
	cout << "match" << endl;
    }else
    {
	cout << "do not match" << endl;
    }
}    
// g++ -DTEST_MYSTRING -Wall -g myString.cc strnew.o
#endif
