• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

Can the bstring library make cpp like basic strings?

Started by Paul Breen, January 17, 2010, 04:42:57 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Paul Breen

I found this library for c and cpp strings:
http://bstring.sourceforge.net/
Is it possible that using this instead of the standard string functions would make c and cpp more like basic string handling?

This subject comes up in exercise eight, comparing string handling.

thanks,
PB

Frederick J. Harris

#1
Hello Paul!

    I can't believe I missed this post of yours, but somehow I did.  I'm really glad I found it and that you pointed this out.  I just downloaded the various parts and will study it close.  Believe it or not, I had been musing about this for some time.  Somewhere in around ProgEx30 or so (29- i just checked) I devoted quite a few posts about the development of my String Class.  Its just based on '\0' terminated strings though.  I had been thinking about doing something similiar with BSTRs. Actually, I believe its the CComBSTR Class...

http://msdn.microsoft.com/en-us/library/zh7x9w3f.aspx

that I was considering as my point of departure, so to speak, and that's associated pretty close with ATL, which also interests me.  What had occurred to me was deriving a class from CComBSTR and adding good PowerBASIC like stuff such as Parse, InStr, etc.  Just haven't gotten to it yet.  I'll get back with you on this after I've had time to look at it.  But I think you've got a good point.

James C. Fuller

Fred,
  I know this is primarily a c++ endeavor but you (or others) may want to check out the string handling functions in glib. I use them all the time with "c" and BaCon on Linux but José did the headers for PB so you can experiment on Windows too.

James

Frederick J. Harris

Quote
you (or others) may want to check out the string handling functions in glib. I use them all the time with "c" and BaCon on Linux but José did the headers for PB so you can experiment on Windows too.

Hi Jim!

    Yea, I should do that.  I'm not sure what BaCon is.  I have heard of glib though in the little bit of Linux coding I've done.

Fred

Frederick J. Harris

Hi Paul!

     Well, Ive taken a cursory look at BStrLib (Better String library), and it doesn't appear to be based in any way on the BSTR type which is a part of the OLE/COM libraries.  Also, it isn't UNICODE. 

     I have downloaded and installed the files, and I compiled with Code::Blocks this minimal console program which I named Main.cpp and I optimized it for small code and to strip the executable of symbols...


#include <stdio.h>       //standard C stream i/o file
#include "bstrlib.h"       //Better String Library Specific header
#include "bstrwrap.h"   //C++ Wrappers around BStrLib
#include "bstraux.h"     //Some extra enhancements

int main(void)
{
CBString strName("Frederick");
printf("strName = %s\n",strName.data);

return 0;
}

//Output
//=======
//Frederick


....and I ended up with a - now take a deep breath - 402 K executable!!!!!  I really need to see what's getting compiled into that; perhaps the atrocious iostream code or something.  I believe if one contents him/herself with the std::string class that's now part of the standard C++ libraries, and doesn't link in iostream, one can end up with fairly decent string handling capabilities and start out somewhere around 30 - 35 K or so in terms of executable size.  Perhaps I'm overly fixated on the size thing; be that as it may, I'm not inclined to use any language product that needs more than 15 or 20 k to get a program up on the screen.

     Where I'm at with the whole thing at this point is that I'm pretty sure BSTRs are the way to go if you intend to do cross language or COM work because they inter-operate so nicely with different dialects of BASIC - PowerBASIC particularly.  I'd like to see if I can't create a BSTR variant of my C++ String class, which only adds a few K to an executable.  By adding it to a Hello, World GUI program I start out at only around 10 or 15 K depending on the compiler.  I'm speaking of the String Class I start developing in that ProgEx29 example.




Paul Breen

Fred:
All (well, I checked three or four) of my code::blocks programs, based on your code, are really big, in the 400K+ size.The g++ compiler puts all the debug symbols inside the exe and the Microsoft compiler has a separate file with most, if not all, of the debug symbols. I compared them to the visual c++ express projects and they are only ~10K. But, when you add the debug file and the exe together, the total is much closer to each other.  As far as booting up speed and size, old VC6 is still the best, and I like the default workspace that it makes. You can just double click on a file_name.cpp and away you go.

There is a really cool add-on for c++ called visual assist, by whole tomato. http://www.wholetomato.com/  works on VC6 to VS2010

I thought the bstring library kept the size of the string in a series of bytes at the beginning, just like bstring. That is why I thought it might be a good compromise, and a better way to compare the languages.

I'll wait for your string class, it looks like a good idea.

PB

Frederick J. Harris

I always compile my CodeBlocks projects with the options checked in the 'Build Options' to strip the symbols from the exe and optimized for small size, and all those sample programs of mine only come in in the 7 to 15 K range.  I guess with debug symbols included and all that they would be as you say way bigger.  The odd thing is that sometimes the VC9 C++ programs are smaller than the CodeBlocks (g++) and sometimes the other way around.  However, when I compiled that program that included the BStrLib code I downloaded, I had done all that to minimize size and still came up over 400 k.  I'll have to see what I get otherwise! 

Basically, what you seem to be interested in is very much along my way of thinking too, and that is a way to use C++ so it doesn't have so many d*** rough edges!  Several years back I had to start rewriting several handheld data recorder programs that I had originally written in QuickBasic 4.5 and PowerBASIC 3.5.  At the time my opinion of C++ was really low due to the fact that I really disliked the various class libraries associated with it.  To me, I really loved Sdk style coding, and somehow the stuff class libraries did with the Api really affronted me.  So I accepted the fact that I'd just have to use C to write my Windows CE programs.  I recall one particular day I was writing the WM_CREATE handler for the custom control grid I had put in a dll.  I was passing a string into the grid through the caption member of the CreateWindow() call that created the grid, and the string contained the column names for the grid columns, their widths in pixels, and I believe the number of asciiz characters allowed for that col/field, and the string looked something like this....

szColumn = L"TNum-70-5,Sp-45-4,Dbh-40-3,Ht-40-3,Cull-45-3,Block-50-3,BkCtr-50-5";

In the C dll I had to parse that string which as you can see is comma delimiter with '-' chars between parameters.  Something like that would be all of about five minutes work in PowerBASIC with that language's terrific string parsing functions.  Doing it in C with strtok() or whatever it is pretty much killed an entire afternoon for me.  That whole affair played on my mind for quite some time until one day out of the clear blue sky it occurred to me that if I could create my own C++ string class and put all my favorite BASIC string handling functions in it, I'd finally have a C++ coding environment that would be really useful to me.

An issue going on there is that at that time I only had VC++ 6, and the only String Class in that environment was part of the MFC library, which was unacceptable to me.  Sometime after that the std::string class became a part of the C++ standard library, but I was not aware of that, unfortunately. 

So I began to develop a C++ String class of my own without really doing much research on the issue, and it struck me that it wasn't really terribly hard to do.  I had studied C++ rather intensively on and off for years, and always thought its sophisticated and powerful class capabilities were really neat.  I always foundered though when I looked at its application to Windows OOP frameworks as I previously mentioned.  It wasn't that I couldn't do it; I just didn't like them at all.  When I found that I could use C++'s class capabilities to meet a real compelling need that I had and that was for a powerful String class to make my Windows CE coding bearable, it dawned on me as something of a profound revelation that in rejecting C++ as I had I had 'thrown the baby out with the bathwater', so to speak.   Just because I disliked GUI class frameworks didn't mean that C++ was of no use to me.  C++ isn't a GUI class framework.  Its a programming language with useful and powerful OOP capabilities relating to classes in the abstract. 

At this time my String class is around 600 - 700 lines of code only and it only adds maybe 6 or 7 k to an executable.  I'll post a GUI app for you that has a main form with four buttons that exercises a bunch of member functions such as InStr(), Parse(), Left(), Right(), Mid(), Val(), etc.  When a button is clicked the results of the code handling functions are displayed in four seperate windows.  How big do you think it is?  50K, 100k, a megabyte?  With VC9 it comes in 17k.  With CodeBlocks I believe around 25 or 30 k. 

So I know that there is something of a reputation out there that C++ produces bloatware.  That's not exactly true.  Where you get bloatware from are the libraries you link with.  With VC9 or CodeBlocks (GNU) compiled as either C or C++ (makes no difference) your basic "Hello, World!" GUI comes in 7 to 10 k.  Adding my string class will add only a few to that - maybe bring you up to 10 - 11 or so.  Add iostream though and all of a sudden your up into three digits!  And for what?  So that you can do this...

cout << "Hello, World!" << '\n';

instead of this...

puts("Hello, World!");

Well that's a really big help!  My use for iostream is a C++ substitute for #Bloat.  You may have noticed you won't find it anywhere in my tutorials.  Anyway, I would think it would be of some satisfaction to Mr. Zale to see that it takes a 400k C++ exe to produce a String enabled version of the language to compete with his PowerBASIC whose executables start around 15 k.   

Here's a C++ GUI app that comes in 17k with VC9 Pro optimized for tight code...


//StrDemo.h
#define IDC_CONCATENATION       1500
#define IDC_PARSE               1505
#define IDC_LEFT_RIGHT_MID      1510
#define IDC_INSTR               1515


typedef struct    WindowsEventArguments               
{
HWND             hWnd;                               
WPARAM           wParam;                             
LPARAM           lParam;                             
HINSTANCE        hIns;                               
}WndEventArgs, *lpWndEventArgs;


struct EVENTHANDLER
{
unsigned int    Code;
long            (*fnPtr)(lpWndEventArgs);
};




//Strings.h
#if !defined(STRINGS_H)
#define STRINGS_H
#define EXPANSION_FACTOR      2
#define MINIMUM_ALLOCATION   16

class String
{
public:
~String();                               //String Destructor
String();                                //Uninitialized Constructor
String(const TCHAR);                     //Constructor Initializes String With TCHAR
String(const TCHAR*);                    //Constructor Initializes String With TCHAR*
String(const String&);                   //Constructor Initializes String With Another String (Copy Constructor)
String(const int);                       //Constructor Initializes Buffer To Specific Size
String& operator=(const TCHAR);          //Assigns TCHAR To String
String& operator=(const TCHAR*);         //Assigns TCHAR* To String
String& operator=(const String&);        //Assigns one String to another (this one)
bool operator==(const String);           //For comparing Strings
String& operator+(const TCHAR);          //For adding TCHAR to String
String& operator+(const TCHAR*);         //For adding null terminated TCHAR array to String
String& operator+(const String&);        //For adding one String to Another
String Left(unsigned int);               //Returns String of iNum Left Most chars of this
String Right(unsigned int);              //Returns String of iNum Right Most chars of this
String Mid(unsigned int, unsigned int);  //Returns String consisting of number of chars from some offset
String Remove(const TCHAR);              //Returns A String With A Specified TCHAR Removed
String Remove(const TCHAR*, bool);       //Returns A String With A Specified TCHAR* Removed
int InStr(const TCHAR);                  //Returns one based offset of a specific TCHAR in a String
int InStr(const TCHAR*, bool);           //Returns one based offset of a particular TCHAR pStr in a String
int InStr(const String&, bool);          //Returns one based offset of where a particular String is in another String
String LTrim();                          //Returns String with leading spaces/tabs removed
String RTrim();                          //Returns String with spaces/tabs removed from end
String Trim();                           //Returns String with both leading and trailing whitespace removed
unsigned int ParseCount(const TCHAR);    //Returns count of Strings delimited by a TCHAR passed as a parameter
void Parse(String*, TCHAR);              //Returns array of Strings in first parameter as delimited by 2nd TCHAR delimiter
String Parse(TCHAR,unsigned int);        //Returns nth one based sub-string of String based on delimiter
String CStr(const int);                  //Converts String to integer
String CStr(const unsigned int);         //Converts String to unsigned int
String CStr(const short int);            //Converts String to 16 bit int
String CStr(const double);               //Converts String to double
int iVal();                              //Returns int value of a String
int LenStr(void);                        //Returns length of string
TCHAR* lpStr();                          //Returns address of pStrBuffer member variable
void Print(bool);                        //Outputs String to Console with or without CrLf

private:
TCHAR* pStrBuffer;
int    iAllowableCharacterCount;
};
#endif
   



//frmOutput.h
long __stdcall frmOutput(HWND, unsigned int, WPARAM, LPARAM);




//Main.cpp
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include "StrDemo.h"
#include "frmOutput.h"
#include "Strings.h"
EVENTHANDLER  EventHandler[3];


long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
TCHAR szClassName[]=_T("frmOutput");
WNDCLASSEX wc;
HWND hCtrl;

Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
wc.lpszClassName=szClassName;                          wc.lpfnWndProc=frmOutput;
wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=CS_DBLCLKS;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=Wea->hIns;
wc.hIconSm=NULL;                                       wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=0;
wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
RegisterClassEx(&wc);
hCtrl=CreateWindow(_T("button"),_T("String Concatenation"),WS_CHILD|WS_VISIBLE,45,20,250,25,Wea->hWnd,(HMENU)IDC_CONCATENATION,Wea->hIns,0);
hCtrl=CreateWindow(_T("button"),_T("String Parsing And Trimming"),WS_CHILD|WS_VISIBLE,45,55,250,25,Wea->hWnd,(HMENU)IDC_PARSE,Wea->hIns,0);
hCtrl=CreateWindow(_T("button"),_T("Good Old Left, Right, And Mid!"),WS_CHILD|WS_VISIBLE,45,90,250,25,Wea->hWnd,(HMENU)IDC_LEFT_RIGHT_MID,Wea->hIns,0);
hCtrl=CreateWindow(_T("button"),_T("But Led's Not Forget InStr( )!"),WS_CHILD|WS_VISIBLE,45,125,250,25,Wea->hWnd,(HMENU)IDC_INSTR,Wea->hIns,0);

return 0;
}


void btnConcatenation_OnClick(void)
{
HWND hOutput;
String s;
int iY=0;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,450,20,305,480,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
for(unsigned int i=65; i<91; i++)
{
     s=s+i;                                   //65 Is Capital 'A'
     TextOut(hDC,0,iY,s.lpStr(),s.LenStr());  //TextOut() is the Windows Version Of Basic's 'Print'
     iY=iY+16;                                //A Spacing Of 16 Pixels Works Good For Most Normal Sized Fonts
}
ReleaseDC(hOutput,hDC);
}


void btnParse_OnClick(void)
{
String* ptrStrings;
unsigned int iCt;
HWND hOutput;
String s;
int iY=0;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,50,400,310,180,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
s=_T("Frederick,  Mary,  James,  Samuel,  Edward,  Richard,  Michael,  Joseph");  //Create comma delimited text string
iCt=s.ParseCount(_T(','));                                                        //Count delimited substrings
ptrStrings = new String[iCt];                                                     //Create array of String Pointers
s.Parse(ptrStrings,_T(','));                                                      //Parse 's' based on comma delimiter
for(unsigned int i=0; i<iCt; i++)
{
     ptrStrings[i].LTrim();  //Comment This Out To See Effect.                     //There are some spaces in delimited strings so remove them
     TextOut(hDC,0,iY,ptrStrings[i].lpStr(),ptrStrings[i].LenStr());               //Output/Print array of strings one at a time
     iY=iY+16;
}
delete [] ptrStrings;
ReleaseDC(hOutput,hDC);
}


void btnLeftRightMid_OnClick(void)
{
HWND hOutput;
String s1,s2;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,800,100,325,250,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
s1=_T("George Washington Carver");
s2=s1.Left(6);
TextOut(hDC,20,20,s1.lpStr(),s1.LenStr());
TextOut(hDC,20,40,s2.lpStr(),s2.LenStr());
s2=s1.Mid(8,10);
TextOut(hDC,20,60,s2.lpStr(),s2.LenStr());
s2=s1.Right(6);
TextOut(hDC,20,80,s2.lpStr(),s2.LenStr());
ReleaseDC(hOutput,hDC);
}


void btnInStr_OnClick(void)
{
String s1,s2,s3;
double dblPi;
HWND hOutput;
int iOffset;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,550,575,525,200,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
s1=_T("InStr('Some Text') Locates The One Based Offset Of Where Something Is At.");
TextOut(hDC,0,0,s1.lpStr(),s1.LenStr());
s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
TextOut(hDC,0,16,s1.lpStr(),s1.LenStr());
iOffset=s1.InStr(_T("real"),false);
s3 =_T("The Offset Of 'real' In The Above String Is ");
s3 = s3 + s2.CStr(iOffset) + _T('.');
TextOut(hDC,0,32,s3.lpStr(),s3.LenStr());
s1=_T("Note In The Above Code We Used The Case Insensitive Version Of InStr().");
TextOut(hDC,0,48,s1.lpStr(),s1.LenStr());
s1=_T("We Also Learned How To Use .CStr().");
TextOut(hDC,0,64,s1.lpStr(),s1.LenStr());
s1=_T("Let's See If We Can Remove() 'Real' From Our 1st String!");
TextOut(hDC,0,80,s1.lpStr(),s1.LenStr());
s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
TextOut(hDC,0,96,s1.lpStr(),s1.LenStr());
s1.Remove(_T("Real"),true);
TextOut(hDC,0,112,s1.lpStr(),s1.LenStr());
s1=_T("Dieses Program funktioniert aber sehr gut, nicht wahr?");
TextOut(hDC,0,128,s1.lpStr(),s1.LenStr());
dblPi=3.14159;
s1=_T("dblPi = ");
s1=s1+s2.CStr(dblPi);
TextOut(hDC,0,144,s1.lpStr(),s1.LenStr());
ReleaseDC(hOutput,hDC);
}


long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
switch(LOWORD(Wea->wParam))
{
  case IDC_CONCATENATION:
    btnConcatenation_OnClick();
    break;
  case IDC_PARSE:
    btnParse_OnClick();
    break;
  case IDC_LEFT_RIGHT_MID:
    btnLeftRightMid_OnClick();
    break;
  case IDC_INSTR:
    btnInStr_OnClick();
    break;
}

return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
DestroyWindow(Wea->hWnd);
PostQuitMessage(0);

return 0;
}


void AttachEventHandlers(void)
{
EventHandler[0].Code=WM_CREATE,    EventHandler[0].fnPtr=fnWndProc_OnCreate;
EventHandler[1].Code=WM_COMMAND,   EventHandler[1].fnPtr=fnWndProc_OnCommand;
EventHandler[2].Code=WM_CLOSE,     EventHandler[2].fnPtr=fnWndProc_OnClose;
}


long __stdcall fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
WndEventArgs Wea;

for(unsigned int i=0; i<3; i++)
{
     if(EventHandler[i].Code==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
}

return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int __stdcall WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
TCHAR szClassName[]=_T("StrDemo");
WNDCLASSEX wc;
MSG messages;
HWND hWnd;

AttachEventHandlers();
wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
wc.cbSize=sizeof (WNDCLASSEX);               wc.style=CS_DBLCLKS;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
RegisterClassEx(&wc);
hWnd=CreateWindow(szClassName,_T("StrDemo"),WS_OVERLAPPEDWINDOW,20,20,350,210,HWND_DESKTOP,0,hIns,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
  TranslateMessage(&messages);
  DispatchMessage(&messages);
}

return messages.wParam;
}

/*
fp=_tfopen(_T("Output.txt"),TEXT("w"));
_ftprintf(fp,_T("Entering fnWndProc_OnCommand()\n"));

_ftprintf(fp,_T("Leaving fnWndProc_OnCommand()\n"));
fclose(fp);
*/




//Strings.cpp
//#define   _UNICODE
#include  <tchar.h>
#include  <stdlib.h>
#include  <stdio.h>
#include  <string.h>
#include  "Strings.h"


String::~String()   //String Destructor
{
delete [] pStrBuffer;
}


String::String()    //Uninitialized Constructor
{
pStrBuffer=new TCHAR[MINIMUM_ALLOCATION];
pStrBuffer[0]=_T('\0');
this->iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


String::String(const TCHAR ch)  //Constructor: Initializes with TCHAR
{
pStrBuffer=new TCHAR[MINIMUM_ALLOCATION];
pStrBuffer[0]=ch;
pStrBuffer[1]=_T('\0');
iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


String::String(const TCHAR* pStr)  //Constructor: Initializes with TCHAR*
{
int iLen,iNewSize;

iLen=_tcslen(pStr);
iNewSize=(iLen/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
pStrBuffer=new TCHAR[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
_tcscpy(pStrBuffer,pStr);
}


String::String(const String& s)  //Constructor Initializes With Another String, i.e., Copy Constructor
{
int iLen,iNewSize;

iLen=_tcslen(s.pStrBuffer);
iNewSize=(iLen/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
this->pStrBuffer=new TCHAR[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
_tcscpy(this->pStrBuffer,s.pStrBuffer);
}


String::String(const int iSize)  //Constructor Creates String With Custom Sized
{                                //Buffer (rounded up to paragraph boundary)
int iNewSize;

iNewSize=(iSize/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
pStrBuffer=new TCHAR[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
}


String& String::operator=(const TCHAR ch)  //Overloaded operator = for assigning a TCHARacter to a String
{
this->pStrBuffer[0]=ch;
this->pStrBuffer[1]=_T('\0');

return *this;
}


String& String::operator=(const TCHAR* pStr)   //Constructor For If Pointer To Asciiz String Parameter
{
int iLen,iNewSize;

iLen=_tcslen(pStr);
if(iLen<this->iAllowableCharacterCount)
    _tcscpy(pStrBuffer,pStr);
else
{
    delete [] pStrBuffer;
    iNewSize=(iLen/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    _tcscpy(pStrBuffer,pStr);
}

return *this;
}


String& String::operator=(const String& strRight)    //Overloaded operator = for assigning
{                                                    //another String to a String
int iRightLen,iThisLen,iNewSize;
TCHAR* pNew;

if(this==&strRight)
    return *this;
iRightLen=_tcslen(strRight.pStrBuffer);
iThisLen=_tcslen(this->pStrBuffer);
if(iRightLen < this->iAllowableCharacterCount)
    _tcscpy(pStrBuffer,strRight.pStrBuffer);
else
{
    if(iThisLen) //There Is Something Stored In This!
    {
       iNewSize=iThisLen+iRightLen+1;
       iNewSize=(iNewSize/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       pNew=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pNew,pStrBuffer);
       _tcscat(pNew,strRight.pStrBuffer);
       delete [] this->pStrBuffer;
       pStrBuffer=pNew;
    }
    else
    {
       iNewSize=(iRightLen/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       delete [] this->pStrBuffer;
       this->pStrBuffer=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pStrBuffer,strRight.pStrBuffer);
    }
}

return *this;
}


bool String::operator==(const String strCompare)
{
if(_tcscmp(this->pStrBuffer,strCompare.pStrBuffer)==0)  //strcmp
    return true;
else
    return false;
}


String& String::operator+(const TCHAR ch)      //Overloaded operator + (Puts TCHAR in String)
{
int iLen,iNewSize;
TCHAR* pNew;

iLen=_tcslen(this->pStrBuffer);
if(iLen<this->iAllowableCharacterCount)
{
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]=_T('\0');
}
else
{
    iNewSize=((this->iAllowableCharacterCount*EXPANSION_FACTOR)/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    pNew=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    _tcscpy(pNew,this->pStrBuffer);
    delete [] this->pStrBuffer;
    this->pStrBuffer=pNew;
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]=_T('\0');
}

return *this;
}


String& String::operator+(const TCHAR* pChar) //Overloaded operator + (Adds TCHAR literals
{                                             //or pointers to Asciiz Strings)
int iLen,iNewSize;
TCHAR* pNew;

iLen=_tcslen(this->pStrBuffer)+_tcslen(pChar);
if(iLen<this->iAllowableCharacterCount)
    _tcscat(this->pStrBuffer,pChar);
else
{
    iNewSize=(iLen*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    pNew=new TCHAR[iNewSize];
    this->iAllowableCharacterCount = iNewSize-1;
    _tcscpy(pNew,this->pStrBuffer);
    delete [] pStrBuffer;
    _tcscat(this->pStrBuffer,pChar);
    this->pStrBuffer=pNew;
}

return *this;
}


String& String::operator+(const String& strRight)  //Overloaded operator + Adds Another String
{                                                  //to the left operand
int iLen,iNewSize;
TCHAR* pNew;

iLen=_tcslen(this->pStrBuffer) + _tcslen(strRight.pStrBuffer);
if(iLen < this->iAllowableCharacterCount)
{
    if(this->pStrBuffer)
       _tcscat(this->pStrBuffer,strRight.pStrBuffer);
    else
       _tcscpy(this->pStrBuffer,strRight.pStrBuffer);
}
else
{
    if(this->pStrBuffer)
    {
       iNewSize=(iLen*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       pNew=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       _tcscat(pNew,strRight.pStrBuffer);
       this->pStrBuffer=pNew;
    }
    else
    {
       iNewSize=(iLen*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       pNew=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pNew,strRight.pStrBuffer);
       this->pStrBuffer=pNew;
    }
}

return *this;
}


String String::Left(unsigned int iNum)
{
unsigned int iLen,i,iNewSize;
String sr;

iLen=_tcslen(this->pStrBuffer);
if(iNum<iLen)
{
    iNewSize=(iNum*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new TCHAR[iNewSize];
    for(i=0;i<iNum;i++)
        sr.pStrBuffer[i]=this->pStrBuffer[i];
    sr.pStrBuffer[iNum]=_T('\0');
    return sr;
}
else
{
    sr=*this;
    return sr;
}
}


String String::Remove(const TCHAR ch)
{
unsigned int iLen,iNewSize;
TCHAR* pStr=NULL;
String sr;
TCHAR* p;
TCHAR* c;

iLen=_tcslen(this->pStrBuffer);
iNewSize=(iLen/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
pStr=new TCHAR[iNewSize];
sr.iAllowableCharacterCount=iNewSize-1;
c=this->pStrBuffer;
p=pStr;
while(*c)
{
  if(*c!=ch)
  {
     *p=*c;
     c++, p++;
     *p=_T('\0');
  }
  else
     c++;
}
sr=pStr;

return sr;
}


String String::Remove(const TCHAR* pToRemove, bool blnCaseSensitive)
{
int i,j,iParamLen,iReturn=0;
bool blnFound=false;

if(*pToRemove==0)
    return true;
iParamLen=_tcslen(pToRemove);
i=0, j=0;
do
{
  if(pStrBuffer[i]==0)
     break;
  if(blnCaseSensitive)
     iReturn=_tcsncmp(pStrBuffer+i,pToRemove,iParamLen);  //strncmp
  else
     iReturn=_tcsnicmp(pStrBuffer+i,pToRemove,iParamLen); //_strnicmp
  if(iReturn!=0)
  {
     if(blnFound)
        pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
  else   //made a match
  {
     blnFound=true;
     i=i+iParamLen;
     pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
}while(1);
if(blnFound)
    pStrBuffer[i-iParamLen]=_T('\0');
String sr=pStrBuffer;

return sr;
}


String String::Right(unsigned int iNum)  //Returns Right$(strMain,iNum)
{
unsigned int iLen,i,j,iNewSize;
String sr;

iLen=_tcslen(this->pStrBuffer);
if(iNum<iLen)
{
    iNewSize=(iNum*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new TCHAR[iNewSize];
    j=0;
    for(i=iLen-iNum;i<=iLen;i++)
    {
        _tprintf(_T("%u\t%u\t%c\n"),i,j,pStrBuffer[i]);
        sr.pStrBuffer[j]=this->pStrBuffer[i];
        j++;
    }
    sr.pStrBuffer[iNum]=_T('\0');
    return sr;
}
else
{
    sr=*this;
    return sr;
}
}


String String::Mid(unsigned int iStart, unsigned int iCount)
{
unsigned int iLen,i,j,iNewSize;
String sr;

if(!iStart || !iCount)
    return sr;
iLen=_tcslen(this->pStrBuffer);
if(iStart>iLen)
    return sr;
if(iStart+iCount>iLen)
    iCount=iLen-iStart+1;
iNewSize=(iCount*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
sr.iAllowableCharacterCount=iNewSize-1;
sr.pStrBuffer=new TCHAR[iNewSize];
j=0;
for(i=iStart-1;i<iStart+iCount-1;i++)
{
     sr.pStrBuffer[j]=this->pStrBuffer[i];
     j++;
}
sr.pStrBuffer[iCount]=_T('\0');

return sr;
}


int String::InStr(const TCHAR ch)
{
int iLen,i;

iLen=_tcslen(this->pStrBuffer);
for(i=0;i<iLen;i++)
{
     if(this->pStrBuffer[i]==ch)
        return (i+1);
}

return 0;
}


int String::InStr(const TCHAR* pStr, bool blnCaseSensitive)
{
int i,iParamLen,iRange;

if(*pStr==0)
    return 0;
iParamLen=_tcslen(pStr);
iRange=_tcslen(pStrBuffer)-iParamLen;
if(iRange>=0)
{
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(_tcsncmp(pStrBuffer+i,pStr,iParamLen)==0)   //strncmp
              return i+1;
        }
        else
        {
           if(_tcsnicmp(pStrBuffer+i,pStr,iParamLen)==0)  //_strnicmp
              return i+1;
        }
    }
}

return 0;
}


int String::InStr(const String& s, bool blnCaseSensitive)
{
int i,iParamLen,iRange,iLen;

iLen=_tcslen(s.pStrBuffer);
if(iLen==0)
    return 0;
iParamLen=iLen;
iRange=_tcslen(pStrBuffer)-iParamLen;
if(iRange>=0)
{
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(_tcsncmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0)  //strncmp
              return i+1;
        }
        else
        {
           if(_tcsnicmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0) //_strnicmp
              return i+1;
        }
    }
}

return 0;
}


String String::LTrim()
{
unsigned int i,iCt=0,iLenStr;

iLenStr=this->LenStr();
for(i=0;i<iLenStr;i++)
{
     if(pStrBuffer[i]==32||pStrBuffer[i]==9)
        iCt++;
     else
        break;
}
if(iCt)
{
    for(i=iCt;i<=iLenStr;i++)
        pStrBuffer[i-iCt]=pStrBuffer[i];
}

return *this;
}


String String::RTrim()
{
unsigned int iCt=0, iLenStr;

iLenStr=this->LenStr()-1;
for(unsigned int i=iLenStr; i>0; i--)
{
     if(this->pStrBuffer[i]==32 || this->pStrBuffer[i]==9)
        iCt++;
     else
        break;
}
this->pStrBuffer[this->LenStr()-iCt]=0;

return *this;
}


String String::Trim()
{
this->LTrim();
this->RTrim();

return *this;
}


unsigned int String::ParseCount(const TCHAR c)  //returns one more than # of
{                                               //delimiters so it accurately
unsigned int iCtr=0;                           //reflects # of strings delimited
TCHAR* p;                                      //by delimiter.

p=this->pStrBuffer;
while(*p)
{
  if(*p==c)
     iCtr++;
  p++;
}

return ++iCtr;
}


void String::Parse(String* pStr, TCHAR delimiter)
{
unsigned int i=0;
TCHAR* pBuffer=0;
TCHAR* c;
TCHAR* p;

pBuffer=new TCHAR[this->LenStr()+1];
if(pBuffer)
{
    p=pBuffer;
    c=this->pStrBuffer;
    while(*c)
    {
     if(*c==delimiter)
     {
        pStr[i]=pBuffer;
        p=pBuffer;
        i++;
     }
     else
     {
        *p=*c;
        p++;
        *p=0;
     }
     c++;
    }
    pStr[i]=pBuffer;
    delete [] pBuffer;
}
}


String String::Parse(TCHAR delimiter, unsigned int iNum)
{
unsigned int iCtr=1,iCtStrs=0;
TCHAR* pBuffer=0;
String sr;
TCHAR* c;
TCHAR* p;

if(!iNum)
    return sr;
iCtStrs=this->ParseCount(delimiter);
if(iNum>iCtStrs)
    return sr;
pBuffer=new TCHAR[this->LenStr()+1];
if(pBuffer)
{
    pBuffer[0]=_T('\0');
    p=pBuffer,  c=this->pStrBuffer;
    if(iCtr==iNum)
    {
       while(*c)
       {
          if(*c==delimiter)
             break;
          *p=*c;
          p++, c++;
          *p=_T('\0');
       }
       sr=pBuffer;
       return sr;
    }
    while(*c)
    {
       if(*c==delimiter)
       {
          iCtr++;
          if(iCtr==iNum)
             break;
       }
       c++;
    }
    c++;
    while(*c)
    {
       if(*c==delimiter)
          break;
       *p=*c;
       p++, c++;
       *p=_T('\0');
    }
    sr=pBuffer;
}

return sr;
}


int String::iVal()
{
return _ttoi(this->pStrBuffer);  //atoi
}


String String::CStr(const int iNum)
{
String sr;
_stprintf(sr.pStrBuffer,_T("%d"),iNum);
return sr;
}


String String::CStr(const unsigned int iNum)
{
String sr;
_stprintf(sr.pStrBuffer,_T("%u"),iNum);
return sr;
}


String String::CStr(const short int iNum)
{
String sr;
_stprintf(sr.pStrBuffer,_T("%d"),iNum);
return sr;
}


String String::CStr(const double dblNum)
{
String sr(32);
_stprintf(sr.pStrBuffer,_T("%f"),dblNum);
return sr;
}


int String::LenStr(void)
{
return _tcslen(this->pStrBuffer);
}


TCHAR* String::lpStr()
{
return pStrBuffer;
}

void String::Print(bool blnCrLf)
{
_tprintf(_T("%s"),pStrBuffer);
if(blnCrLf)
    _tprintf(_T("\n"));
}




//frmOutput.cpp  All this file consists of is the Window Procedure for the frmOutput
//Window Class.  A window of class frmOutput is created whose sole purpose is to be
//printed to with TextOut().  All default window procedure processing is ok so the
//window procedure frmOutput doesn't have to do anything on its own other than exist
//and pass all messages received onto DefWindowProc().  It has to exist because its
//referenced in the Class Registration code in fnWndProc_OnCreate() where the frmOutput
//class is registered. 
#include <windows.h>
#include "frmOutput.h"

long __stdcall frmOutput(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
return (DefWindowProc(hwnd, msg, wParam, lParam));
}