TCLib Now Uses Msvcrt.dll

Started by Frederick J. Harris, July 07, 2016, 09:27:26 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

Big Doins in TCLib World!

     As a result of an exchange I had in a C++ forum I decided to download Mark Russinovich's Sysinternals "Process Explorer" utility...

www.sysinternals.com

...to examine the dlls loaded into various Windows' processes.  I had been laboring under the assumption that my use of TCLib in linking was removing the necessity of the loading of msvcrt.dll into my processes.  That's actually partly true.  Let me explain. 

     In the seminal work on this topic of eliminating the C Runtime library to minimize program size by Matt Pietrek in the January 2001 issue of "Microsoft Systems Journal" he states this...

Quote
What About the C++ Runtime Library DLL?

      Alert readers might say, "Hey Matt! Why don't you just use the DLL version of the runtime library?" In the past, I could make the argument that there was no consistently named version of the C++ runtime library DLL available on Windows® 95, Windows 98, Windows NT 3.51, Windows NT® 4.0, and so forth. Luckily, we've moved past those days, and in most cases you can rely on MSVCRT.DLL being available on your target machines.

      Making this switch and recompiling Hello.CPP, the resulting executable is now only 16KB. Not bad, but you can do better. More importantly, you're just shifting all of this unneeded code to someplace else (that is, to MSVCRT.DLL). In addition, when your program starts up, another DLL will have to be loaded and initialized. This initialization includes items like locale support, which you may not care about. If MSVCRT.DLL suits your needs, then by all means use it. However, I believe that using a stripped-down, statically linked runtime library still has merit.

      I may be tilting at windmills here, but my e-mail conversations with readers show that I'm not alone. There are people out there who want the leanest possible code. In this day of writeable CDs, DVDs, and fast Internet connections, it's easy not to worry about code size. However, the best Internet connection I can get at home is only 24Kbps. I hate wasting time downloading bloated controls for a Web page.

      As a matter of principle, I want my code to have as small a footprint as possible. I don't want to load any extra DLLs that I don't really need. Even if I might need a DLL, I'll try to delayload it so that I don't incur the cost of loading it until I use the DLL. Delayloading is a topic I've described in previous columns, and I strongly encourage you to become familiar with it. See Under the Hood in the December 1998 issue of MSJ for starters.

     So perhaps I can be partly excused for my lack of knowledge.  It was my reading of the above that caused me to assume that msvcrt.dll wasn't being loaded if I used Matt's techniques.  Which as a matter of fact is absolutely true for console processes.  And that's all Matt Pietrek was really writing about, i.e., the use of such techniques as his LibCTiny.lib in small utility type programs.  It was I that came up with the idea that these techniques could be used to minimize the size of ALL C++ programs – including GUIs.  For the situation is simply this – msvcrt.dll seems to be loaded into every GUI process – even the very simplest ones that seemingly make no calls on stdio.h or string.h functions such as this very simplest one...


// cl Form1.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
// cl Form1.cpp /O1 /Os /GS- /link kernel32.lib user32.lib gdi32.lib
#define UNICODE        //  3,584 Bytes x64 UNICODE TCLib.lib
#define _UNICODE       // 84,992 Bytes With C Standard Library Loaded (LIBCMT.LIB)
#include <windows.h>
#include "tchar.h"

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
if(msg==WM_DESTROY)
{
    PostQuitMessage(0);
    return 0;
}

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

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
{
WNDCLASSEX wc={0};
MSG messages;
HWND hWnd;

wc.lpszClassName = _T("Form1");
wc.lpfnWndProc   = fnWndProc;
wc.cbSize        = sizeof(WNDCLASSEX);
wc.hInstance     = hInstance;
wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,_T("Form1"),_T("Form1"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
while(GetMessage(&messages,NULL,0,0))
{
    TranslateMessage(&messages);
    DispatchMessage(&messages);
}

return messages.wParam;
}


     When compiled with TCLib.lib these are the dlls Mark Russinovich's "Process Explorer" tells me are associated with it within its process....

Bcrypt.dll
Bcryptprimitives.dll
Clbcatq.dll
Combase.dll
Dwmapi.dll
Gdi32.dll
Imm32.dll
Kernel.appcore.dll
Kernel32.dll
KernelBase.dll
Locale.nls
Msctf.dll
Msvcrt.dll                               <<<<< !!!
Ntdll.dll
Ole32.dll
Oleacc.dll
Oleaccrc.dll
Oleaut32.dll
Profapi.dll
Rpcrt4.dll
Sechose.dll
SHCore.dll
SortDefault.nls
Sxs.dll
Tiptsf.dll
Twinapi.appcore.dll
Twinapi.dll
UIAutomationCore.dll
User32.dll
Userenv.dll
Uxtheme.dll

     Quite a list, isn't it?  And among those listed is good 'ole msvcrt.dll!  For comparison, here are the "Process Explorer" results for my Demo1.cpp which is just the canonical  Dennis Ritchie "Hello, World!" program compiled with the last version of TCLib.lib I presented here.  First the program...


// cl Demo1.cpp /O1 /Os /GS- TCLib.lib kernel32.lib
// 2,048 bytes x86 ASCII, 2,048 bytes x86 UNICODE VC15
// 3,072 bytes x64 ASCII, 3,072 bytes x64 UNICODE VC19
#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include "stdio.h"
#include "tchar.h"

int _tmain()
{
_tprintf(_T("Hello, World!\n"));
getchar();

return 0;
}
     

And here are the "Process Explorer" results...

Gdi32.dll
Imm32.dll
Kernel32.dll
KernelBase.dll
Localew.nls
Ntdll.dll
User32.dll

     Certainly a lot smaller!  Notably absent from the above list is msvcrt.dll.  So indeed, for console processes my use of TCLib.lib does greatly minimize program executable size while at the same time eliminating a large dll from being loaded into the process.  But for GUI apps msvcrt.dll seems to always be loaded.  At least for every program I've examined and certainly for all those linked with my TCLib.lib.  And by the way, I checked a PowerBASIC Console Compiler "Hello, World!" Console program and msvcrt.dll is being loaded into that, as well as a lot of other stuff more nearly like a GUI program.

     For me, this changes everything.  It's a game changer.  I've been struggling for months on end trying to come up with ways of duplicating functionality I needed from the C Runtime without its being loaded and here its been sitting in memory all the time just begging to be used!

     Its kind of like not wanting to get wet during a rainstorm so you go out and buy an umbrella and dutifully carry it around with you everywhere you go, then when it starts to finally rain you fail to open it and still get drenched even though you have the umbrella in your hand!  What's the sense?  If you've got the umbrella in your hand and you don't want to get wet why not open it?  If msvcrt.dll is default loaded into every GUI process and there isn't anything you can do about it then why not use it rather than struggling to write code that's already loaded into your process?

     By the way, and as something of an interesting aside, in every version of the PowerBASIC compilers I bought from PB Dll 6 and Console Compiler 2 circa 1998 or so onwards up to version 9 of PB Win Bob Zale included msvcrt.dll in the \bin subdirectory of his installations alongside his compilers themselves and other files in his compiler toolchain.  I do believe it was lacking in PB Win 10 and PB CC 6.  By that time though msvcrt.dll was an official Windows installation file accorded 'system protection' status.  So I believe it may be something of an open question as to how much of the PowerBASIC Compiler's features are dependent on the C Runtime.  Interesting question, I believe.   

continued...
  •  

Frederick J. Harris

     Way back in my first postings on this topic when I was still struggling with floating point issues and before I developed my FltToCh() function I dynamically loaded msvcrt.dll and obtained function pointers to several of the printf functions I needed.  At that point I stated that I was cheating by doing that, and that I would endeavor to come up with a workaround, which I eventually did (my FltToCh() function).  But now I'm thinking, "What's the point?"  Msvcrt.dll is loaded in any case, whether I load it or not.  What actually happened when I did a LoadLibrary() on msvcrt.dll in my early work is that the reference count on an already loaded dll was incremented.  The dll was already loaded.  Check out the docs on LoadLibrary() if you have any doubt about what I'm saying.  LoadLibrary() only loads a library if it is not already loaded.  Otherwise Windows simply increments the reference count on the already loaded object.

     To bring home to you my reader the significance of all this let me review and bring you up to speed on the issues.  Take for instance this simple C program to output the string ...

"My Name Is Fred And I Weight 214.5 Pounds And Am 5 Feet 11 Inches Tall!"


// cl Test1a.cpp
// 122,880 bytes
#include <stdio.h>

int main()
{
double dblWeight = 214.5;
int    iFeet     = 5;
int    iInches   = 11;
char   szName[]  = "Fred";
char   szBuffer[128];

sprintf(szBuffer,"My Name Is %s And I Weight %5.1f Pounds And Am %d Feet %d Inches Tall!",szName,dblWeight,iFeet,iInches);
printf("%s\n",szBuffer);
getchar();

return 0;
}


     The above code using the C Runtime will produce the desired result.  Note I did it a little circuitously by using sprintf and printf instead of just printf.  Sprintf writes the results to a specified buffer first rather than directly to the console.  But note further there are %s format specifiers for character strings, %d format specifiers for integral class numbers and %f format specifiers for 'real' numbers, i.e., floating points. 

     The above wouldn't work with my TCLib.lib without modifications.  The reason it wouldn't work is because I used Matt Pietrek's original sprintf/printf implementation which passed on all the processing to user32.dll's wvsprintf() which correctly interprets all format specifiers except those for floating point numbers.  My solution to that issue for TCLib, as you may recall, was to create a FltToCh() function which converted floating point numbers to either asci or wide character strings.  That function had a good number or parameters, but basically, one passed into it a buffer where the results were to be written, and the floating point number one wished to convert.  Then, once having converted the float to a string, one could output it using Matt's sprintf/printf code because now one had a variable type, i.e., %s, which could be processed by his 'simplified' but crippled function.  Here would be my TCLib interpretation of that...


// cl Test1b.cpp /O1 /Os /GS- TCLib.lib kernel32.lib
// 5,120 bytes
#include <windows.h>
#include "stdio.h"
extern "C" int _fltused = 1;

int main()
{
double dblWeight = 214.5;
int    iFeet     = 5;
int    iInches   = 11;
char   szName[]  = "Fred";
char   szBuffer[128];
char   szWeight[16];

FltToCh(szWeight,dblWeight,6,1,'.',true);
sprintf(szBuffer,"My Name Is %s And I Weight %s Pounds And Am %d Feet %d Inches Tall!",szName,szWeight,iFeet,iInches);
printf("%s\n",szBuffer);
getchar();

return 0;
}


     Please examine the code carefully to see the differences.  In Test1b I needed to create another buffer which I named szWeight to contain the converted result of dblWeight converted to a string.  That buffer was used in my FltToCh() function call.  Of course, in Test1a none of that was needed.  And note the sprintf() call now has a %s format specifier for a string rather than the %f for the double.  Now, if you are working on a real program that has no floating points or only a few, then the above protocol might not be overly arduous to employ considering the code size reductions my TCLib.lib makes for you.  But in my own work I ran into a major program of mine with not one or a few floating point output statements, but rather thousands and thousands of them!  I worked on it for three or four days converting them, and only got about half way through them all when I threw up my hands in despair and realized that this was a 'no go'.  It was time for Plan B.

     It would have been ridiculous to proceed.  The whole purpose of TCLib.lib was to minimize executable program size.  And due to the difficult situation with floating point outputs I was having to add thousands of extra lines of code to a program which I had hoped to make smaller by the use of my TCLib.lib!

     I had looked at Matt Pietrek's implementation of the printf family of functions which lacked floating point support many times with an eye to how they could be modified to enable floating point support just like the real printf functions in the C Runtime.  And I always knew I could do it.  But I also knew it would be a truly nasty programming job requiring a lot of code and many days of work.  I had studied the CRT sources and knew what was involved.  Even Microsoft hated to tackle it as evidenced by the article I gave a reference to much earlier something along the lines of  "The Great C Runtime Refactoring".  So every time I looked at it and thought about it I decided to just live with what I had.  But now I knew I had to do it, and I did.  It took about a week.  And right about when I had a working prototype - not yet refactored though, I came upon my realization or discovery or enlightenment that msvcrt.dll was loaded into every GUI Windows process!  So another wasted week I guess, because as soon as I realized that I immediately knew what I would do.  But before getting into that, let me present my working prototype of an sprintf implementation which would finally allow me to escape the nastiness of Matt Pietrek's partial and crippled implementation.  The following program uses my Parse function to parse the format string based on the '%' character which is a field delimiter.  The program then loops through each delimited string and chooses what to do based on whether a string - s, 64 bit unsigned - Iu, 64 bit signed - Id, 32 bit unsigned - u, 32 bit signed - d, floating point - f, or char - c is encountered.  The 'choosing what to do part' affects how the vararg parameters are removed from the stack.  So yea, its involved.  Here's the above program using my implementation of the format specifiers.  Its hundreds of lines of code, and I'm more grateful than I can tell that I don't have to use it now, but I just wanted you all to get an idea of the complexity of the issues.  So if you aren't interested in this code in any way but want to get on to my new TCLib.lib, then just skim over this...


// cl Test9.cpp /O1 /Os /GS- TCLib.lib kernel32.lib
#include <windows.h>
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include <stdarg.h>
typedef SSIZE_T ssize_t;
extern "C" int _fltused=1;


struct FmtSpecs
{
BOOL       blnRightJustified;
int        iDummy;
short int  iFmtSpecPos;
short int  iFieldWidth;         
short int  iType;               
short int  iDecimalPoints;
};

// FmtSpecs::iType
//=================
// 1 = int;
// 2 = __int64;
// 3 = unsigned int;
// 4 = unsigned __int64;
// 5 = double;
// 6 = string
// 7 = char


size_t ParseCount(const char* pString, char cDelimiter)
{
int iCtr=0;                        //reflects # of strings delimited
const char* p;                     //by delimiter.

p=pString;
while(*p)
{
  if(*p==cDelimiter)
     iCtr++;
  p++;
}

return ++iCtr;
}


void Parse(char** pStrings, const char* pDelimitedString, char cDelimiter, size_t iParseCount)
{
char* pBuffer=(char*)malloc(strlen(pDelimitedString)+1);
if(pBuffer)
{
    const char* c=pDelimitedString;
    char* p=pBuffer;
    while(*c)
    {
       if(*c==cDelimiter)
          *p=0;
       else
          *p=*c;
       p++, c++;
    }
    *p=0, p=pBuffer;
    for(size_t i=0; i<iParseCount; i++)
    {
        pStrings[i]=(char*)malloc(strlen(p)+1);
        strcpy(pStrings[i],p);
        p=p+strlen(pStrings[i])+1;
    }
}
}


int iGetFieldWidth(char* pChars, size_t iFound, BOOL blnRhtJus)
{
char szBuffer[4];

if(iFound==0)
    return 0;
if(iFound==1 && blnRhtJus==FALSE)
    return 0; 
memset(szBuffer,0,4); 
if(blnRhtJus)
{
    switch(iFound)
    {
      case 1:
        szBuffer[0]=pChars[0];
        szBuffer[1]=0;
        break;
      case 2:
        szBuffer[0]=pChars[0];
        szBuffer[1]=pChars[1];
        szBuffer[2]=0;
        break;
    }
}
else
{
    switch(iFound)
    {
      case 2:
        szBuffer[0]=pChars[1];
        szBuffer[1]=0;
        break;
      case 3:
        szBuffer[0]=pChars[1];
        szBuffer[1]=pChars[2];
        szBuffer[2]=0;
        break;
    }
}

return atoi(szBuffer);
}


bool blnFindFormatSpec(char* pFmtSpec, FmtSpecs& fs)
{
if(*pFmtSpec=='-')
    fs.blnRightJustified=FALSE;
else
    fs.blnRightJustified=TRUE; 
for(size_t i=0; i<7; i++)
{
     if(strncmp(pFmtSpec+i,"u",1)==0)
     {
        fs.iType=3;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
     if(strncmp(pFmtSpec+i,"d",1)==0)
     {
        fs.iType=1;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
     if(strncmp(pFmtSpec+i,"Iu",2)==0)
     {
        fs.iType=4;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i+1;
        return true;
     }
     if(strncmp(pFmtSpec+i,"Id",2)==0)
     {
        fs.iType=2;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i+1;
        return true;
     }
     if(strncmp(pFmtSpec+i,".",1)==0)
     {
        char szBuffer[4];
        memset(szBuffer,0,4);
        fs.iType=5;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        char* pChar;
        if(strncmp(pFmtSpec+i+2,"f",1)==0)  // it'll only be one digit then
        {
           pChar=pFmtSpec+i+1;
           szBuffer[0]=*pChar;
           szBuffer[1]=0;
           fs.iDecimalPoints=atoi(szBuffer);
           fs.iFmtSpecPos=i+2;
        }
        if(strncmp(pFmtSpec+i+3,"f",1)==0)  // it'll only be one digit then
        {
           pChar=pFmtSpec+i+1;
           szBuffer[0]=*pChar;
           szBuffer[1]=*(pChar+1);
           szBuffer[2]=0;
           fs.iDecimalPoints=atoi(szBuffer);
           fs.iFmtSpecPos=i+3;
        }
        return true;
     }
     if(strncmp(pFmtSpec+i,"f",1)==0)
     {
        fs.iType=5;
        fs.iFieldWidth=0;
        fs.iFmtSpecPos=i;
        return true;
       
     }
     if(strncmp(pFmtSpec+i,"s",1)==0)
     {
        fs.iType=6;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
     if(strncmp(pFmtSpec+i,"c",1)==0)
     {
        fs.iType=7;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
}

return false;
}




// FmtSpecs::iType
//=================
// 1 = int;
// 2 = __int64;
// 3 = unsigned int;
// 4 = unsigned __int64;
// 5 = double;
// 6 = string
// 7 = char


int MySPrintF(char* pBuffer, const char* pFmtStr, ...)
{
size_t iParseCount=0,iLen=0,iStrLen=0;
char** pFmtSpecs=NULL;
char szBuffer[80];
char*  pCh=NULL;
char szTmp1[80];
va_list args;
FmtSpecs fs;

memset(&fs,0,sizeof(fs));
iParseCount=ParseCount(pFmtStr,'%');
pFmtSpecs=(char**)malloc(iParseCount*sizeof(char*));
if(pFmtSpecs)
{
    Parse(pFmtSpecs,pFmtStr,'%',iParseCount);
    va_start(args, pFmtStr);
    for(size_t i=0; i<iParseCount; i++)
    {
        iLen=strlen(pFmtSpecs[i]);
        char* pStr=pFmtSpecs[i];
        if(i==0)
        {
           if(iLen)
              strcpy(pBuffer,pFmtSpecs[i]);
        }
        else
        {
           memset(&fs,0,sizeof(fs));
           blnFindFormatSpec(pFmtSpecs[i],fs);
           switch(fs.iType)
           {
              case 1: // %d -- 32 bit int
              {
                 itoa(va_arg(args,int),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }                 
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }
                 break;
              }
              case 2: // %Id -- 64 bit int
              {
                 _i64toa(va_arg(args,ssize_t),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }                 
                 break;
              }
              case 3: // %u  -- 32 bit unsigned int
              {
                 itoa(va_arg(args,unsigned int),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }                 
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }
                 break;
              }
              case 4: // %Iu  -- 64 bit unsigned int
              {
                 _ui64toa(va_arg(args,size_t),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }
                 break;
              }             
              case 5:
              {
                 if(fs.iFieldWidth==0)
                 {
                    FltToCh(szTmp1,va_arg(args,double),20,6,'.',false);
                    iStrLen=strlen(szTmp1);
                    for(size_t j=0; j<iStrLen; j++)
                    {
                        if(szTmp1[j]==' ')
                        {
                           szBuffer[j]=0;       
                           break;
                        }
                        else
                           szBuffer[j]=szTmp1[j];                     
                    }
                    strcat(pBuffer,szBuffer);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    if(fs.blnRightJustified==TRUE)
                       FltToCh(szTmp1,va_arg(args,double),fs.iFieldWidth+1,fs.iDecimalPoints,'.',true);
                    else
                       FltToCh(szTmp1,va_arg(args,double),fs.iFieldWidth+1,fs.iDecimalPoints,'.',false);
                    strcat(pBuffer,szTmp1);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }               
                 break;
              }
              case 6:   
              {
                 strcpy(szTmp1,va_arg(args,char*));
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {                     
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);           
                    }
                 }               
                 break;
              }
              case 7:
              {
                 char c=va_arg(args,char);
                 char* pChar=&c;
                 strcpy(szTmp1,pChar);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {                     
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);           
                    }
                 }         
                 break;
              }             
           }  // switch(fs.iType)
        }  // end if(i==0)       
    }
    for(size_t i=0; i<iParseCount; i++)
        free(pFmtSpecs[i]);   
    free(pFmtSpecs);
}

return 0;
}


int main()
{
int    iFeet     = 5;
int    iInches   = 11;
double dblWeight = 214.54321;
char   szName[]  = "Fred";
char   szBuffer[128];

MySPrintF(szBuffer, "My Name Is %s And I Weight %5.1f Pounds And Am %d Feet %d Inches Tall!\n", szName, dblWeight, iFeet, iInches);
printf("%s",szBuffer);
getchar();

return 0;
}

// My Name Is Fred And I Weight 214.5 Pounds And Am 5 Feet 11 Inches Tall!


continued...
  •  

Frederick J. Harris

#2
    I'd like to be able to tell you the above program could be built with all the code I've already provided in previous posts on the topic, but the sad fact is I had to add several more C Runtime functions to what I already had, namely these...


extern "C" char*    __cdecl itoa    (int             iNumber, char*      pszBuffer, int radix);
extern "C" wchar_t* __cdecl _itow   (int             iNumber, wchar_t*   pszBuffer, int radix);
extern "C" char*    __cdecl _i64toa (_int64          iNumber, char*      pszBuffer, int radix);
extern "C" wchar_t* __cdecl _i64tow (_int64          iNumber, wchar_t*   pszBuffer, int radix);
extern "C" char*    __cdecl _ui64toa(unsigned _int64 iNumber, char*      pszBuffer, int radix);
extern "C" wchar_t* __cdecl _ui64tow(unsigned _int64 iNumber, wchar_t*   pszBuffer, int radix);


     Those additions were necessary to convert %u, %d, %Iu and %Id numbers to asci/wide buffers, and I implemented them in terms of my FltToCh() function, which as you can see, became the cornerstone of a lot of my work.  Here they are if you want them...


//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, June 2016
//
//       cl itoa.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"
#include "stdio.h"

void SetNullChar(char* p)
{
while(true)
{
   if(*p==' ')
   {
      *p=0;
      break;
   }
   p++;
}
}

void SetNullWChar(wchar_t* p)
{
while(true)
{
   if(*p==L' ')
   {
      *p=0;
      break;
   }
   p++;
}
}

char* itoa(int iNumber, char* pszBuffer, int radix)
{
FltToCh(pszBuffer,(double)iNumber,12,0,'.',false);
SetNullChar(pszBuffer);
return pszBuffer;
}

wchar_t* _itow(int iNumber, wchar_t* pszBuffer, int radix)
{
FltToWch(pszBuffer,(double)iNumber,12,0,L'.',false);
SetNullWChar(pszBuffer);
return pszBuffer;
}

char* _i64toa(_int64 iNumber, char* pszBuffer, int radix)
{
FltToCh(pszBuffer,(double)iNumber,20,0,'.',false);
SetNullChar(pszBuffer);
return pszBuffer;
}

char* _ui64toa(unsigned _int64 iNumber, char* pszBuffer, int radix)
{
FltToCh(pszBuffer,(double)iNumber,20,0,'.',false);
SetNullChar(pszBuffer);
return pszBuffer;
}

wchar_t* _i64tow(_int64 iNumber, wchar_t* pszBuffer, int radix)
{
FltToWch(pszBuffer,(double)iNumber,20,0,L'.',false);
SetNullWChar(pszBuffer);
return pszBuffer;
}

wchar_t* _ui64tow(unsigned _int64 iNumber, wchar_t* pszBuffer, int radix)
{
FltToWch(pszBuffer,(double)iNumber,20,0,L'.',false);
SetNullWChar(pszBuffer);
return pszBuffer;
}


     With that you actually should be able to build that.  As I said I finally got that working right before my massive illumination on msvcrt.dll being loaded into every GUI process, so its not really done.  There's a lot of redundant code that could likely be refactored and so forth.  But I doubt I'll ever make any further use or refinement of it, as I now intend to use msvcrt.dll for whatever I need.

     So the conclusion I've come to is this.  I might as well just do a LoadLibrary() on msvcrt.dll to get the HINSTANCE of the process (which is already loaded into my process by Windows) so I can make GetProcAddress() calls on whatever C Runtime functionality I need.  What I did was simply redefine every stdio function I needed as a function pointer instead of a function, thereby obtaining any C Runtime functionality I needed.  For example, the use of the symbol 'printf' defined as a function pointer is exactly the same as the use of the real function printf. 

     One might further reason that one might just as well do that for every C Runtime function I finally implemented myself in my last TCLib postings, i.e., all the string.h functions too.  Reality sets in there and it didn't work.  But it absolutely worked for everything I needed from stdio.  Here's the new and modified code, for example, for  crt_win_a.cpp, which is my implementation of the WinMainCRTStartup() function, which is actually the entry point for a Win32/64 asci GUI process...   


//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
extern "C" void __cdecl InitStdio(HINSTANCE);

extern "C" void __cdecl WinMainCRTStartup(void)
{
HINSTANCE hLib=NULL;
int iReturn;

hLib=LoadLibrary("msvcrt.dll");
if(hLib)
{
    InitStdio(hLib);
    iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
    FreeLibrary(hLib);   
}
ExitProcess(iReturn);
}


     You can see above I do a LoadLibrary() on msvcrt.dll which doesn't really load the library into memory because its already there, but it does return to me the HINSTANCE of the library.  With that I call my InitStdio(hLib) function passing the HINSTANCE of msvcrt.dll, which is where the 'magic' occurs.  Here is that, which you might find interesting (it's a bit bizarre, I admit!)...


//=========================================================================================================
//                                             InitStdio.cpp
//                                         Fred Harris, June 2016
//
//         cl InitStdio.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=========================================================================================================
#include <windows.h>

struct      FILE
{
char*      _ptr;
int        _cnt;
char*      _base;
int        _flag;
int        _file;
int        _charbuf;
int        _bufsiz;
char*      _tmpfname;
};

extern "C"  void InitStdio(HINSTANCE hLib);

//          From stdio.h
char        (__cdecl* getchar )(                                               );
FILE*       (__cdecl* fopen   )(const char* pszFile, const char* pszMode       );
FILE*       (__cdecl* _wfopen )(const wchar_t* pszFile, const wchar_t* pszMode );
int         (__cdecl* printf  )(const char* pFormat, ...                       );
int         (__cdecl* wprintf )(const wchar_t* pFormat, ...                    );
int         (__cdecl* fprintf )(FILE* fp, const char* format, ...              );
int         (__cdecl* fwprintf)(FILE* fp, const wchar_t* format, ...           );
int         (__cdecl* fscanf  )(FILE* fp, const char* pFormat, ...             );
int         (__cdecl* fwscanf )(FILE* fp, const wchar_t* pFormat, ...          );
int         (__cdecl* sprintf )(char* buffer, const char* format, ...          );
int         (__cdecl* swprintf)(wchar_t* buffer, const wchar_t* format, ...    );
char*       (__cdecl* fgets   )(char* pBuffer, int iSize, FILE* fp             );
wchar_t*    (__cdecl* fgetws  )(wchar_t* pBuffer, int iSize, FILE* fp          );
void        (__cdecl* rewind  )(FILE* fp                                       );
int         (__cdecl* fclose  )(FILE* fp                                       );

void InitStdio(HINSTANCE hLib)
{
//          From stdio.h
getchar  = (char     (__cdecl*)(                                              )) GetProcAddress(hLib,"getchar" );
fopen    = (FILE*    (__cdecl*)(const char* pszFile, const char* pszMode      )) GetProcAddress(hLib,"fopen"   );
_wfopen  = (FILE*    (__cdecl*)(const wchar_t* pszFile, const wchar_t* pszMode)) GetProcAddress(hLib,"_wfopen" );
printf   = (int      (__cdecl*)(const char* pFormat,    ...                   )) GetProcAddress(hLib,"printf"  );
wprintf  = (int      (__cdecl*)(const wchar_t* pFormat, ...                   )) GetProcAddress(hLib,"wprintf" );
fprintf  = (int      (__cdecl*)(FILE* fp, const char* format, ...             )) GetProcAddress(hLib,"fprintf" );
fwprintf = (int      (__cdecl*)(FILE* fp, const wchar_t* format, ...          )) GetProcAddress(hLib,"fwprintf");
fscanf   = (int      (__cdecl*)(FILE* fp, const char* pFmt, ...               )) GetProcAddress(hLib,"fscanf"  );
fwscanf  = (int      (__cdecl*)(FILE* fp, const wchar_t* pFmt, ...            )) GetProcAddress(hLib,"fwscanf" );
sprintf  = (int      (__cdecl*)(char* buffer, const char* pFormat, ...        )) GetProcAddress(hLib,"sprintf" );
swprintf = (int      (__cdecl*)(wchar_t* buffer, const wchar_t* pFormat, ...  )) GetProcAddress(hLib,"swprintf");
fgets    = (char*    (__cdecl*)(char* pBuffer, int iSize, FILE* fp            )) GetProcAddress(hLib,"fgets"   );
fgetws   = (wchar_t* (__cdecl*)(wchar_t* pBuffer, int iSize, FILE* fp         )) GetProcAddress(hLib,"fgetws"  );
rewind   = (void     (__cdecl*)(FILE* fp                                      )) GetProcAddress(hLib,"rewind"  );
fclose   = (int      (__cdecl*)(FILE* fp                                      )) GetProcAddress(hLib,"fclose"  );
}


     So what's going on in the code above is that right after the FILE struct is defined I define a mess of function pointers with names exactly like the stdio.h functions I desperately need such as printf, wprintf, sprintf, etc.  Then in the InitStdio() function I do GetProcAddress() on all those functions exported from msvcrt.dll.  The use of those function pointers will be indistinguishable from the use of the real functions of the same name in msvcrt.dll. 

     It didn't work with the string.h functions.  Apparently, MS VC knew I was up to some kind of monkey business.  The compiler apparently has built in definitions of the string.h functions, for, when I tried to pull that trick above, it complained and pointed out to me the exact line in string.h where the entity I was trying to re-define as a function pointer was actually a function of that same name.  I thought that was curious being as I wasn't including the version of string.h that came with my compiler.  So I decided to temporarily remove the string.h that came with my compiler to see what it would do.  Guess what?  Since it couldn't find that string.h that came with that compiler installation, it looked for another installation!  On the particular box I was using at that time I had both VC6 and VC9 installed (VS 6 and VS 2008).  So my next compile it found the string.h from my VC6 installation, and reported to me the exact line number in that string.h file where the symbol in question was defined as a function.  So at that point, really impressed with the determination of the compiler to prevent me from doing what I wanted, I realized that its determination was greater than mine, so I gave up.  I will admit it would have been interesting to see what it would do if I removed that string.h file too! J  Maybe start searching for GCC installations!

     So I just used the string.h and stdlib.h functions of my previous work, none of which amounted to more than a dozen or so lines of code, with many of them only a line or two, and called it 'good enough'.  The net result of this change on code size is that any program using floating points will end up being about 1.5 k smaller than my previous versions (those FltToCh() functions I had to implement involved several hundreds of lines of code).  Very simple "Hello, World!" type programs not using floating points may be a half k to a k larger, presumably due to the overhead of calling InitStdio(). 

     I hate to have to do this, but I'll post all the required code.  A lot of it is exactly the same as code I've already posted, so that's why I'm saying I hate to have to post it all.  But enough has changed to make it problematic to just post an item here or an item there that has changed.  I may miss something.  Or you might get a wrong version of something or other from my previous posts or not know where to find something.  And I wasted a couple hours trying to find all the bits and pieces and provide links as to where they can be found.  And it was horrible enough finding stuff that I decided to post everything afresh.  So you'll need to grab crt_win_a.cpp and InitStdio.cpp as posted just above.  The new TCLib.mak file is this....


PROJ       = TCLib

OBJS       = crt_con_a.obj crt_con_w.obj crt_win_a.obj crt_win_w.obj InitStdio.obj InitMath.obj\
             crt_dll.obj newdel.obj alloc.obj alloc2.obj allocsup.obj  strlen.obj memcpy.obj \
             strcpy.obj strncpy.obj strcmp.obj _stricmp.obj _strnicmp.obj _strrev.obj strncmp.obj \
             _atoi64.obj atof.obj abs.obj memset.obj strchr.obj strcat.obj memcmp.obj atol.obj
       
CC         = CL
CC_OPTIONS = /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN

$(PROJ).LIB: $(OBJS)
    LIB /NODEFAULTLIB /machine:x64 /OUT:$(PROJ).lib $(OBJS)

.CPP.OBJ:
    $(CC) $(CC_OPTIONS) $<


Here is InitMath.cpp, which just grabs pow() from Math.h...


//=========================================================================================================
//                                              InitMath.cpp
//                                         Fred Harris, July 2016
//
//         cl InitMath.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=========================================================================================================
#include <windows.h>
extern "C" void InitMath(HINSTANCE hLib);
double (__cdecl* pow)(const double dblBase, const double dblExponent);

void InitMath(HINSTANCE hLib)
{
pow = (double (__cdecl*)(const double dblBase, const double dblExponent))GetProcAddress(hLib,"pow");
}


continued...
  •  

Frederick J. Harris

#3
Here are the rest of the *.cpp files as I now have them in about the same order as in the make file...


//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_con_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath (HINSTANCE);
extern "C" int  __cdecl main();

extern "C" void __cdecl mainCRTStartup()
{
HINSTANCE hLib=NULL;
int iReturn=0;

hLib=LoadLibrary("msvcrt.dll");
if(hLib)
{
    InitStdio(hLib);
    InitMath (hLib);
    iReturn=main();
    FreeLibrary(hLib);   
}
ExitProcess(iReturn);
}



//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_con_w.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);
extern "C" int __cdecl  wmain();

extern "C" void __cdecl wmainCRTStartup()
{
HINSTANCE hLib=NULL;
int iReturn;

hLib=LoadLibrary("msvcrt.dll");
if(hLib)
{
    InitStdio(hLib);
    InitMath (hLib);
    iReturn=wmain();
    FreeLibrary(hLib);   
}
ExitProcess(iReturn);
}
 


//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);

extern "C" void __cdecl WinMainCRTStartup(void)
{
HINSTANCE hLib=NULL;
int iReturn;

hLib=LoadLibrary("msvcrt.dll");
if(hLib)
{
    InitStdio(hLib);
    InitMath (hLib);   
    iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
    FreeLibrary(hLib);   
}
ExitProcess(iReturn);
}



//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_w.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int);
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);

extern "C" void __cdecl wWinMainCRTStartup(void)
{
HINSTANCE hLib=NULL;
int iReturn;

hLib=LoadLibrary("msvcrt.dll");
if(hLib)
{
    InitStdio(hLib);
    InitMath (hLib);
    iReturn = wWinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
    FreeLibrary(hLib);   
}
ExitProcess(iReturn);
}



// cl crt_dll.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
extern BOOL WINAPI DllMain(HINSTANCE hDllHandle, DWORD dwReason, LPVOID lpreserved);
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);

extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE  hDllHandle, DWORD dwReason, LPVOID  lpreserved)
{
HINSTANCE hLib=NULL;
BOOL retcode=FALSE;

hLib=LoadLibrary("msvcrt.dll");
if(hLib)
{
    InitStdio(hLib);
    InitMath (hLib);
    retcode = DllMain(hDllHandle, dwReason, lpreserved);
    FreeLibrary(hLib);   
}

return retcode;
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//
//                          LIBCTINY -- Matt Pietrek 2001
//                           MSDN Magazine, January 2001
//
//                              With Help From Mike_V
//                       
//                           By Fred Harris, January 2016
//
//                    cl newdel.cpp /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>

void* __cdecl operator new(size_t s)
{
return HeapAlloc(GetProcessHeap(), 0, s);
}

void  __cdecl operator delete(void* p)
{
HeapFree(GetProcessHeap(), 0, p);
}

void* operator new [] (size_t s)
{
return HeapAlloc(GetProcessHeap(), 0, s);
}

void operator delete [] (void* p)
{
HeapFree(GetProcessHeap(), 0, p);
}



//===============================================================
//    Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                 By Fred Harris, January 2016
//
//     cl alloc.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================
#include <windows.h>
#include "malloc.h"

void* __cdecl malloc(size_t size)
{
return HeapAlloc(GetProcessHeap(), 0, size);
}

void __cdecl free(void* pMem)
{
HeapFree(GetProcessHeap(), 0, pMem);
}
 


//===============================================================
//    Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                 By Fred Harris, January 2016
//
// cl alloc2.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================
#include <windows.h>
#include "malloc.h"

void* __cdecl realloc(void* pMem, size_t size)
{
if(pMem)
    return HeapReAlloc(GetProcessHeap(), 0, pMem, size);
else
    return HeapAlloc(GetProcessHeap(), 0, size);
}

void* __cdecl calloc(size_t nitems, size_t size)
{
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nitems * size);
}



//===============================================================
//    Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                 By Fred Harris, January 2016
//
// cl allocsup.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================
#include <windows.h>
#include "malloc.h"

void* __cdecl _nh_malloc(size_t size, int nhFlag)
{
return HeapAlloc(GetProcessHeap(), 0, size);
}

size_t __cdecl _msize(void* pMem)
{
return HeapSize(GetProcessHeap(), 0, pMem);
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strlen.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

size_t __cdecl strlen(const char* strSource)
{
return lstrlenA(strSource);
}

size_t __cdecl wcslen(const wchar_t* strSource)
{
return lstrlenW(strSource);
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
//#include "memory.h"    // for some reason you can't put the prototype for this in
                         // memory.h

extern "C" void* __cdecl memcpy(void* pDest, void* pSrc, size_t iCount)
{
char* pDestination=(char*)pDest;
char* pSource=(char*)pSrc;

for(size_t i=0; i<iCount; i++)
     pDestination[i]=pSource[i];

return pDest;
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include <string.h>

char* __cdecl strcpy(char* strDestination, const char* strSource)
{
return lstrcpyA(strDestination, strSource);
}

wchar_t* __cdecl wcscpy(wchar_t* strDestination, const wchar_t* strSource)
{
return lstrcpyW(strDestination, strSource);
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                 cl strncpy.cpp /O1 /Os /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"


char* __cdecl strncpy(char* strDest, const char* strSource, size_t iCount)   
{                                              // 3rd param is size_t
size_t iLenSrc=strlen(strSource);             // strlen returns size_t                                           
lstrcpynA(strDest,strSource,(int)iCount);     // lstrcpyn wants an int here for 3rd param                                           
strDest[iCount-1]=strSource[iCount-1];        // so try cast
if(iCount>iLenSrc)
{
    for(size_t i=iLenSrc; i<iCount; i++)
        strDest[i]=0;
}

return strDest;
}


wchar_t* __cdecl wcsncpy(wchar_t* strDest, const wchar_t* strSource, size_t iCount)   
{                                           // 3rd param is size_t
size_t iLen=wcslen(strSource);             // strlen returns size_t                                           
lstrcpynW(strDest,strSource,(int)iCount);  // lstrcpyn wants an int here for 3rd param                                           
strDest[iCount-1]=strSource[iCount-1];     // so try cast
if(iCount>iLen)
{
    for(size_t i=iLen; i<iCount; i++)
        strDest[i]=0;
}

return strDest;
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strcmp.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

int __cdecl strcmp(const char* string1, const char* string2)   
{
return lstrcmpA(string1,string2);
}

int __cdecl wcscmp(const wchar_t* string1, const wchar_t* string2)   
{
return lstrcmpW(string1,string2);
}



//===============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//       cl strncmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"

int __cdecl strncmp(const char* str1, const char* str2, size_t count)
{
for(size_t i=0; i<count; i++)
{
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
}

return 0;
}

int __cdecl wcsncmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
for(size_t i=0; i<count; i++)
{
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
}

return 0;
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl _stricmp.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

int __cdecl _stricmp(const char* string1, const char* string2)   
{
return lstrcmpiA(string1,string2);
}

int __cdecl _wcsicmp(const wchar_t* string1, const wchar_t* string2)   
{
return lstrcmpiW(string1,string2);
}




// _strnicmp.cpp
//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//     cl _strnicmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"

int __cdecl _strnicmp(const char* str1, const char* str2, size_t count)
{
size_t iLen1=strlen(str1);
size_t iLen2=strlen(str2);
if(count>iLen1)
    return -1;
if(count>iLen2)
    return 1;
char* pStr1=(char*)malloc(count+1);
char* pStr2=(char*)malloc(count+1);
strncpy(pStr1,str1,count);
strncpy(pStr2,str2,count);
pStr1[count]=0;
pStr2[count]=0;
int iReturn=lstrcmpiA(pStr1,pStr2);
free(pStr1);
free(pStr2);

return iReturn;
}

int __cdecl _wcsnicmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
size_t iLen1=wcslen(str1);
size_t iLen2=wcslen(str2);
if(count>iLen1)
    return -1;
if(count>iLen2)
    return 1;
wchar_t* pStr1=(wchar_t*)malloc(count*2+2);
wchar_t* pStr2=(wchar_t*)malloc(count*2+2);
wcsncpy(pStr1,str1,count);
wcsncpy(pStr2,str2,count);
pStr1[count]=0;
pStr2[count]=0;
int iReturn=lstrcmpiW(pStr1,pStr2);
free(pStr1);
free(pStr2);

return iReturn;
}



//========================================================
// Developed As An Addition To Matt Pietrek's LibCTiny.lib
//              By Fred Harris, January 2016
//
//     cl _strrev.cpp /Oi /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================
#include <windows.h>
#include "string.h"

char* __cdecl _strrev(char* pStr)
{
size_t iLen,iHalf;
char a,b;

iLen=strlen(pStr), iHalf=iLen/2;
for(size_t i=0; i<iHalf; i++)
{
     a=pStr[i], b=pStr[iLen-i-1];
     pStr[i]=b, pStr[iLen-i-1]=a;
}

return pStr;
}

wchar_t* __cdecl _wcsrev(wchar_t* pStr)
{
size_t iLen,iHalf;
wchar_t a,b;

iLen=wcslen(pStr), iHalf=iLen/2;
for(size_t i=0; i<iHalf; i++)
{
     a=pStr[i], b=pStr[iLen-i-1];
     pStr[i]=b, pStr[iLen-i-1]=a;
}

return pStr;
}



//===============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//       cl strncmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"

int __cdecl strncmp(const char* str1, const char* str2, size_t count)
{
for(size_t i=0; i<count; i++)
{
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
}

return 0;
}

int __cdecl wcsncmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
for(size_t i=0; i<count; i++)
{
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
}

return 0;
}



//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//       cl _atoi64.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"


_int64 __cdecl _atoi64(const char* pStr)
{
char c,cNeg=NULL;           // c holds the char; cNeg holds the '-' sign.
_int64 lTotal=0;            // The running total.

while(*pStr==32 || *pStr==8)
    pStr++; 
if(*pStr=='-')
{
    cNeg='-';
    pStr++;
}
while(*pStr)
{
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
}
if(cNeg=='-')               // If we have a negative sign, convert the value.
    lTotal=-lTotal;

return lTotal;
}


_int64 __cdecl _wtoi64(const wchar_t* pStr)
{
wchar_t c,cNeg=NULL;        // c holds the char; cNeg holds the '-' sign.
_int64 lTotal=0;            // The running total.

while(*pStr==32 || *pStr==8)
    pStr++; 
if(*pStr==L'-')
{
    cNeg=L'-';
    pStr++;
}
while(*pStr)
{
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
}
if(cNeg==L'-')              // If we have a negative sign, convert the value.
    lTotal=-lTotal;

return lTotal;
}



//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, May 2016
//
//        cl atof.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include <windows.h>
#include "stdlib.h"
typedef SSIZE_T ssize_t;

double __cdecl atof(const char* pStr)
{
ssize_t lTotal   = 0;
char* pDecPt     = NULL;
char c,cNeg      = NULL;
double dblReturn;
size_t iDiff;

while(*pStr==32 || *pStr==8 || *pStr==48)
    pStr++;
if(*pStr=='-')
{
    cNeg='-';
    pStr++;
}
while(*pStr)
{
    if(*pStr=='.')
    {
       pDecPt=(char*)pStr;
       pStr++;
    }
    else
    {
       if(*pStr>=48 && *pStr<=57)
       {
          c=*pStr++;
          lTotal=10*lTotal+(c-48); // Add this digit to the total.
       }
       else
          break;
    }   
}
if(pDecPt)
    iDiff=(int)(pStr-pDecPt-1);
else
    iDiff=0;
if(cNeg=='-')                  // If we have a negative sign, convert the value.
    lTotal=-lTotal;
dblReturn=(double)lTotal;
for(size_t i=0; i<iDiff; i++)
     dblReturn=dblReturn/10;

return dblReturn;
}

double __cdecl _wtof(const wchar_t* pStr)
{
ssize_t lTotal = 0;
wchar_t* pDecPt=NULL;
wchar_t c,cNeg=NULL;
double dblReturn;
size_t iDiff;

while(*pStr==32 || *pStr==8 || *pStr==48)
    pStr++;
if(*pStr==L'-')
{
    cNeg=L'-';
    pStr++;
}
while(*pStr)
{
    if(*pStr==L'.')
    {
       pDecPt=(wchar_t*)pStr;
       pStr++;
    }
    else
    {
       if(*pStr>=48 && *pStr<=57)
       {
          c=*pStr++;
          lTotal=10*lTotal+(c-48); // Add this digit to the total.
       }
       else
          break;   
    }
}
if(pDecPt)
    iDiff=(int)(pStr-pDecPt-1);
else
    iDiff=0;
if(cNeg==L'-')                  // If we have a negative sign, convert the value.
    lTotal=-lTotal;
dblReturn=(double)lTotal;
for(size_t i=0; i<iDiff; i++)
     dblReturn=dblReturn/10;

return dblReturn;
}



//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//         cl abs.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"

int __cdecl abs(int n)
{
if(n<0)
    return -n;
else
    return n;
}

_int64 __cdecl _abs64(__int64 n)
{
if(n<0)
    return -n;
else
    return n;
}

long labs(long n)
{
if(n<0)
    return -n;
else
    return n;
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memset.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include "memory.h"

void* __cdecl memset(void* p, int c, size_t count)
{
char* pCh=(char*)p;
for(size_t i=0; i<count; i++)
     pCh[i]=(char)c;
return p;
}

wchar_t* __cdecl wmemset(wchar_t* p, wchar_t c, size_t count)
{
for(size_t i=0; i<count; i++)
     p[i]=c;
return p;
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By James C. Fuller March 2016
//
//                      cl strchr.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

char * __cdecl _strchr (const char *s, int c)
{
    do {
        if (*s == c)
        {
            return (char*)s;
        }
    } while (*s++);
    return (0);
}

wchar_t * __cdecl _wcschr (const wchar_t *s, int c)
{
    do {
        if (*s == c)
        {
            return (wchar_t*)s;
        }
    } while (*s++);
    return (0);
}




//===================================================================
//   Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                By Fred Harris, January 2016
//
//         cl strcat.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//===================================================================
#include <windows.h>
#include "string.h"

char* __cdecl strcat(char* strDest, const char* strSource)   
{                                           // 3rd param is size_t
return lstrcatA(strDest, strSource);
}

wchar_t* __cdecl wcscat(wchar_t* strDest, const wchar_t* strSource)   
{                                           // 3rd param is size_t
return lstrcatW(strDest, strSource);
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
//#include "memory.h"    // for some reason you can't put the prototype for this in
                         // memory.h

extern "C" void* __cdecl memcpy(void* pDest, void* pSrc, size_t iCount)
{
char* pDestination=(char*)pDest;
char* pSource=(char*)pSrc;

for(size_t i=0; i<iCount; i++)
     pDestination[i]=pSource[i];

return pDest;
}



//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, April 2016
//
//                     cl memcmp.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================

extern "C" int __cdecl memcmp(const void* buf1, const void* buf2, size_t iCount)
{
char* p1=(char*)buf1;
char* p2=(char*)buf2;
for(size_t i=0; i<iCount; i++)
{
     if(*p1<*p2)
        return -1;
     if(*p1>*p2)
        return 1;
     p1++, p2++;
}

return 0;
}



//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//        cl atol.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"


long __cdecl atol(const char* pStr)
{
char c,cNeg=NULL;           // c holds the char; cNeg holds the '-' sign.
long lTotal=0;              // The running total.

while(*pStr==32 || *pStr==8)
    pStr++; 
if(*pStr=='-')
{
    cNeg='-';
    pStr++;
}
while(*pStr)
{
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
}
if(cNeg=='-')               // If we have a negative sign, convert the value.
    lTotal=-lTotal;

return lTotal;
}


int __cdecl atoi (const char* pStr)
{
return ((int)atol(pStr));
}


long __cdecl _wtol(const wchar_t* pStr)
{
wchar_t c,cNeg=NULL;        // c holds the char; cNeg holds the '-' sign.
long lTotal=0;              // The running total.

while(*pStr==32 || *pStr==8)
    pStr++; 
if(*pStr==L'-')
{
    cNeg=L'-';
    pStr++;
}
while(*pStr)
{
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
}
if(cNeg==L'-')              // If we have a negative sign, convert the value.
    lTotal=-lTotal;

return lTotal;
}


int __cdecl _wtoi (const wchar_t* pStr)
{
return ((int)_wtol(pStr));
}


continued...
  •  

Frederick J. Harris

#4
     Unfortunately, my String Class had to change too, as several of the members (a constructor and operator=) take floats which must be converted to null terminated strings with my old FltToCh() function – now superceded by ordinary sprintf C Runtime usage.   And then there are several Format() members in the same situation.  So here is Strings.h...


// Strings.h                                                    // NULL Terminated String Class With Method Naming Conventions After BASIC Model Languages.  Implemented For
#ifndef Strings_h                                               // x86 / x64, Asci / Unicode (For x86 Builds Comment Out x64 #define Below Left).
#define Strings_h

#ifndef ssize_t
typedef SSIZE_T ssize_t;                                        // ssize_t is defined in GCC, but isn't defined in VC 9-15, but rather SSIZE_T.  For symetry we'll define it.
#endif

#define MINIMUM_ALLOCATION                   16                 // allocate at least this for every String
#define EXPANSION_FACTOR                      2                 // repeated concatenations will keep doubling buffer
#define INTEGRAL_CONVERSIONS                                    // allows direct assignment of integral numeric values to Strings, e.g., String s(12345) or s=54321;  It can be remmed out.
#define FLOATING_POINT_CONVERSIONS                              // allows direct assignment of floating point values to Strings, e.g., String s(3.14159) or s=2.98;  It can be remmed out.
#define FORMATTING                                              // put commas every three places, rounding, left/right justification, specify field sizes (padding), etc.  It can be remmed out.
#define PLUS_EQUAL                                              // For += Functionality With String Concatenation;  It can be remmed out.
#define CONSOLE_OUTPUT                                          // support console output, i.e., enable String::Print();  It can be remmed out.
#define FILE_OUTPUT                                             // Support Writing To FILEs;  It can be remmed out.
#define x64

class String
{
public:                                                        // Constructors (10)
String();                                                      // Uninitialized Constructor
String(const size_t iSize, bool blnNullOut);                   // Constructor creates String of size iSize and optionally nulls out
String(const size_t iCount, const TCHAR c);                    // Constructor initializes String with size_t # of TCHARs
String(const TCHAR ch);                                        // Constructor creates a String initialized with a char, e.g., String c('A');
String(const TCHAR* pStr);                                     // Constructor: Initializes with char*, e.g. s1 = "PowerBASIC! Compile Without Compromise!"
String(const String& strAnother);                              // Constructor creates String initialized with another already existing String, e.g., String s2(s1);
#ifdef INTEGRAL_CONVERSIONS
    String(int iNumber);                                        // Constructor creates String initialized with an int, e.g., String s1(2468); kind of Str$(2468) in PowerBASIC
    String(unsigned int uNumber);                               // Constructor creates String initialized with an unsigned int, e.g., String s1(2468); kind of Str$(2468) in PowerBASIC
    #ifdef x64
       String(size_t  uiNumber);                                // Constructor creates String from 64 bit unsigned number, e.g., String strBigNum(12345678987654);
       String(ssize_t iNumber);                                 // Constructor creates String from 64 bit signed number, e.g., String strBigNum(-12345678987654);
    #endif
#endif
#ifdef FLOATING_POINT_CONVERSIONS
    String(double dblNumber);                                   // Constructor creates String from floating point number, e.g., String s(3.14159);
#endif
String& operator=(const TCHAR c);                              // Assign a char to a String, e.g., String s='A';
String& operator=(const TCHAR* pStr);                          // Assign a character string to a String Object, e.g.,  String s1 = "Compile Without Compromise";
String& operator=(const String& strAnother);                   // Assign an already existing String to this one, e.g., String s2 = s1;
String& Make(const TCHAR, size_t);                             // Returns reference to this with iCount ch TCHARs in it
#ifdef INTEGRAL_CONVERSIONS
    String& operator=(int iNumber);                             // Assign an int converted to a String to this, e.g., String s1 = -123456789;
    String& operator=(unsigned int uNumber);                    // Assign an unsigned int converted to a String to this, e.g., String s1 =  123456789;
    #ifdef x64
       String& operator=(size_t  uNumber);                      // Assign a 64 bit unsigned quantity converted to a String to this, e.g., String s2 =  12345678987654;
       String& operator=(ssize_t iNumber);                      // Assign a 64 bit   signed quantity converted to a String to this, e.g., String s2 = -12345678987654;
    #endif
#endif
#ifdef FLOATING_POINT_CONVERSIONS
    String& operator=(double dblNumber);                        // Assign a double converted to a String to this, e.g., String strDouble = 3.14159;
#endif
String operator+(const TCHAR ch);                              // Concatenates or adds a character to an already existing String, e.g., s1 = s1 + 'A';
String operator+(const TCHAR* pChar);                          // Concatenates or adds a character array (char*) to an already existing String, e.g., s1 = s1 + lpText;
String operator+(String& s);                                   // Concatenates or adds another String to this one, e.g., s1 = s1 + s2;
#ifdef PLUS_EQUAL
    String& operator+=(const TCHAR ch);                         // Add TCHAR to this
    String& operator+=(const String&);                          // Adds a String to this and assigns it to left of equal sign
    String& operator+=(const TCHAR*);                           // Adds a TCHAR* to this and assigns it to left of equal sign
#endif
bool operator==(String s);                                     // Compares two Strings for case sensitive equality
bool operator==(const TCHAR* pChar);                           // Compares a String against a char* for case sensitive equality
bool operator!=(TCHAR* pChar);                                 // Compares a String against a char* for case sensitive inequality
void LTrim();                                                  // Removes leading white space by modifying existing String
void RTrim();                                                  // Removes trailing white space by modifying existing String
void Trim();                                                   // Removes leading or trailing white space from existing String
String UCase();                                                // Returns *this in upper case
String Left(size_t iCntChars);                                 // Returns a String consisting of left-most iCntChars of this
String Right(size_t iCntChars);                                // Returns a String consisting of right-most iCntChars of this
String Mid(size_t iStart, size_t iCount);                      // Returns a String consisting of iCount characters of this starting at one based iStart
int ParseCount(const TCHAR delimiter);                         // Returns count of delimited fields as specified by char delimiter, i.e., comma, space, tab, etc.
void Parse(String* pStr, TCHAR delimiter, size_t iParseCount); // Parses this based on delimiter.  Must pass in 1st parameter String* to sufficient number of Strings
String Remove(const TCHAR* pCharsToRemove);                    // Returns A String With All The chars In A char* Removed (Individual char removal)
String Remove(const TCHAR* pStrToRemove, bool bCaseSensitive); // Returns a String with 1st parameter removed.  2nd is bool for case sensitivity.
String Replace(TCHAR* pToRevove, TCHAR* pReplacement);         // Replaces pToRemove with pReplacement in new String.  Replacement can cause String to grow
bool blnSuccess();                                             // true if String memory allocation succeeded; false otherwise

int InStr                                                      // Returns one based position of pStr in this by case sensitivity and left/right starting position
(
  const TCHAR* pStr,                                            // pStr -- TCHAR* To Character String To Search For
  bool  blnCaseSensitive,                                       // true / case sensitive; false / case insensitive
  bool  blnStartLeft                                            // Can Specify Search Start From Beginning Or End
);

int InStr                                                      // Returns one based position of String in this by case sensitivity and left/right starting position
(
  const String& str,                                            // String To Search For
  bool  blnCaseSensitive,                                       // true / case sensitive; false / case insensitive
  bool  blnStartLeft                                            // Can Specify Search Start From Beginning Or End
);

#ifdef FORMATTING
    void Format                                                 // dblNumber converted to String, in iArrayCount Buffer Size, with iDecPlaces right of decimal, left or right justified.
    (
     double dblNumber,                                          // Double To Be Formatted.
     size_t iFieldSize,                                         // Field Size In Characters.
     size_t iDecimalPlaces,                                     // Number of decimal places to right of decimal
     TCHAR  cDecimalSeperator,                                  // In US We Use '.'.  In Europe mostly this -- ','
     bool   blnRightJustified                                   // Left/Right Justify Result.
    );

    void Format                                                 // double converted to Str, in iArCnt Buf Size, with iDecPlaces right of dec, with cSeperator for thousands, l/r justified.
    (
     double dblNumber,                                          // double to be converted to String
     size_t iFieldSize,                                         // See above.
     size_t iDecimalPlaces,                                     // Number of decimal places to right of decimal
     TCHAR  cThousandsSeperator,                                // Period, comma, whatever
     TCHAR  cDecimalSeperator,                                  // Period, comma, whatever
     bool   blnRightJustified                                   // true is right justified; false left justified in iArraySizeCountOfObjects buffer
    );

    void Format                                                 // For integral numbers; can specify justification, field width, and seperator for thousands place
    (
     ssize_t iNumber,                                           // Integral number to format
     size_t  iFieldSize,                                        // See above.
     TCHAR   cThousandsSeperator,                               // Comma, period, whatever
     bool    blnRightJustified                                  // true is right justified; false left justified in iArraySizeCountOfObjects buffer
    );
#endif

#ifdef CONSOLE_OUTPUT
    void Print(bool blnCrLf);                                   // Outputs String with or without CrLf
    void Print(const TCHAR* pText, bool blnCrLf);               // Parameter #1 - leading text literal const; Parameter #2 - with/without CrLf
#endif
#ifdef FILE_OUTPUT
    void Print(FILE*, bool);                                    //Outputs String To FILE With Or Without CrLf.
    void Print(FILE*, TCHAR*, bool);                            //Outputs String To FILE With Leading Text String With Or Without CrLf.
#endif
ssize_t iVal();                                                // Returns integral value of String
size_t Len();                                                  // accessor for String::iLen member
size_t Capacity();                                             // Will be one less than underlying memory allocation
TCHAR* lpStr();                                                // Same as std::string.c_str().  Returns pointer to underlying Z String
~String();                                                     // String Destructor

private:
TCHAR*    lpBuffer;                                            // Buffer controlled by String
size_t    iLen;                                                // Keeps track of present length of String
size_t    iCapacity;                                           // Keeps track of present capacity of String.  Will be one less element than length of this->lpBuffer memory allocation
size_t    blnSucceeded;                                        // True if String Method Allocation/Invocation Succeeded; False Otherwise
};

String operator+(TCHAR* lhs, String& rhs);
String Str(double dblNum);                                      // Usage: String s1 = "Pi = " + Str(3.14159); // Output: Pi = 3.14159
#ifdef x64
   String Str(int iNum);                                        // Returns 32 bit int converted to String
   String Str(unsigned int iNum);                               // Returns 32 bit unsigned int converted to String
   String Str(SSIZE_T iNum);                                    // Returns 64 bit signed integral number converted to String
   String Str(size_t iNum);                                     // Returns 64 bit unsigned integral number converted to String
#else
   String Str(int iNum);                                        // Returns 32 bit int converted to String
   String Str(unsigned int iNum);                               // Returns 32 bit unsigned int converted to String
#endif
#endif
// End Strings.h


     Before I post Strings.cpp I really need to make some comments on the above, because I changed stuff.  The three formatting members as I had them in my last postings were weird and awkward.  I believe I improved them considerably.  The deal is this.  Before, I had to use my FltToCh() function to do the conversion from a floating point value in 8 byte binary format to an asci or wide character representation.  FltToCh() was something of a low level C type function one of whose parameters was the size of the character buffer where the converted characters were to be written/stored.  In my original implementation of my String::Format() functions I left that parameter like it was, which turns out to be awkward in high level usage.  What makes a lot more sense is to simply specify a field size and number of decimal places – right or left justified, which the member will return in String Class format.  In other words, exactly like the format specifiers in the printf family of functions.  For example, suppose you are outputting a column of dollar amounts where the dollar amounts will never exceed $ 1000.00.  For that you may wish to specify a field width of 8 with 2 decimal points.  A field width of 8 would allow for numbers up to 10000.00 to be output (if not using the version that uses thousands separators.  In still other words, it works exactly like printf where it would be specified like so...


printf("%8.2f",dblAmount); 


     So now it leaves all the internal work of allocating buffers and so on to the internals of the member, and returns to the caller a String Object of the correct field size for further use in your high level program.  So here is Strings.cpp...

continued...
  •  

Frederick J. Harris

#5

// Strings.cpp
#define   TCLib
#ifndef UNICODE
   #define   UNICODE
#endif
#ifndef _UNICODE   
   #define   _UNICODE
#endif   
#include  <windows.h>
#ifdef    TCLib
   #include  "string.h"
   #include  "stdio.h"
   #include  "tchar.h"
   #include  "memory.h"
   #define   NEW new
   extern "C" int _fltused=1;
#else
   #include  <string>
   #include  <cstdio>
   #include  <tchar.h>
   #include  <new>
   #define   NEW new(std::nothrow)
#endif
#include  "Strings.h"


String operator+(TCHAR* lhs, String& rhs)    //global function
{
String sr=lhs;
sr=sr+rhs;

return sr;
}


String Str(double dblNum)
{
return dblNum;
}


#ifdef x64
   String Str(int iNum)
   {
    return iNum;
   }


   String Str(unsigned int iNum)
   {
    return iNum;
   }

   String Str(SSIZE_T iNum)
   {
    return iNum;
   }


   String Str(size_t iNum)
   {
    return iNum;
   }
#else
   String Str(int iNum)
   {
    return iNum;
   }


   String Str(unsigned int iNum)
   {
    return iNum;
   }
#endif


String::String() : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)
{
this->lpBuffer=NEW TCHAR[MINIMUM_ALLOCATION];
if(this->lpBuffer)
{
    this->lpBuffer[0]=0;
    this->iLen=0;
    this->iCapacity=MINIMUM_ALLOCATION-1;
    this->blnSucceeded=TRUE;
}
}


String::String(const size_t iSize, bool blnFillNulls) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)  // Constructor Creates String With Custom Sized
{                                                                                                                   // Buffer (rounded up to paragraph boundary) and
size_t iNewSize       = (iSize/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;                                           // optionally filled with NULLs.
TCHAR* pChar          = NEW TCHAR[iNewSize];
if(pChar)
{
    this->lpBuffer     = pChar;
    this->iCapacity    = iNewSize-1;
    this->iLen         = 0;
    this->lpBuffer[0]  = _T('\0');
    this->blnSucceeded=TRUE;
    if(blnFillNulls)
       memset(this->lpBuffer,0,iNewSize*sizeof(TCHAR));
}
}


String::String(size_t iCount, const TCHAR ch)
{
size_t iNewSize=(iCount/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
TCHAR* pChar=NEW TCHAR[iNewSize];
if(pChar)
{
    this->iCapacity=iNewSize-1;
    this->lpBuffer=pChar;
    #ifdef UNICODE
    wmemset(this->lpBuffer,ch,iCount);
    #else
    memset(this->lpBuffer,ch,iCount);
    #endif
    this->lpBuffer[iCount]=_T('\0');
    this->iLen=iCount;
    this->blnSucceeded=TRUE;
}
}


String::String(const TCHAR ch) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)                          //Constructor: Initializes with wchar_t
{
this->lpBuffer=NEW TCHAR[MINIMUM_ALLOCATION];
if(this->lpBuffer)
{
    this->iLen         = 1;
    this->iCapacity    = MINIMUM_ALLOCATION-1;
    this->lpBuffer[0]  = ch;
    this->lpBuffer[1]  = _T('\0');
    this->blnSucceeded = TRUE;
}
}


String::String(const TCHAR* pStr) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)                      //Constructor: Initializes with TCHAR*
{
size_t iStrLen  = _tcslen(pStr);
size_t iNewSize = (iStrLen/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
this->lpBuffer=NEW TCHAR[iNewSize];
if(this->lpBuffer)
{
    _tcscpy(this->lpBuffer,pStr);
    this->iCapacity    = iNewSize-1;
    this->iLen         = iStrLen;
    this->blnSucceeded = TRUE;
}
}


String::String(const String& s) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)                       //Constructor Initializes With Another String, i.e., Copy Constructor
{
size_t iNewSize = (s.iLen/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
this->lpBuffer  = NEW TCHAR[iNewSize];
if(this->lpBuffer)
{
    _tcscpy(this->lpBuffer,s.lpBuffer);
    this->iLen         = s.iLen;
    this->iCapacity    = iNewSize-1;
    this->blnSucceeded = TRUE;
}
}


#ifdef INTEGRAL_CONVERSIONS
   String::String(int iNum) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)
   {
    this->lpBuffer = NEW TCHAR[MINIMUM_ALLOCATION];
    if(this->lpBuffer)
    {
       this->iCapacity    = MINIMUM_ALLOCATION-1;
       this->iLen         =_stprintf(this->lpBuffer,_T("%d"),iNum);
       this->blnSucceeded = TRUE;
    }
   }


   String::String(unsigned int iNum) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)
   {
    this->lpBuffer = NEW TCHAR[MINIMUM_ALLOCATION];
    if(this->lpBuffer)
    {
       this->iCapacity    = MINIMUM_ALLOCATION-1;
       this->iLen         =_stprintf(this->lpBuffer,_T("%u"),iNum);
       this->blnSucceeded = TRUE;
    }
   }


   #ifdef x64
      String::String(size_t iNum) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)
      {
       this->lpBuffer=NEW TCHAR[32];
       if(this->lpBuffer)
       {
          this->iCapacity=31;
          this->iLen=_stprintf(this->lpBuffer,_T("%Iu"),iNum);
          this->blnSucceeded = TRUE;
       }
      }


      String::String(ssize_t iNum) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)
      {
       this->lpBuffer = NEW TCHAR[32];
       if(this->lpBuffer)
       {
          this->iCapacity    = 31;
          this->iLen         = _stprintf(this->lpBuffer,_T("%Id"),iNum);
          this->blnSucceeded = TRUE;
       }
      }
   #endif
#endif


#ifdef FLOATING_POINT_CONVERSIONS
   String::String(double dblNumber) : lpBuffer(NULL), iLen(0), iCapacity(0), blnSucceeded(FALSE)
   {
    this->lpBuffer = NEW TCHAR[24];
    if(this->lpBuffer)
    {
       this->iCapacity    = 23;
       this->iLen         = _stprintf(this->lpBuffer,_T("%-23.6f"),dblNumber);
       this->blnSucceeded = TRUE;
    }
   }
#endif


String& String::operator=(const TCHAR ch)
{
this->lpBuffer[0]=ch, this->lpBuffer[1]=_T('\0');
this->iLen=1;
this->blnSucceeded=TRUE;

return *this;
}


String& String::operator=(const TCHAR* pStr)   // Assign TCHAR* to String
{
size_t iNewLen=_tcslen(pStr);
if(iNewLen>this->iCapacity)
{
    size_t iNewSize=(iNewLen*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    TCHAR* pCh=NEW TCHAR[iNewSize];
    if(pCh)
    {
       delete [] this->lpBuffer;
       this->lpBuffer=pCh;
       _tcscpy(this->lpBuffer,pStr);
       this->iCapacity=iNewSize-1;
       this->iLen=iNewLen;
       this->blnSucceeded=TRUE;
    }
    else
    {
       this->blnSucceeded=FALSE;
    }
}
else
{
    _tcscpy(this->lpBuffer,pStr);
    this->iLen=iNewLen;
    this->blnSucceeded=TRUE;
}

return *this;
}


String& String::operator=(const String& strAnother)
{
if(this==&strAnother)
    return *this;
if(strAnother.iLen>this->iCapacity)
{
    size_t iNewSize=(strAnother.iLen*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    TCHAR* pCh=NEW TCHAR[iNewSize];
    if(pCh)
    {
       delete [] this->lpBuffer;
       this->lpBuffer=pCh;
       _tcscpy(this->lpBuffer,strAnother.lpBuffer);
       this->iCapacity=iNewSize-1;
       this->iLen=strAnother.iLen;
       this->blnSucceeded=TRUE;
    }
    else
    {
       this->blnSucceeded=FALSE;
    }
}
else
{
    _tcscpy(this->lpBuffer,strAnother.lpBuffer);
    this->iLen=strAnother.iLen;
    this->blnSucceeded=TRUE;
}

return *this;
}


String& String::Make(const TCHAR ch, size_t iCount)    //Creates (Makes) a String with iCount TCHARs
{
if(iCount>this->iCapacity)
{
    size_t iNewSize=(iCount*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    TCHAR* pCh=NEW TCHAR[iNewSize];
    if(pCh)
    {
       delete [] this->lpBuffer;
       this->lpBuffer=pCh;
       this->iCapacity=iNewSize-1;
       for(size_t i=0; i<iCount; i++)
           this->lpBuffer[i]=ch;
       this->lpBuffer[iCount]=0;
       this->iLen=iCount;
       this->blnSucceeded=TRUE;
    }
    else
    {
       this->blnSucceeded=FALSE;
    }
}
else
{
    for(size_t i=0; i<iCount; i++)
        this->lpBuffer[i]=ch;
    this->lpBuffer[iCount]=0;
    this->iLen=iCount;
    this->blnSucceeded=TRUE;
}

return *this;
}


#ifdef INTEGRAL_CONVERSIONS
   #ifdef x64
      String& String::operator=(size_t iNum)
      {
       if(this->iCapacity>=23)
       {
          this->iLen=_stprintf(this->lpBuffer,_T("%Iu"),iNum);
          this->blnSucceeded=TRUE;
       }
       else
       {
          TCHAR* pCh=NEW TCHAR[24];
          if(pCh)
          {
             if(this->lpBuffer)
                delete [] this->lpBuffer;
             this->lpBuffer=pCh;
             this->iCapacity=23;
             this->iLen=_stprintf(this->lpBuffer,_T("%Iu"),iNum);
             this->blnSucceeded=TRUE;
          }
          else
          {
             this->blnSucceeded=FALSE;

          }
       }

       return *this;
      }


      String& String::operator=(ssize_t iNum)
      {
       if(this->iCapacity>=23)
       {
          this->iLen=_stprintf(this->lpBuffer,_T("%Id"),iNum);
          this->blnSucceeded=TRUE;
       }
       else
       {
          TCHAR* pCh=NEW TCHAR[24];
          if(pCh)
          {
             if(this->lpBuffer)
                delete [] this->lpBuffer;
             this->lpBuffer=pCh;
             this->iCapacity=23;
             this->iLen=_stprintf(this->lpBuffer,_T("%Id"),iNum);
             this->blnSucceeded=TRUE;
          }
          else
          {
             this->blnSucceeded=FALSE;
          }
       }

       return *this;
      }
   #endif


   String& String::operator=(int iNum)
   {
    if(this->iCapacity>=15)
    {
       this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
       this->blnSucceeded=TRUE;
    }
    else
    {
       TCHAR* pCh=NEW TCHAR[MINIMUM_ALLOCATION];
       if(pCh)
       {
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pCh;
          this->iCapacity=15;
          this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
          this->blnSucceeded=TRUE;
       }
       else
       {
          this->blnSucceeded=FALSE;
       }
    }

    return *this;
   }


   String& String::operator=(unsigned int iNum)
   {
    if(this->iCapacity>=15)
    {
       this->iLen=_stprintf(this->lpBuffer,_T("%u"),iNum);
       this->blnSucceeded=TRUE;
    }
    else
    {
       TCHAR* pCh=NEW TCHAR[MINIMUM_ALLOCATION];
       if(pCh)
       {
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pCh;
          this->iCapacity=15;
          this->iLen=_stprintf(this->lpBuffer,_T("%u"),iNum);
          this->blnSucceeded=TRUE;
       }
       else
       {
          this->blnSucceeded=FALSE;
       }
    }

    return *this;
   }
#endif


#ifdef FLOATING_POINT_CONVERSIONS
   String& String::operator=(double dblNumber)
   {
    if(this->iCapacity>=24)
    {
       this->iLen=_stprintf(this->lpBuffer,_T("%-23.6f"),dblNumber);
       this->blnSucceeded=TRUE;
    }
    else
    {
       TCHAR* pCh=NEW TCHAR[24];
       if(pCh)
       {
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pCh;
          this->iCapacity=23;
          this->iLen=_stprintf(this->lpBuffer,_T("%-23.6f"),dblNumber);
          this->blnSucceeded=TRUE;
       }
       else
       {
          this->blnSucceeded=FALSE;
       }
    }

    return *this;
   }
#endif


String String::operator+(const TCHAR ch)
{
int iNewLen=this->iLen+1;

String s(iNewLen,false);
if(s.blnSucceeded)
{
    _tcscpy(s.lpBuffer,this->lpBuffer);
    s.lpBuffer[iNewLen-1]=ch;
    s.lpBuffer[iNewLen]=_T('\0');
    s.iLen=iNewLen;
}

return s;
}


String String::operator+(const TCHAR* pStr)
{
int iNewLen=_tcslen(pStr)+this->iLen;
String s(iNewLen,false);
if(s.blnSucceeded)
{
    _tcscpy(s.lpBuffer,this->lpBuffer);
    _tcscat(s.lpBuffer,pStr);
    s.iLen=iNewLen;
}

return s;
}


String String::operator+(String& strRef)
{
int iNewLen=strRef.iLen+this->iLen;
String s(iNewLen,false);
if(s.blnSucceeded==TRUE)
{
    _tcscpy(s.lpBuffer,this->lpBuffer);
    _tcscat(s.lpBuffer,strRef.lpBuffer);
    s.iLen=iNewLen;
}

return s;
}


#ifdef PLUS_EQUAL
   String& String::operator+=(const TCHAR ch)
   {
    size_t iTot=this->iLen+1;
    if(iTot>this->iCapacity)
    {
       int iNewSize=(iTot*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       TCHAR* pNew=NEW TCHAR[iNewSize];
       if(pNew)
       {
          _tcscpy(pNew,this->lpBuffer);
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pNew;
          this->lpBuffer[iTot-1]=ch;
          this->lpBuffer[iTot]=_T('\0');
          this->iCapacity=iNewSize-1;
          this->iLen=iTot;
          this->blnSucceeded=TRUE;
       }
       else
       {
          this->blnSucceeded=FALSE;
       }
    }
    else
    {
       this->lpBuffer[iTot-1]=ch;
       this->lpBuffer[iTot]=_T('\0');
       this->iLen=iTot;
       this->blnSucceeded=TRUE;
    }

    return *this;
   }


   String& String::operator+=(const TCHAR* pStr)
   {
    size_t iStrlen=_tcslen(pStr);
    size_t iTot=iStrlen+this->iLen;
    if(iTot>this->iCapacity)
    {
       int iNewSize=(iTot*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       TCHAR* pNew=NEW TCHAR[iNewSize];
       if(pNew)
       {
          _tcscpy(pNew,this->lpBuffer);
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pNew;
          _tcscat(pNew,pStr);
          this->iCapacity=iNewSize-1;
          this->iLen=iTot;
          this->blnSucceeded=TRUE;
       }
       else
       {
          this->blnSucceeded=FALSE;
       }
    }
    else
    {
       _tcscat(this->lpBuffer,pStr);
       this->iLen=iTot;
       this->blnSucceeded=TRUE;
    }

    return *this;
   }


   String& String::operator+=(const String& strRef)
   {
    size_t iTot=strRef.iLen+this->iLen;
    if(iTot>this->iCapacity)
    {
       int iNewSize=(iTot*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       TCHAR* pNew=NEW TCHAR[iNewSize];
       if(pNew)
       {
          _tcscpy(pNew,this->lpBuffer);
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pNew;
          _tcscat(pNew,strRef.lpBuffer);
          this->iCapacity=iNewSize-1;
          this->iLen=iTot;
          this->blnSucceeded=TRUE;
       }
       else
       {
          this->blnSucceeded=FALSE;
       }
    }
    else
    {
       _tcscat(this->lpBuffer,strRef.lpBuffer);
       this->iLen=iTot;
       this->blnSucceeded=TRUE;
    }

    return *this;
   }
#endif


bool String::operator==(String strRef)
{
if(_tcscmp(this->lpStr(),strRef.lpStr())==0)
    return true;
else
    return false;
}


bool String::operator==(const TCHAR* pStr)
{
//printf("Entering String::operator==(const TCHAR* pStr)\n");
//printf("  this->lpStr() = %s\n",this->lpStr());
if(_tcscmp(this->lpStr(),pStr)==0)
{
    //printf("  Returning true!\n");
    //printf("Leaving String::operator==(const TCHAR* pStr)\n");
    return true;
}   
else
{
    //printf("  Returning false!\n");
    //printf("Leaving String::operator==(const TCHAR* pStr)\n");
    return false;
}   
}


bool String::operator!=(TCHAR* pStr)
{
if(_tcscmp(this->lpStr(),pStr)==0)
    return false;
else
    return true;
}


String String::Left(size_t iNum)   //  strncpy = _tcsncpy
{
if(iNum<this->iLen)
{
    size_t iNewSize=(iNum*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    String sr(iNewSize,true);
    if(sr.blnSucceeded)
    {
       _tcsncpy(sr.lpBuffer,this->lpBuffer,iNum);   // wcsncpy(wchar_t pDest, wchar_t pSource, size_t iCount);
       sr.lpBuffer[iNum]=_T('\0');
       sr.iLen=iNum;
       sr.blnSucceeded=TRUE;
       return sr;
    }
    else
    {
       sr.blnSucceeded=FALSE;
       return sr;
    }
}
else
{
    String sr=*this;
    sr.iLen=this->iLen;
    sr.blnSucceeded=TRUE;
    return sr;
}
}


String String::Right(size_t iNum)  //Returns Right$(strMain,iNum)
{
if(iNum<this->iLen)
{
    size_t iNewSize=(iNum*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
    String sr(iNewSize,false);
    if(sr.blnSucceeded)
    {
       _tcsncpy(sr.lpBuffer,this->lpBuffer+this->iLen-iNum,iNum);
       sr.lpBuffer[iNum]=_T('\0');
       sr.iLen=iNum;
       sr.blnSucceeded=TRUE;
       return sr;
    }
    else
    {
       sr.blnSucceeded=FALSE;
       return sr;
    }
}
else
{
    String sr=*this;
    sr.iLen=this->iLen;
    sr.blnSucceeded=TRUE;
    return sr;
}
}


String String::Mid(size_t iStart, size_t iCount)
{
if(iStart<1)
{
    String sr;
    return sr;
}
if(iCount+iStart>this->iLen)
    iCount=this->iLen-iStart+1;
String sr(iCount,false);
if(sr.blnSucceeded)
{
    _tcsncpy(sr.lpBuffer,this->lpBuffer+iStart-1,iCount);
    sr.lpBuffer[iCount]=_T('\0');
    sr.iLen=iCount;
    sr.blnSucceeded=TRUE;
}
else
    sr.blnSucceeded=FALSE;

return sr;
}


void String::LTrim()
{
size_t iCt=0;

for(size_t i=0; i<this->iLen; i++)
{
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32)
        iCt++;
     else
        break;
}
if(iCt)
{
    for(size_t i=iCt; i<=this->iLen; i++)
        this->lpBuffer[i-iCt]=this->lpBuffer[i];
}
this->iLen=this->iLen-iCt;
this->blnSucceeded=TRUE;
}


void String::RTrim()
{
int iCt=0;

for(int i=this->iLen-1; i>0; i--)
{
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32)
        iCt++;
     else
        break;
}
this->lpBuffer[this->iLen-iCt]=0;
this->iLen=this->iLen-iCt;
this->blnSucceeded=TRUE;
}


void String::Trim()
{
this->LTrim();
this->RTrim();
this->blnSucceeded=TRUE;
}


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

p=this->lpBuffer;
while(*p)
{
   if(*p==delimiter)
      iCtr++;
   p++;
}
this->blnSucceeded=TRUE;

return ++iCtr;
}


void String::Parse(String* pStr, TCHAR delimiter, size_t iParseCount)
{
TCHAR* pBuffer=NEW TCHAR[this->iLen+1];
if(pBuffer)
{
    TCHAR* p=pBuffer;
    TCHAR* c=this->lpBuffer;
    while(*c)
    {
       if(*c==delimiter)
          *p=0;
       else
          *p=*c;
       p++, c++;
    }
    *p=0, p=pBuffer;
    for(size_t i=0; i<iParseCount; i++)
    {
        pStr[i]=p;
        p=p+pStr[i].iLen+1;
    }
    delete [] pBuffer;
    this->blnSucceeded=TRUE;
}
else
    this->blnSucceeded=FALSE;
}


int iMatch(TCHAR* pThis, const TCHAR* pStr, bool blnCaseSensitive, bool blnStartBeginning, int i, int iParamLen)
{
if(blnCaseSensitive)
{
    if(_tcsncmp(pThis+i,pStr,iParamLen)==0)   //_tcsncmp
       return i+1;
    else
       return 0;
}
else
{
    if(_tcsnicmp(pThis+i,pStr,iParamLen)==0)  //__tcsnicmp
       return i+1;
    else
       return 0;
}
}


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

if(*pStr==0)
{
    this->blnSucceeded=FALSE;
    return 0;
}
iParamLen=_tcslen(pStr);
iRange=this->iLen-iParamLen;
if(blnStartBeginning)
{
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
           {
              this->blnSucceeded=TRUE;
              return iReturn;
           }
       }
    }
    else
       this->blnSucceeded=FALSE;
}
else
{
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
           {
              this->blnSucceeded=TRUE;
              return iReturn;
           }
       }
    }
    else
       this->blnSucceeded=FALSE;
}
this->blnSucceeded=TRUE;

return 0;
}


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

if(s.iLen==0)
{
    this->blnSucceeded=FALSE;
    return 0;
}
iParamLen=s.iLen;
iRange=this->iLen-iParamLen;
if(blnStartBeginning)
{
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
           {
              this->blnSucceeded=TRUE;
              return iReturn;
           }
       }
    }
    else
       this->blnSucceeded=FALSE;
}
else
{
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
           {
              this->blnSucceeded=TRUE;
              return iReturn;
           }
       }
    }
}
this->blnSucceeded=TRUE;

return 0;
}


String String::Remove(const TCHAR* pStr)   // Individual character removal, i.e., if this contains the
{                                          // English alphabet...
size_t i,j,iStrLen,iParamLen;             //
TCHAR *pThis, *pThat, *p;                 // abcdefghijklmnopqrstuvwxyz
bool blnFoundBadTCHAR;                    //
                                           // ...and pStr is this...
iStrLen=this->iLen;                       //
String sr((int)iStrLen,false);            // "aeiou"
if(sr.blnSucceeded)                       //
{                                         // ,,,then this will be returned in the returned String...
    iParamLen=_tcslen(pStr);               //
    pThis=this->lpBuffer;                  // bcdfghjklmnpqrstvwxyz
    p=sr.lpStr();                          //
    for(i=0; i<iStrLen; i++)               // That is, the vowels will be individually removed
    {
        pThat=(TCHAR*)pStr;
        blnFoundBadTCHAR=false;
        for(j=0; j<iParamLen; j++)
        {
            if(*pThis==*pThat)
            {
               blnFoundBadTCHAR=true;
               break;
            }
            pThat++;
        }
        if(!blnFoundBadTCHAR)
        {
           *p=*pThis;
           p++;
           *p=_T('\0');
        }
        pThis++;
    }
    sr.iLen=_tcslen(sr.lpStr());
    sr.blnSucceeded=TRUE;
}
else
    sr.blnSucceeded=FALSE;

return sr;
}


String String::Remove(const TCHAR* pMatch, bool blnCaseSensitive)
{
size_t i,iCountMatches=0,iCtr=0;

size_t iLenMatch=_tcslen(pMatch);
for(i=0; i<this->iLen; i++)
{
     if(blnCaseSensitive)
     {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)  //_tcsncmp
           iCountMatches++;
     }
     else
     {
        if(_tcsnicmp(lpBuffer+i,pMatch,iLenMatch)==0) //__tcsnicmp
           iCountMatches++;
     }
}
ssize_t iAllocation=this->iLen-(iCountMatches*iLenMatch);
String sr(iAllocation,false);
if(sr.blnSucceeded)
{
    for(i=0; i<this->iLen; i++)
    {
        if(blnCaseSensitive)
        {
           if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
              i+=iLenMatch-1;
           else
           {
              sr.lpBuffer[iCtr]=this->lpBuffer[i];
              iCtr++;
           }
           sr.lpBuffer[iCtr]=_T('\0');
        }
        else
        {
           if(_tcsnicmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
              i+=iLenMatch-1;
           else
           {
              sr.lpBuffer[iCtr]=this->lpBuffer[i];
              iCtr++;
           }
           sr.lpBuffer[iCtr]=_T('\0');
        }
    }
    sr.iLen=iCtr;
    sr.blnSucceeded=TRUE;
}
else
    sr.blnSucceeded=FALSE;

return sr;
}


String String::Replace(TCHAR* pMatch, TCHAR* pNew)  //strncmp = _tcsncmp
{
size_t i,iLenMatch,iLenNew,iCountMatches,iExtra,iExtraLengthNeeded,iAllocation,iCtr;

iLenMatch=_tcslen(pMatch);
iCountMatches=0, iAllocation=0, iCtr=0;
iLenNew=_tcslen(pNew);
if(iLenNew==0)
{
    String sr=this->Remove(pMatch,true); //return
    sr.blnSucceeded=TRUE;
    return sr;
}
else
{
    iExtra=iLenNew-iLenMatch;
    for(i=0; i<this->iLen; i++)
    {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)
           iCountMatches++;  //Count how many match strings
    }
    iExtraLengthNeeded=iCountMatches*iExtra;
    iAllocation=this->iLen+iExtraLengthNeeded;
    String sr(iAllocation,false);
    if(sr.blnSucceeded)
    {
       for(i=0; i<this->iLen; i++)
       {
           if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
           {
              _tcscpy(sr.lpBuffer+iCtr,pNew);
              iCtr+=iLenNew;
              i+=iLenMatch-1;
           }
           else
           {
              sr.lpBuffer[iCtr]=this->lpBuffer[i];
              iCtr++;
           }
           sr.lpBuffer[iCtr]=_T('\0');
       }
       sr.iLen=iCtr;
       sr.blnSucceeded=TRUE;
       return sr;
    }
    else
    {
       sr.blnSucceeded=FALSE;
       return sr;
    }
}
}


#ifdef FORMATTING
   void String::Format(double dblNumber, size_t iFieldSize, size_t iDecPlaces, TCHAR cDecimalSeperator, bool blnRightJustified)
   {
    String strFldSpec,strDecPlaces,strFldSize;

    strFldSpec=_T('%');
    strFldSize=iFieldSize;
    strDecPlaces=iDecPlaces;
    if(blnRightJustified)
       strFldSpec=strFldSpec+strFldSize+cDecimalSeperator+strDecPlaces;
    else
       strFldSpec=strFldSpec+_T('-')+strFldSize+cDecimalSeperator+strDecPlaces;
    strFldSpec=strFldSpec+_T('f');
    if(this->iCapacity<iFieldSize)
    {
       TCHAR* pCh=NEW TCHAR[iFieldSize+1];
       if(pCh)
       {
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pCh;
          this->iCapacity=iFieldSize;
          this->blnSucceeded=TRUE;
          this->iLen=_stprintf(this->lpBuffer,strFldSpec.lpStr(),dblNumber);
       }
       else
       {
          this->blnSucceeded=FALSE;
       }
    }
    else
    {
       this->iLen=_stprintf(this->lpBuffer,strFldSpec.lpStr(),dblNumber);
       this->blnSucceeded=TRUE;
    }
   }


   void String::Format(double dblNumber, size_t iFieldSize, size_t iDecimalPlaces, TCHAR cThousandsSeperator, TCHAR cDecimalSeperator, bool blnRightJustified)
   {
    size_t iLenStr=0,iPadding=0;
    int iDecPt=0;
    ssize_t iInt;

    if(this->iCapacity<iFieldSize+1)
    {
       size_t iNewSize=(iFieldSize/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       TCHAR* pCh=NEW TCHAR[iNewSize];
       if(pCh)
       {
          if(this->lpBuffer)
             delete [] this->lpBuffer;
          this->lpBuffer=pCh;
          this->iCapacity=iNewSize-1;
          this->lpBuffer[0]=0;
       }
       else
       {
          this->blnSucceeded=FALSE;
          return;
       }
    }
    String s1(24,true);
    s1.Format(dblNumber,24,iDecimalPlaces,cDecimalSeperator,false);
    s1.Trim();
    if(iDecimalPlaces)
    {
       iDecPt=s1.InStr(cDecimalSeperator,false,false);
       String s2=s1.Left(iDecPt-1);
       iInt=_ttoi64(s2.lpStr());
    }
    else
       iInt=_ttoi64(s1.lpStr());
    String s3(28,true);
    if(iInt==0 && dblNumber<0.0)
       s3=_T("-0");
    else
       s3.Format((ssize_t)iInt,28,cThousandsSeperator,false);
    s3.Trim();
    if(iDecimalPlaces)
    {
       String s4=s1.Mid(iDecPt,iDecimalPlaces+1);
       s3=s3+s4;
    }
    iLenStr=s3.Len();
    if(iLenStr>this->iCapacity)
       return;
    iPadding=iFieldSize-iLenStr;
    if(blnRightJustified)
    {
       for(size_t i=0; i<iPadding; i++)
           this->lpBuffer[i]=32;
       this->lpBuffer[iPadding]=0;
       _tcscat(this->lpBuffer, s3.lpStr());
    }
    else
    {
       _tcscpy(this->lpBuffer,s3.lpStr());
       for(size_t i=iLenStr; i<iFieldSize; i++)
           this->lpBuffer[i]=32;
       this->lpBuffer[iFieldSize]=0;
    }
    this->iLen=_tcslen(this->lpBuffer);
   }


   void String::Format(ssize_t iNumber, size_t iFieldSize, TCHAR cThousandsSeperator, bool blnRightJustified)
   {
    bool blnPositive;
    TCHAR szBuf1[28];
    TCHAR szBuf2[28];
    size_t iLenStr=0;
    size_t iDigit=0;
    size_t iCtr=1;
    size_t j=0;

    memset(szBuf1,0,28*sizeof(TCHAR));
    memset(szBuf2,0,28*sizeof(TCHAR));
    if(iNumber<0)
       blnPositive=false;
    else
       blnPositive=true;
    #ifdef x64
       iNumber=_abs64(iNumber);
    #else
       iNumber=abs(iNumber);
    #endif
    #ifdef x64
       _stprintf(szBuf1,_T("%Iu"),(size_t)iNumber);
    #else
       _stprintf(szBuf1,_T("%u"),(size_t)iNumber);
    #endif
    _tcsrev(szBuf1);
    iLenStr=_tcslen(szBuf1);
    for(size_t i=0; i<iLenStr; i++)
    {
        if(iCtr==3)
        {
           iDigit++;
           szBuf2[j]=szBuf1[i];
           if(iDigit<iLenStr)
           {
              j++;
              szBuf2[j]=cThousandsSeperator;
           }
           j++, iCtr=1;
        }
        else
        {
           iDigit++;
           szBuf2[j]=szBuf1[i];
           j++, iCtr++;
        }
    }
    _tcsrev(szBuf2); // Done With Creating String With Commas
    memset(szBuf1,0,28*sizeof(TCHAR));  // Reuse szBuf1
    if(blnPositive)
       _tcscpy(szBuf1,szBuf2);
    else
    {
       szBuf1[0]=_T('-');
       _tcscat(szBuf1,szBuf2);
    }
    size_t iRequiredBytes;         // Find out which of two is larger - length of string necessary
    iLenStr=_tcslen(szBuf1);          // or iFldLen
    if(iFieldSize<iLenStr)
       return;
    iRequiredBytes=iFieldSize+1;
    if(iRequiredBytes>(size_t)this->iCapacity)
    {
       delete [] this->lpBuffer;
       int iNewSize=(iRequiredBytes*EXPANSION_FACTOR/MINIMUM_ALLOCATION+1)*MINIMUM_ALLOCATION;
       this->lpBuffer=NEW TCHAR[iNewSize];
       this->iCapacity=iNewSize-1;
    }

    memset(this->lpBuffer,0,(this->iCapacity+1)*sizeof(TCHAR));
    ssize_t iDifference=iFieldSize-iLenStr;
    if(blnRightJustified)
    {
       if(iDifference > 0)
       {
          for(size_t i=0; i<(size_t)iDifference; i++)
              this->lpBuffer[i]=_T(' ');  // 32
       }
       _tcscat(this->lpBuffer,szBuf1);
    }
    else
    {
       _tcscpy(this->lpBuffer,szBuf1);
       if(iDifference>0)
       {
          for(size_t i=iLenStr; i<iDifference+iLenStr; i++)
              this->lpBuffer[i]=_T(' ');  // 32
       }
    }
    this->iLen=_tcslen(this->lpBuffer);
  }
#endif


ssize_t String::iVal()
{
#ifdef x64
    return _ttoi64(this->lpBuffer);
#else
    return _ttoi(this->lpBuffer);  //_ttoi
#endif
}


String String::UCase()
{
String s(*this);
CharUpper(s.lpBuffer);
return s;
}


TCHAR* String::lpStr()
{
return this->lpBuffer;
}


size_t String::Len(void)
{
return this->iLen;
}


size_t String::Capacity(void)
{
return this->iCapacity;
}


bool String::blnSuccess()
{
return this->blnSucceeded;
}


#ifdef CONSOLE_OUTPUT
   void String::Print(bool blnCrLf)
   {
    _tprintf(_T("%s"),this->lpBuffer);
    if(blnCrLf)
       _tprintf(_T("\n"));
   }


   void String::Print(const TCHAR* pStr, bool blnCrLf)
   {
    _tprintf(_T("%s%s"),pStr,lpBuffer);
    if(blnCrLf)
       _tprintf(_T("\n"));
   }
#endif


#ifdef FILE_OUTPUT
   void String::Print(FILE* fp1, bool blnCrLf)
   {
    _ftprintf(fp1, _T("%s"),lpBuffer);
    if(blnCrLf)
       _ftprintf(fp1,_T("\n"));
   }


   void String::Print(FILE* fp1, TCHAR* pStr, bool blnCrLf)
   {
    _ftprintf(fp1, _T("%s%s"),pStr,lpBuffer);
    if(blnCrLf)
       _ftprintf(fp1,_T("\n"));
   }
#endif


String::~String()
{
if(this->lpBuffer)
    delete [] this->lpBuffer;
}
// End Strings.cpp


Next are the various headers referenced in the above code...
  •  

Frederick J. Harris

#6

// malloc.h
#ifndef malloc_h
#define malloc_h

extern "C" void*  __cdecl malloc     (size_t size                  );
extern "C" void   __cdecl free       (void*  pMem                  );
extern "C" void*  __cdecl realloc    (void*  pMem,   size_t size   );
extern "C" void*  __cdecl calloc     (size_t nitems, size_t size   );
extern "C" void*  __cdecl _nh_malloc (size_t size,   int    nhFlag );
extern "C" size_t __cdecl _msize     (void*  pMem                  );

#endif



// memory.h
#ifndef memory_h
#define memory_h

extern "C" void*     __cdecl memset  (void* p, int c, size_t count);
extern "C" wchar_t*  __cdecl wmemset (wchar_t* p, wchar_t c, size_t count);
//extern "C" void*     __cdecl memcpy(void* pDest, void* pSrc, size_t iCount); // won't make lib!?!

#endif


This next one is especially important, as it redefines all the critical stdio functions as function pointers...


// stdio.h
#ifndef stdio_h
#define stdio_h

struct          FILE
{
char*          _ptr;
int            _cnt;
char*          _base;
int            _flag;
int            _file;
int            _charbuf;
int            _bufsiz;
char*          _tmpfname;
};

extern char     (__cdecl* getchar)();
extern FILE*    (__cdecl* fopen)(const char* pszFile, const char* pszMode);
extern FILE*    (__cdecl* _wfopen)(const wchar_t* pszFile, const wchar_t* pszMode);
extern int      (__cdecl* printf)(const char* pFormat, ...);
extern int      (__cdecl* wprintf)(const wchar_t* pFormat, ...);
extern int      (__cdecl* fprintf)(FILE* fp, const char* format, ...);
extern int      (__cdecl* fwprintf)(FILE* fp, const wchar_t* format, ...);
extern int      (__cdecl* fscanf)(FILE* fp, const char* pFormat, ...);
extern int      (__cdecl* fwscanf)(FILE* fp, const wchar_t* pFormat, ...);
extern int      (__cdecl* sprintf)(char* buffer, const char* format, ...);
extern int      (__cdecl* swprintf)(wchar_t* buffer, const wchar_t* format, ...);
extern char*    (__cdecl* fgets)(char* pBuffer, int iSize, FILE* fp);
extern wchar_t* (__cdecl* fgetws)(wchar_t* pBuffer, int iSize, FILE* fp);
extern void     (__cdecl* rewind)(FILE* fp);
extern int      (__cdecl* fclose)(FILE* fp);

#endif



// stdlib.h
#ifndef stdlib_h
#define stdlib_h
   #define NULL 0
   extern "C" void*    __cdecl malloc  (size_t          size);
   extern "C" void     __cdecl free    (void*           pMem);
   extern "C" long     __cdecl atol    (const char*     pStr);
   extern "C" int      __cdecl atoi    (const char*     pStr);
   extern "C" long     __cdecl _wtol   (const wchar_t*  pStr);
   extern "C" int      __cdecl _wtoi   (const wchar_t*  pStr);
   extern "C" _int64   __cdecl _atoi64 (const char*     pStr);
   extern "C" _int64   __cdecl _wtoi64 (const wchar_t*  pStr);
   extern "C" double   __cdecl atof    (const char*     pStr);
   extern "C" double   __cdecl _wtof   (const wchar_t*  pStr);
   extern "C" int      __cdecl abs     (int             n);
   extern "C" long     __cdecl labs    (long            n);
   extern "C" _int64   __cdecl _abs64  (__int64         n);
#endif



// string.h
#ifndef string_h
#define string_h

extern "C" size_t   __cdecl strlen(const char* pStr);
extern "C" size_t   __cdecl wcslen(const wchar_t* pStr);

extern "C" char*    __cdecl strcpy(char* strDestination, const char* strSource);
extern "C" wchar_t* __cdecl wcscpy(wchar_t* strDestination, const wchar_t* strSource);

extern "C" char*    __cdecl strcat(char* strDest, const char* strSource);
extern "C" wchar_t* __cdecl wcscat(wchar_t* strDest, const wchar_t* strSource);

extern "C" char*    __cdecl strncpy(char* strDest, const char* strSource, size_t iCount);   
extern "C" wchar_t* __cdecl wcsncpy(wchar_t* strDest, const wchar_t* strSource, size_t iCount); 

extern "C" int      __cdecl strcmp(const char* string1, const char* string2);
extern "C" int      __cdecl wcscmp(const wchar_t* string1, const wchar_t* string2);

extern "C" int      __cdecl _stricmp(const char* string1, const char* string2);   
extern "C" int      __cdecl _wcsicmp(const wchar_t* string1, const wchar_t* string2); 

extern "C" int      __cdecl strncmp(const char* str1, const char* str2, size_t count);
extern "C" int      __cdecl wcsncmp(const wchar_t* str1, const wchar_t* str2, size_t count);

extern "C" int      __cdecl _strnicmp(const char* str1, const char* str2, size_t count);
extern "C" int      __cdecl _wcsnicmp(const wchar_t* str1, const wchar_t* str2, size_t count);

extern "C" char*    __cdecl _strrev(char* pStr);
extern "C" wchar_t* __cdecl _wcsrev(wchar_t* pStr);

extern "C" char*    __cdecl _strchr(const char* s, int c);
extern "C" wchar_t* __cdecl _wcschr(const wchar_t* s, int c);

#endif


     That's it.  In closing I'd like to make one further observation.  I expect its more desirable to proceed in this way because of the localization issue.  Take decimal, date and thousands separators in numbers and dates.  For a long time I was the only American who had a board here on Jose's Forum.  Now James Fuller who compared to everybody else is practically my neighbor has a board, but nonetheless this Forum has a distinctly International flavor.  And most other countries have different conventions for these numeric symbols than Americans have.  That's why in my FltToCh() function I had two character parameters for the decimal point to be used and the character for separating thousand places.  If msvcrt.dll is used for C Runtime functionality I expect locales will be interpreted correctly for every Windows installation wherever it may be.  That's what 'locales support' is supposed to be about, I think.
  •  

Frederick J. Harris

Almost all the code of significance in the above posts is included in the attached zip, including the newest TCLib.lib binary.
  •  

James C. Fuller

Fred,
  You have been busy and so have I but on a completely different venue.
Paul Squires FreeBasic Support:
http://www.planetsquires.com/protect/forum/index.php
I hope to get a chance to review your work soon.

James
  •  

James C. Fuller

Fred,
  I'm back and downloaded the zip.
I was able to tweak and compile TCLib for use with bc9Basic. I only ran a couple of tests but so far so good. I did notice there are more *.cpp files than there are OBJS = in your TCLib.mak.
Extra files or missing items in the make file??

James
  •  

Frederick J. Harris

Just trying to track it down Jim, and I counted 26 objs in TCLib.mak and 27 *.cpps in the zip.  It looks like atol is missing from the mak file.  So I think that's the discrepancy.  Sorry I missed that.

What do you think of my reasoning about using msvcrt.dll instead of trying to code my own versions of everything I come up against that I need?

I followed your link and I see now lots of us old time PB'ers are now with Freebasic at Paul's site.  Seems to even have engaged Jose's interest! :)
  •  

James C. Fuller

Fred,
  I would feel sorry for you for all the time you put in trying to replace the msvcrt.dll functions but I have done similar things in the past myself :)
Look at it this way, it did provide a lot of good information along the way though.

I grabbed your Strings class from the cprogramming.com forum. Is this one newer/better than the one in the TCLib download here?
I've done some more testing with bc9Basic and so far things look good.

It is good to see José grab onto a project and provide the community with his CWindow / Afx framework.
One of the best things about FreeBasic is it's "Private" keyword for Sub/Functions. It does the same thing as PowerBASIC's dead code removal.

James

  •  

Frederick J. Harris

In that latest version I posted there I changed all the uses of new to allocate memory so that the return value could be tested in the standard manner for a non NULL return.  I imagine you and every other good programmer tests memory allocations for success, and that's something I always do to with the single exception of my uses of new in my String Class.  Of course, the situation there with C++ is that new doesn't behave like the Win32 functions in regard to indicating a memory allocation failure with a NULL return.  Rather, it raises an exception that will crash the app if it isn't handled with a try / catch exception handler block.  At least that's my understanding of it.  However, using the std::nothrow version of new causes it to return NULL instead, so I added that to all my calls to new.  So I added too another private member variable to the class named blnSucceeded, which I set to FALSE if new fails.  Seems to me a reasonable thing to do.

I've never had my string class crash due to a failed memory allocation, but it certainly would if the requested memory was large enough, or one was allocating millions of strings or anything like that.  So this should solve that, at least in my opinion.  In usage, if one thought there was any possibility of a string failing to allocate, one could test it now with the new setup I have.  I'm surprised you picked up on that so soon!  It didn't add anything to size really so I'm happy with it.

Yea, I did spend a lot of time on code I'm now not using, but like you said I don't feel too bad about it.  I've still got it if I ever need it, and I really feel like the msvcrt usage is the better idea.

  •  

Frederick J. Harris

Quite a few changes, additions, and hopefully improvements to note...

1)   Demo22.cpp shows how atof/_wtof now stop character conversion to numbers at first non-numeric character.  This change affects integral conversions too;

2)   Demo23 and Demo25 show addition of Str$() capability to String Class.  This is exciting!  Can now code just like with PowerBASIC!!!

3)   Demo24 shows how memory allocations by String Class can be tested for success/failure.  Since TCLib doesn't use C++ Exceptions, memory allocations can be tested for NULL returns;

4)   Demo26 is Demo of templated CArray Class.  Note in main() how use of Str() reduces code.  I have commented out code for how it would be done in C or C++ before my addition of Str();

5)   Demo27 is example of use of pow() from math.h.  This addition necessitated changes to all the crt start up files;

6)   Demo29 shows addition of another String Constructor which allows creation of a String containing a user specified number of some specific character, kind of like PowerBASIC's String$(Count, Char);

7)   Demo29 also shows printing to text file output has been added; kind of like the CONSOLE_OUTPUT #define.  So now there is both a CONSOLE_OUTPUT #define and a FILE_OUTPUT #define in Strings.h, either or both of which can be commented in/out as desired or needed.

Finally, I'd like to say a few words on my progress of converting some of my major projects to TCLib.  Its going pretty darn well.  As I stated several months ago, I managed to build my ActiveX Grid Control with TCLib.lib, and in x64 its coming out compacted with UPX to 17 k, which is 5 k smaller than my PowerBASIC x86 version (22 k compacted).  Here are the files and lines of code involved in that project to give you an idea of the amount of code involved...


FHGrid Project Lines Of Code
============================

FHGRid.def             7
FHGrid.rc              1
FHGrid.idl            58
Server.cpp           265
Grid.cpp            1130
Grid.h               116
GStrings.cpp         166
GStrings.h            30
Interfaces.h          36
Registry.cpp         170
Registry.h             5
WinCode.cpp         1365
WinCode.h             59
============================
                    2408


A project of mine which uses this grid control is my TimberBeast C++ project.  Here are the lines of code on that...


TimberBeast Lines Of Code
=========================

TimberBeast.cpp   232
frmProcess       8111
frmCruise.cpp    1505
frmTally         2189
CSql.cpp          130
Strings.cpp      1410

TimberBeast.h     400
frmCruise.h        61
frmProcess.h      126
frmTally.h         91
IGrid.h            33
Strings.h         155
CSql.h             35
=====================
                14478


So there are around 14,500 lines of code and its building to 159,744 bytes.  I might add that likely 10% of the lines of code are debug output code (to a log file) that gets turned off in release builds.  So the release build might only be like 13,000 lines of code or so.  But in any case, my release build is compacting with UPX to only 53 k!  Add about 17 k for the ActiveX Grid Control mentioned above and you have an enterprise level project whose binaries only come to about 70 k!  Amazing!

Another major project which I've just successfully converted to a TCLib build is my DCSilvah project.  That's short for 'Data Collector Silvah'.  This project started out as a Windows CE project for use on the handheld data collectors used by my organization.  Its for collecting data for the U.S. Forest Service's Silvah system, which is an 'Expert System' for helping foresters perform silvicultural prescriptions for Northeastern hardwood forests.  Several years back I did some work on it so that it could be built for desktop/laptop Windows use.  Anyway, it's a major piece of code.  There are two parts to the project.  There is a dll which contains my String Class plus two custom control grids.  That project is named dllSilvah.  Then there is DCSilvah itself.  The String Class is built into the dll and exported for use in the host DCSilvah project.  That way the compiled code for the String Class exists only once and can be used in both projects.  Here are the files involved and lines of code for each project...

The DCSilvah Project Files and Lines of Code are as follows...


Main.cpp                       4860 Lines of Code
Main.h                          808 ...
CArray.cpp                       99 ...
frmLandscape.cpp                215 ...
frmLandscape.h                   10 ...
frmExportData.cpp              1005 ...
frmExportData.h                  13 ...
frmOutputStatistics.cpp        2438 ...
frmOutputStatistics.h            12 ...
frmGotoPlot.cpp                 124 ...
frmGotoPlot.h                    10 ...
frmCreateNew.cpp                629 ...
frmCreateNew.h                   10 ...
frmCvta.cpp                     973 ...
frmCvta.h                        12 ...
frmOutput.cpp                   267 ...
frmOutput.h                      10 ...
frmRegen.cpp                   2397 ...
frmRegen.h                       21 ...
frmViewPlots.cpp                301 ...
frmViewPlots.h                    9 ...
frmViewRegen.cpp                265 ...
frmViewRegen.h                    9 ...
DCSilvah.rc                      75 ...
===================================================
                              14572 Lines of Code
[CODE]

The dllSilvah Project and Lines of Code are as follows...

[CODE]
dllMain.cpp                    2327 Lines of Code
dllMain.h                       286 ...
dllStrings.cpp                 1409 ...
dllStrings.h                    150 ...
===================================================
                               4172 Lines of Code
                             
 

So you can see we're talking about a pretty serious project here with about 18,000 lines of code.  The uncompacted and compacted sizes for this project's two binaries are as follows...


DCSilvah.exe   163,328 Bytes Uncompacted     60,928  Bytes UPX'ed
dllSilvah.dll   34,816 Bytes Uncompacted     18,432  Bytes UPX'ed
==============================================================================
                198,144 Bytes Uncompacted    79,360  Total Compacted With UPX 


So I have an 18,000 lines of code project building to just 198 k uncompacted or 79 k compacted!  And of course these are all 'stand alone' builds not requiring any additional libraries other than required Windows System Libraries.  I'm happy with that!

Attached is the current and latest (8/8/2016) TCLib and files.

  •  

James C. Fuller

Fred,
  Looks pretty good.
I was able to make the changes I needed (I think :)) but have not fully tested yet.

I did have to rem the #define FILE_OUTPUT in Strings.h.

c:\bc9adp2\Include\TCLib\Strings.cpp(1391): error C2664: 'int (FILE *,const char *,...)': cannot convert argument 2 from 'const wchar_t [3]' to 'const char *'
c:\bc9adp2\Include\TCLib\Strings.cpp(1391): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
c:\bc9adp2\Include\TCLib\Strings.cpp(1393): error C2664: 'int (FILE *,const char *,...)': cannot convert argument 2 from 'const wchar_t [2]' to 'const char *'
c:\bc9adp2\Include\TCLib\Strings.cpp(1393): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
c:\bc9adp2\Include\TCLib\Strings.cpp(1399): error C2664: 'int (FILE *,const char *,...)': cannot convert argument 2 from 'const wchar_t [5]' to 'const char *'
c:\bc9adp2\Include\TCLib\Strings.cpp(1399): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
c:\bc9adp2\Include\TCLib\Strings.cpp(1401): error C2664: 'int (FILE *,const char *,...)': cannot convert argument 2 from 'const wchar_t [2]' to 'const char *'
c:\bc9adp2\Include\TCLib\Strings.cpp(1401): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast


Currently I am only using normal "c" type strings with the port of José's CWindow and Afx package.

Thank you very much for all your hard work!!

James


  •