/*
 * @(#) f_iter.cpp - Text file iterator demo.
 * (c) 1996 Ivan Maidanski <ivmai@chat.ru> http://ivmai.chat.ru
 * Freeware program source. All rights reserved.
 **
 * Language: ISO/ANSI C++
 * Tested with: Watcom C++ v10.0
 * Last modified: 1996-05-24 09:05:00 GMT+04:00
 */

#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream.h>

typedef enum {FALSE,TRUE} BOOL;

class TString
{
 public:
  TString(const char *AStr="",int BlockSize=16);
  TString(char Ch,int BlockSize=1) : Block(BlockSize)
  { Count=(Block+1)/Block; Str=new char[Block*Count];Str[0]=Ch;Str[1]='\0'; };
  TString(const TString& String) : Block(String.Block), Count(String.Count)
  { Str=new char[Block*Count]; strcpy(Str,String.Str); };
  ~TString() { delete[] Str; };
  operator const char* () const { return (Str); };
  TString& operator= (const TString& String);
  TString& operator+= (const TString& String);
  BOOL operator== (const TString& String) const
  { return (strcmp(Str,String.Str)? FALSE : TRUE); };
  BOOL operator!= (const TString& String) const
  { return (strcmp(Str,String.Str)? TRUE : FALSE); };
  char operator[] (int Pos) const
  { return ((Pos>=0 && Pos<strlen(Str))? Str[Pos] : '\0'); };
  void Trunc(int Cnt)
  { if (Cnt>0) { Cnt=strlen(Str)-Cnt; if (Cnt>=0) Str[Cnt]='\0'; } };
  friend istream& operator>> (istream& Stream,TString& String);
 private:
  char* Str;
  int Count;
  const int Block;
};

TString::TString(const char *AStr,int BlockSize) : Block(BlockSize)
{
 Count=(strlen(AStr)+Block)/Block;
 Str=new char[Block*Count];
 strcpy(Str,AStr);
};

TString& TString::operator= (const TString& String)
{
 delete[] Str;
 Count=(strlen(String.Str)+Block)/Block;
 Str=new char[Block*Count];
 strcpy(Str,String.Str);
 return (*this);
};

TString& TString::operator+= (const TString& String)
{
 int OldCount=Count;
 Count=(strlen(Str)+strlen(String.Str)+Block)/Block;
 if (OldCount<Count)
 {
  char* OldStr=Str;
  Str=new char[Block*Count];
  strcpy(Str,OldStr);
  delete[] OldStr;
 }
 strcat(Str,String.Str);
 return (*this);
};

istream& operator>> (istream& Stream,TString& String)
{
 char Ch;
 String="";
 while (!Stream.eof() && (Stream.read(&Ch,1), Ch!='\n'))
  String+=Ch;
 return (Stream);
};

class TFile
{
 public:
  TFile(const TString&) { err=0; };
  TFile(const TFile&) { err=0; };
  virtual ~TFile() {};
  virtual char Read()=0;
  virtual void Seek(long APos=0)=0;
  virtual long Where()=0;
  virtual long Length()=0;
  int Error() const { return (err); };
 protected:
  int err; // 0 - no error, -1 - end of file, -2 - cannot open
};

class TOpenedFile : public TFile
{
 public:
  TOpenedFile(const TString& AName) : TFile(AName), Name(AName)
  { fid=open(AName,O_RDONLY || O_TEXT); err=(fid<0)? -2 : 0; };
  TOpenedFile(const TOpenedFile& AFile) : TFile(AFile), Name(AFile.Name)
  { fid=open(Name,O_RDONLY || O_TEXT); err=(fid<0)? -2 : 0; };
  virtual ~TOpenedFile() { close(fid); };
  virtual char Read()
  { char Ch='\0'; err=(read(fid,&Ch,1)<1)? -1 : 0; return (Ch); };
  virtual void Seek(long APos=0) { err=(lseek(fid,APos,0)<0)? -2 : 0; };
  virtual long Where() { err=0; return(tell(fid)); };
  virtual long Length();
 private:
  const TString Name;
  int fid;
};

long TOpenedFile::Length()
{
 long Pos=tell(fid);
 long Len=lseek(fid,0,2);
 err=(lseek(fid,Pos,0)<0)? -2 : 0;
 return (Len);
};

class TClosedFile : public TFile
{
 public:
  TClosedFile(const TString& AName) : TFile(AName), Name(AName) { Pos=0; };
  TClosedFile(const TClosedFile& AFile)
             : TFile(AFile), Name(AFile.Name) { Pos=0; };
  virtual ~TClosedFile() {};
  virtual char Read();
  virtual void Seek(long APos=0) { err=0; Pos=APos; };
  virtual long Where() { err=0; return (Pos); };
  virtual long Length();
 private:
  const TString Name;
  long Pos;
};

char TClosedFile::Read()
{
 char Ch='\0';
 int fid=open(Name,O_RDONLY || O_TEXT);
 err=(fid<0)? -2 : 0;
 if (!err)
 {
  lseek(fid,Pos,0);
  err=(read(fid,&Ch,1)<1)? -1 : 0;
  if (!err)
   Pos=tell(fid);
  close(fid);
 }
 return (Ch);
};

long TClosedFile::Length()
{
 int fid=open(Name,O_RDONLY || O_TEXT);
 long Len=lseek(fid,0,2);
 err=(fid<0)? -2 : 0;
 if (!err)
  close(fid);
 return (Len);
};

class TFileIter
{
 public:
  TFileIter(TFile* AFile) : File(AFile) {};
  TFileIter(const TFileIter& AFileIter) : File(AFileIter.File) {};
  virtual ~TFileIter() {};
  virtual BOOL Next();
  TString Get() const { return (Value); };
  void Reset() { File->Seek(); };
  int Error() const
  { if (File->Error()!=-1) return (File->Error()); else return (0); };
 protected:
  TFile* File;
  TString Value;
};

BOOL TFileIter::Next()
{
 Value=File->Read();
 return (File->Error()<0? FALSE : TRUE);
};

class TSubORFileIter : public TFileIter
{
 public:
  TSubORFileIter(TFile* AFile,const TString& ADelimiter=" .,;\t\n")
                : TFileIter(AFile), Delimiter(ADelimiter) {};
  virtual BOOL Next();
 private:
  const TString Delimiter;
};

BOOL TSubORFileIter::Next()
{
 char Ch;
 Value="";
 while (Ch=File->Read(), !File->Error() && strchr(Delimiter,Ch));
 while (!File->Error() && !strchr(Delimiter,Ch))
 {
  Value+=Ch;
  Ch=File->Read();
 }
 return ((Value==TString("") && File->Error()<0)? FALSE : TRUE);
};

class TSubANDFileIter : public TFileIter
{
 public:
  TSubANDFileIter(TFile* AFile,const TString& ADelimiter="\n")
                 : TFileIter(AFile), Delimiter(ADelimiter) {};
  virtual BOOL Next();
 private:
  const TString Delimiter;
};

BOOL TSubANDFileIter::Next()
{
 char Ch;
 int Cnt=0;
 Value="";
 while (Ch=File->Read(), !File->Error())
 {
  Value+=Ch;
  if (Delimiter[Cnt]==Ch)
   Cnt++;
   else Cnt=0;
  if (Cnt==strlen(Delimiter))
  {
   Value.Trunc(Cnt);
   return (TRUE);
  }
 }
 return ((Value==TString("") && File->Error()<0)? FALSE : TRUE);
};

int main(int,char**)
{
 TString Name;
 cout << "Text file iterator classes demo.\n"
      << " Enter file name: ";
 cin >> Name;
 TString Mode;
 cout << " Enter file access mode (Opened,Closed)? ";
 cin >> Mode;
 TFile* File;
 if (Mode[0]=='o')
  File=new TOpenedFile(Name);
  else File=new TClosedFile(Name);
 cout << " Enter iterator type (Symbol,Line,ORString,ANDString)? ";
 cin >> Mode;
 TFileIter* Iter;
 if (Mode[0]=='o' || Mode[0]=='a')
 {
  TString Delimiter;
  cout << " Enter delimiter string: ";
  cin >> Delimiter;
  if (Mode[0]=='o')
   if (Delimiter!=TString(""))
   {
    Delimiter+="\n";
    Iter=new TSubORFileIter(File,Delimiter);
   }
    else Iter=new TSubORFileIter(File);
   else Iter=new TSubANDFileIter(File,Delimiter);
 }
  else if (Mode[0]=='l')
   Iter=new TSubANDFileIter(File);
   else Iter=new TFileIter(File);
 cout << " Result:\n";
 while (Iter->Next())
  cout << Iter->Get() << "\n";
 delete Iter;
 delete File;
 return (0);
}
