/**************************************************************************
 * Source Id :
 *
 * $Id: generate.cpp,v 1.5 2001/04/20 10:15:01 georgetasker Exp $
 *-------------------------------------------------------------------------
 * Project Notes :
 *
 *  Diamond Base
 *  ============
 *      A solid database implementation, spurred on by the continuing
 *  Metal (Lead) Base saga.
 *
 *  Project Team :
 *        A. Davison
 *        K. Lentin
 *        D. Platt
 *
 *	Project Commenced : 05-02-1993
 *
 *-------------------------------------------------------------------------
 *  Module Notes :
 *
 *  Object header and code generator.
 *
 *
 *  Original Author : Krazy Kev
 *
 **************************************************************************/

// FOR NOW::
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#include <diamond/dbconf.h>

#if HAVE_MALLOC_H && !MALLOC_H_INCLUDED
extern "C" {
#include <malloc.h>
}
#define MALLOC_H_INCLUDED 1
#endif
#include <fstream.h>

#if defined(__BORLAND_C__) || defined(_MSC_VER)
#include <strstrea.h>
#else 
#include <strstream.h>
#endif

#include <string.h>
#include <diamond/defs.h>
#include <diamond/utils.h>
#include <diamond/idxinfo.h>
#include <diamond/d_types.h>
#include <diamond/dblimit.h>
#include <diamond/generate.h>

// mySizeOf:
// Since all our types are merely numbers, we need our own sizeof

#if defined(__BORLANDC__) || defined(_MSC_VER)
const char *ccFileExtension = ".cpp";
#else
const char *ccFileExtension = ".cc";
#endif

size_t 
const mySortOrder(const int32 tp)
{
	switch (tp)
	{
		case D_INT64:
		case D_UINT64:
		case D_REAL64: return 4;
		case D_INT32:
		case D_UINT32: return 3;
		case D_SHORT:
		case D_USHORT:
		case D_DATA:
		case D_STRING:
		case D_LONG:
		case D_ULONG:
		case D_DOUBLE:
		case D_FLOAT:
		case D_MONEY:
		case D_DATE:
		case D_UNIQUE: return 2;
		case D_ICHAR:
		case D_BYTE:
		case D_CHAR: return 1;
	}
	return 0;
}
size_t 
const mySizeOf(const int32 tp)
{
	switch (tp)
	{
		case D_INT32:
		case D_UINT32: return 4;
		case D_INT64:
		case D_UINT64:
		case D_REAL64: return 8;
		case D_SHORT: return sizeof(short);
		case D_USHORT: return sizeof(ushort);
		case D_DATA:
		case D_STRING:
		case D_LONG: return sizeof(int32);
		case D_ULONG: return sizeof(uint32);
		case D_DOUBLE: return sizeof(double);
		case D_FLOAT: return sizeof(float);
		case D_MONEY: return sizeof(moneyType);
		case D_DATE: return sizeof(dateType);
		case D_ICHAR:
		case D_BYTE:
		case D_CHAR: return sizeof(char);
		case D_UNIQUE: return sizeof(uniqueType);
	}
	return 0;
}

// getMinimum
// Return the minimum value for a keyable type

dbString
const getMinimum(const int32 tp)
{
	switch (tp)
	{
		case D_SHORT: return DIA_SHORT_MIN;
		case D_FLOAT: return "-HUGE_VAL";
		case D_MONEY:
		case D_REAL64:
		case D_DOUBLE: return "-HUGE_VAL";
		case D_ICHAR:
		case D_CHAR: return DIA_CHAR_MIN;
		case D_BYTE: return "0";
		case D_INT32:
		case D_LONG: 
		case D_DATE:
		case D_UNIQUE: return "-0x80000000";
		case D_INT64: return "-0x8000000000000000";
		case D_ULONG:
		case D_UINT64:
		case D_UINT32:
		case D_USHORT: return "0";
	}
	return "Bad Karma";
}

// getUnknown
// Return the default" unknown" value for a type.

dbString
const getUnknown(const int32 tp)
{
	switch (tp)
	{
		case D_DATA: return "";
		case D_STRING: return "unknown";
		case D_SHORT: 
		case D_USHORT:
		case D_LONG: 
		case D_ULONG:
		case D_DOUBLE:
		case D_FLOAT:
		case D_MONEY:
		case D_INT32:
		case D_UINT32:
		case D_INT64:
		case D_UINT64:
		case D_REAL64:
		case D_DATE: return "-1";
		case D_BYTE: return "0";
		case D_ICHAR:
		case D_CHAR: return "-1";
		case D_UNIQUE: return "-1";
	}
	return "Bad Karma";
}

// typeName:
// This is needed to output the correct code based on our form of types

dbString
const typeName(const int32 tp, const bool realData/*=false*/)
{
	dbString tpName;

	switch (tp) {
		case D_SHORT: tpName ="short"; break;
		case D_USHORT: tpName ="ushort"; break;
		case D_DATA: tpName =realData?"dbData":"int32"; break;
		case D_STRING: tpName =realData?"dbString":"int32"; break;
		case D_LONG: tpName ="int32"; break;
		case D_INT32: tpName ="int32"; break;
		case D_UINT32: tpName ="uint32"; break;
		case D_INT64: tpName ="int64"; break;
		case D_UINT64: tpName ="uint64"; break;
		case D_REAL64: tpName ="real64"; break;
		case D_ULONG: tpName ="uint32"; break;
		case D_DOUBLE: tpName ="double"; break;
		case D_FLOAT: tpName ="float"; break;
		case D_MONEY: tpName ="moneyType"; break;
		case D_DATE: tpName ="dateType"; break;
		case D_CHAR: tpName ="char"; break;
		case D_BYTE: tpName ="DIA_byte"; break;
		case D_ICHAR: tpName ="ichar"; break;
		case D_UNIQUE: tpName ="uniqueType"; break;
		default: tpName = "THISISABUG"; break;
	}
	return tpName;
}

// genUniqueArray:
// Generate the list of bool's that specify is a field is unique

void genUniqueArray(ofstream& of, const indexList* idxList, const fieldList* fldList)
{
	indexInfo* i = idxList->indicies;
	int32 u = -1;
	int32 fldNum = 0;

	// Do we have a unique field?

	for (fieldInfo* f = fldList->fields; f; f=f->nextFld, fldNum++)
		if (f->fldType == D_UNIQUE)
		{
			u = fldNum;
			break;
		}

	// No, leave
	if (u==-1)
	{
		for (int32 l=0; l < idxList->numIndicies; l++)
		{
			if (l)
				of << ",";
			of << "false";
		}
		return;
	}

	// Now go through the indicies
	while (i)
	{
		if (i != idxList->indicies) of << ","; // Add a comma to separate
		bool gotIt = false;

		for (int j=0; j<MAX_FIELDS_IN_INDEX && i->idxFields[j] != -1;  j++)
		{
			// Is this the unique field?
			if (u == i->idxFields[j])
			{
				gotIt = true;
				break;
			}
		}

		// output it
		of << (gotIt?"true":"false");
		i=i->nextIdx;
	}
}

// genArray:
// Generate the list of index sizes for the indexSize array

//void genArray(ofstream& of, indexList* idxList, fieldList* fldList)
void genArray(ofstream& of, const dbString& className, const indexList* idxList)
{
	for (int32 i=0; i < idxList->numIndicies; i++)
	{
		if (i)
			of << ",";
		of << "sizeof(_" << className << "_idxStr" << i << ")";
	}
#if 0
	indexInfo* i = idxList->indicies;

	while (i)
	{
		if (i != idxList->indicies) of << ","; // Add a comma to separate
		int32 len = 0;

		// This ugly loopy thing turns up a lot. It is because we store
		// field numbers in the index list, yet our fields are not directly
		// accessable by those numbers. Given the infrequency of use of the
		// gerenator compared to the rest of the system, there are more
		// important things to fix.

		for (int j=0; j<MAX_FIELDS_IN_INDEX && i->idxFields[j] != -1;  j++)
		{
			fieldInfo* f = fldList->fields;
			for (int k=0; k < i->idxFields[j]; k++)
				f=f->nextFld;

			// Add up the field length
			len+=f->fldSize;
		}

		// output it
		of << len;
		i=i->nextIdx;
	}
#endif
}

// genStructs:
// This generates the structure to store the relation data in.

void genStructs(ofstream& of, const dbString& className, const indexList* idxList, const fieldList* fldList)
{
	indexInfo* i = idxList->indicies;
	int num=0;

	while (i)
	{
		// OK, here's another ugli-ish thing. For historical (or maybe
		// hysterical) reasons, the \n's are at the start of all these long
		// strings. One day, when I have lots of time and patience, I will put
		// them on the end of the strings, and get rid of the ugly \'s on the
		// ends of all the lines.

		of << "\
\nstruct _" << className << "_idxStr" << num << " {\
";
		for (int32 ch=4; ch; ch--)
			for (int j=0; j<MAX_FIELDS_IN_INDEX && i->idxFields[j] != -1;  j++)
			{
				fieldInfo* f = fldList->fields;
				for (int k=0; k < i->idxFields[j]; k++)
					f=f->nextFld;

				// This loop is executed twice. During the first pass, no
				// char types are included. These are added on the end during
				// the second pass. This is to solve alignment problems caused
				// by single chars in the middle of a series of word aligned
				// fields.
				// Upgraded to sort new types better but leave old types in 
				// buggy order.
				if (mySortOrder(f->fldType) == (unsigned int)ch)
				{
					of << "\
\n	" << typeName(f->fldType) << "	";

					// This won't happen since they're not supported yet
					// but I typed it in by mistake so leave it here. It will
					// be needed later.
					if (f->fldType == D_STRING || f->fldType == D_DATA)
					{
						of << "_off_";
					}
					of << f->fldName;

					// Is this an array?
					if (f->fldSize != mySizeOf(f->fldType))
					{
						of << "[" << f->fldSize / mySizeOf(f->fldType) << "]";
					}
					of << ";";
				}
			}
		of << "\
\n};\
\n\
";
		i=i->nextIdx;
		num++;
	}
}

// genUnique:
// This generates 3 different methods. {get,set,has}Unique
// They manipulate the Unique member if it exists.
// The type parameter is number 0 1 2 for set get has

void genUnique(ofstream& of, const fieldList* fldList, const int32 type)
{
	if (type)
		of << "return";
	for (fieldInfo* f = fldList->fields; f; f=f->nextFld) {
		if (f->fldType == D_UNIQUE) {
			if (!type) { // set
				of << "\
\n	void setUnique(const int32 uniq) { " <<  f->fldName << " = uniq; } ";
			}
			else if (type == 1) { // get
				of << " " << f->fldName << ";";
			}
			else {// has
				of << " true;";
			}
			return;
		}
	}
	// There is no unique field
	if (!type) // set
	{
		//of << ";";
		of << "\
\n	void setUnique(const int32) { ; } ";
	}
	else if (type == 1) // get
	{
		of << " -1;";
	}
	else // has
	{
		of << " false;";
	}
}

// genConsDecls:
// Generate header declarations for the constructors.

void genConsDecls(ofstream& of, const unsigned char* className, const fieldList* fldList, const indexList* consList, bool& voidDone)
{
	indexInfo* c = consList->indicies;

	while (c)
	{
		of << "\
\n	" << className << "(";

		int32 count = 0;
        for (int j=0; j<MAX_FIELDS_IN_INDEX && c->idxFields[j] != -1;  j++)
        {
            fieldInfo* f = fldList->fields;
            for (int k=0; k < c->idxFields[j]; k++)
                f=f->nextFld;
			if (j)
				of << ", ";
			of << typeName(f->fldType,true);
			if (f->fldSize != mySizeOf(f->fldType))
				of << "*";
			of << " _cons_" << f->fldName;
			count++;
        }

		if (!count)
			voidDone = true;
		of << ");";

        c=c->nextIdx;
	}
}

// genConsDefns:
// Generate definitions for the constructors.

void genConsDefns(ofstream& of, const unsigned char* className, const unsigned char* structName, const fieldList* fldList, const indexList* consList)
{
	indexInfo* c = consList->indicies;

	while (c)
	{
		int j;
		of << "\
\n" << className << "::" << className << "(";

        for (j=0; j<MAX_FIELDS_IN_INDEX && c->idxFields[j] != -1;  j++)
        {
            fieldInfo* f = fldList->fields;
            for (int k=0; k < c->idxFields[j]; k++)
                f=f->nextFld;
			if (j)
				of << ", ";
			of << typeName(f->fldType, true);
			if (f->fldSize != mySizeOf(f->fldType))
				of << "*";
			of << " _cons_" << f->fldName;
        }

		of << ") : diaRel(\"" << className << "\")," << structName << "() {\
\n	commonInit();\
";

        for (j=0; j<MAX_FIELDS_IN_INDEX && c->idxFields[j] != -1;  j++)
        {
            fieldInfo* f = fldList->fields;
            for (int k=0; k < c->idxFields[j]; k++)
                f=f->nextFld;
			if (f->fldSize / mySizeOf(f->fldType) == 1)
				of << "\
\n	" << f->fldName << " = _cons_" << f->fldName << ";";
			// az: I don't think that D_BYTE belongs here, since byte arrays may
      // contain embedded 0's
			else if (f->fldType == D_CHAR || /*f->fldType == D_BYTE ||*/
				f->fldType == D_ICHAR)
				of << "\
\n	strNcpy((char*)" << f->fldName << ",_cons_" << f->fldName << "," << f->fldSize << ");";
			else of << "\
\n	copyMem(_cons_" << f->fldName << "," << f->fldName << "," << f->fldSize << ");";
        }

		if (c->idxType != -1)
			of << "\
\n	get(" << c->idxType << ");\
";
		of << "\
\n};\
\n\
";

        c=c->nextIdx;
	}
}

// genGetSetCases:
// This is used to generated the case's contained in the switches in getKey
// and setKey.

void genGetSetCases(ofstream& of, const dbString& className, const indexList* idxList, const fieldList* fldList, const bool isGet)
{
	indexInfo* i = idxList->indicies;
	int32 caseNum = 0;

	while (i)
	{
		of << "\
\n		case " << caseNum++ << ":\
\n		{\
";
		int32 keyOff = 0;
		int32 forNum = 0;
		for (int j=0; j<MAX_FIELDS_IN_INDEX && i->idxFields[j] != -1;  j++)
		{
			bool tab;
			fieldInfo* f = fldList->fields;
			for (int32 k=0; k < i->idxFields[j]; k++)
			{
				f=f->nextFld;
			}

			// Got the right field

			// If this is a CHAR type of more than one char (a string) then
			// use strncpy to get the string. Makes things a little quicker.
			// NB Earlier I commented about not worrying about efficiency of
			// generate. This is true, however, the efficiency of the code that
			// genarate produces is quite important.

				// az: strNcpy will not copy byte arrays with embedded 0's correctly
			if ((f->fldType == D_CHAR || /*f->fldType == D_BYTE ||*/
				f->fldType == D_ICHAR) && f->fldSize != 1)
				if (isGet)
					of << "\
\n			strNcpy(((_" << className << "_idxStr" << caseNum-1 << "*)_db_buffer)->" << f->fldName << ", (char*)" << f->fldName << ", " << f->fldSize << ");\
";
				else
					of << "\
\n			strNcpy((char*)" << f->fldName << ", ((_" << className << "_idxStr" << caseNum-1 << "*)_db_buffer)->" << f->fldName << ",  " << f->fldSize << ");\
";
			else if (f->fldType == D_BYTE && f->fldSize != 1) {
				// az: strNcpy will not copy byte arrays with embedded 0's correctly
				if (isGet)
					of << "\
\n			memcpy(((_" << className << "_idxStr" << caseNum-1 << "*)_db_buffer)->" << f->fldName << ", (char*)" << f->fldName << ", " << f->fldSize << ");\
";
				else
					of << "\
\n			memcpy((char*)" << f->fldName << ", ((_" << className << "_idxStr" << caseNum-1 << "*)_db_buffer)->" << f->fldName << ",  " << f->fldSize << ");\
";
			} // if
			else
			{
				// If this is an array then we must loop around copying
				// each of the elements individually.
				// Why not use bcopy of the whole block? Because the code was
				// 50 times slower when we did that!

				// Note that we use the variable a##forNum so that the loop
				// variables to not clash, given the unfortunate scope rules
				// of C++ and for loops.

				if (f->fldSize != mySizeOf(f->fldType))
				{
					of << "\
\n			for (int a" << forNum << "=0;a" << forNum << "<" << f->fldSize << ";a"
<< forNum++ << "+=" << mySizeOf(f->fldType) << ")\
";
					// The tab is just to make sure that things all line up
					// correctly in the generated code. We might as well make
					// things look good.

					tab = true;
				}
				else
					tab = false;

				if (isGet)
					of << "\
\n		" << (tab?"\t":"") << "	((_" << className << "_idxStr" << caseNum-1 << "*)_db_buffer)->" << f->fldName << " = " << f->fldName << ";";
				else
					of << "\
\n		" << (tab?"\t":"") << "	" << f->fldName << " = ((_" << className << "_idxStr" << caseNum-1 << "*)_db_buffer)->" << f->fldName << ";";

			}

			// The offset into the key where we cast the data we want from
			keyOff += f->fldSize;
		}
		of << "\
\n			break;\
\n		}\
";
		i = i->nextIdx;
	}
}

// genCompareCases:
// This does a similar job to genGetSetCases except it's used for the
// compare function.

bool
genCompareCases(ofstream& of, const dbString& className, const indexList* idxList, const fieldList* fldList)
{
	indexInfo* i = idxList->indicies;
	int32 caseNum = 0;
	bool res = false;

	while (i)
	{
		of << "\
\n		case " << caseNum++ << ":\
\n		{\
";
		int32 keyOff = 0;
		int32 forNum = 0;
		for (int j=0; j<MAX_FIELDS_IN_INDEX && i->idxFields[j] != -1;  j++)
		{
			bool tab;
			fieldInfo* f = fldList->fields;
			for (int k=0; k < i->idxFields[j]; k++)
				f=f->nextFld;

			if (f->fldType == D_CHAR && f->fldSize != 1)
				of << "\
\n			if (0 != (res=strncmp(" << f->fldName << ", ((_" << className << "_idxStr" << caseNum-1 << "*)_db_key)->" << f->fldName << ", " << f->fldSize << ")))\
\n				break;\
";
			//
			// Byte case.
			//
			else
			// az: if the field length is 1, then just compare the bytes
			if (f->fldType == D_BYTE && f->fldSize != 1)
				of << "\
\n			if (0 != (res=myMemCmp(" << f->fldName << ", ((_" << className << "_idxStr" << caseNum-1 << "*)_db_key)->" << f->fldName << ", " << f->fldSize << ")))\
\n				break;\
";
			//
			// Case insensitive character.
			//
			else
			if (f->fldType == D_ICHAR && f->fldSize != 1)
				of << "\
\n			if (0 != (res=strncasecmp(" << f->fldName << ", ((_" << className << "_idxStr" << caseNum-1 << "*)_db_key)->" << f->fldName << ", " << f->fldSize << ")))\
\n				break;\
";
			else
			{
				if (f->fldSize != mySizeOf(f->fldType))
				{
					of << "\
\n			for (int a" << forNum << "=0;a" << forNum << "<" << f->fldSize << ";a"
<< forNum++ << "+=" << mySizeOf(f->fldType) << ")\
";
					tab = true;
				}
				else
					tab = false;

				// We use the generated compare functions
				of << "\
\n		" << (tab?"\t":"") << "	if (0 != (res=compare_" << typeName(f->fldType) << "((char*)(&" << f->fldName << "), (char*)(&((_" << className << "_idxStr" << caseNum-1 << "*)_db_key)->" << f->fldName << "))))\
\n			" << (tab?"\t":"") << "	goto end_compare;\
";
				res = true;
			}
			keyOff += f->fldSize;
		}

		of << "\
\n			break;\
\n		}\
";
		i = i->nextIdx;
	}
	return res;
}

// generateIterators
// Generates as many sensible iterators for the given class as it can.

void
generateIterators(ofstream& of, const dbString& className, 
	const indexList* idxList, const fieldList* /* fldList */)
{
	of << "#define DIA_FORALL_" << className << "(r) " << className << " r; for(r.begin(0), r.first(); r.ok(); r.next())\n";

	for(indexInfo* i = idxList->indicies; i; i=i->nextIdx)
	{
		of << "#define DIA_FORALL_" << className << "_" << i->name << "(r) " << className << " r; for(r.begin(" << className << "::dbIdx_" << i->name <<"), r.first(); r.ok(); r.next())\n";
	}
}

// generateStart:
// This starts off the generation process by forming the file names and
// creating the relevant streams. It also outputs all the non-schema-depandant
// stuff that starts each file.

bool generateStart(ofstream& hf, ofstream& cf, const dbString& filename)
{
	char *s;
	dbString incname;
	dbString fle = filename;
	char *file = fle;

	dbString file_s(file);
#ifdef FILENAMELEN

	// We have a filename length problem (eg Minix file systems)
	long extra = file_s.len() - FILENAMELEN + 4;
	if (extra > 0) {// do we have enough space
		file_s[file_s.len() - extra]=0; // chop the filename off
	}
#endif
	file_s = file_s + "_s.h";

	// Strip off all leading paths (Unix and PC style)

	if ((s=strrchr((char *)file, '/')) != 0)
		file= s+1;
	if ((s=strrchr((char *)file, '\\')) != 0)
		file= s+1;

	incname = filename + ".h";

	cout << "incname:" << incname << endl;
	cout << "incname:" << (char*)incname << endl;
	cout << "filename:" << filename << endl;
	cout << "file:" << file << endl;
	cout << "fle:" << fle << endl;
	hf.open(incname);
	if (!hf)
	{
		// Couldn't open, clean up and go.
		return false;
	}

	dbString codename = filename + ccFileExtension;
	cout << "codename:" << codename << endl;
	cf.open(codename);
	if (!cf)
	{
		return false;
	}

	// The top of the include file
	hf << "\
\n//\
\n// Include file for " << filename << ".s schema file.\
\n//\
\n// Automatically generated: Generator Revison $Rev$\
\n//\
\n\
\n#ifndef __" << file << "_H_\
\n#define __" << file << "_H_\
\n\
\n#include <diamond/diarel.h>\
\n#include <diamond/utils.h>\
\n\
\n#include \"" << file_s << "\"\
";

	// The .cc class definition file.
	// The ORD_COMPARE and REAL_COMPARE defines are used to declare COMPARE
	// functions for each of our types. I leave them in here, and not in the
	// library or somewhere else so that these files are fairly self
	// contained.
	//
	// Hopefully, most compilers can inline these nicely for us.

	cf << "\
\n//\
\n// Class definition file for " << filename << ".s schema file.\
\n//\
\n// Automatically generated: Generator Revison $Rev$\
\n//\
\n\
\n#include <iostream.h>\
\n#include \"" << file << ".h\"\
\n#if HAVE_STDLIB_H\
\n#include <stdlib.h>\
\n#endif\
\n#include <string.h>\
\n#include <stdio.h>\
\n#include <ctype.h>\
\n\
\nchar* strNcpy(char* d, char* s, int n);\
\n#define ORD_COMPARE(t) \\\
\n	inline int32 const compare_##t(const pKeyType k1, const pKeyType k2) \\\
\n	{ \\\
\n		t _t1 = *((t *)k1); \\\
\n		t _t2 = *((t *)k2); \\\
\n		return (_t1==_t2)?0:((_t1<_t2)?-1:1); \\\
\n	} \
\n\
\n#define REAL_COMPARE(t) \\\
\n	inline int32 const compare_##t(const pKeyType k1, const pKeyType k2) \\\
\n	{ \\\
\n		t _t1 = *((t *)k1); \\\
\n		t _t2 = *((t *)k2); \\\
\n		return (_t1==_t2)?0:((_t1<_t2)?-1:1); \\\
\n	}\
\n\
\ninline int32 const compare_ichar(const pKeyType k1, const pKeyType k2)\
\n{\
\n	char _t1 = (char)tolower(*((char*)k1));\
\n	char _t2 = (char)tolower(*((char*)k2));\
\n	return (_t1==_t2)?0:((_t1<_t2)?-1:1);\
\n}\
\n\
\nORD_COMPARE(short);\
\nORD_COMPARE(ushort);\
\nORD_COMPARE(int32);\
\nORD_COMPARE(uint32);\
\nORD_COMPARE(dateType);\
\nORD_COMPARE(char);\
\nORD_COMPARE(DIA_byte);\
\nORD_COMPARE(uniqueType);\
\nREAL_COMPARE(float);\
\nREAL_COMPARE(double);\
\nREAL_COMPARE(moneyType);\
";

	// Clean up and go.
	return true;
}

// generateClass:
// This does all the work generating a class. It uses all the above
// functions to keep things neat (well, sort of neat anyway).

bool generateClass(ofstream& hf, ofstream& cf, const int32 recLen, 
	const indexList* idxList, const fieldList* fldList, 
	const dbString& structName, const dbString& className, 
	const int stringCount, const indexList* consList, 
	dbString * const defaults
)
{
	if (!hf) return false;

	hf << "\
\n//\
\n// Declaration of class for " << className << "\
\n//\
";
	genStructs(hf, className, idxList, fldList);
	hf << "\
\n\
\nclass " << className << " : \
\n		public diaRel,\
\n		public " << structName;
	if (stringCount)
		hf << ",\
\n		public _Strings_" << structName << "\
";
	hf << "\
\n{\
\npublic:\
\n	static const char* relName;\
\n\
\n	inline virtual operator void*() const { return (void*)((" << structName << "*)this); }\
\n	inline virtual operator char*() const { return (char*)((" << structName << "*)this); }\
\n	inline virtual char* theData(void) const { return (char*)((" << structName << "*)this); }\
\n	inline virtual int32 getDataLength(void) const { return " << recLen << "; };\
\n	pKeyType getKey(const int32 _db_keyNum=0) const;\
\n	static const bool uniqueFields[" << idxList->numIndicies << "];\
\n	bool isUnique(const int32 _db_keyNum) const\
\n	{\
\n		if (_db_keyNum==-1)\
\n			return false;\
\n		else\
\n			return uniqueFields[_db_keyNum];\
\n	}\
\n\
\n	static const int32 keyLengths[" << idxList->numIndicies << "];\
\n	inline virtual int32 getKeyLength(const int32 _db_keyNum) const\
\n	{\
\n		if (_db_keyNum==-1)\
\n			return 0;\
\n		else\
\n			return keyLengths[_db_keyNum];\
\n	}\
\n\
\n	virtual int32 compare(const pKeyType _db_key, const int32 _db_keyNum=0) const;\
";
	bool voidCons = false;
	genConsDecls(hf, className, fldList, consList, voidCons);
	if (!voidCons)
		hf << "\
\n	" << className << "();\
";
	hf << "\
\n	" << className << "(pKeyType _db_newKey, int32 _db_keyNum);\
\n	" << className << "& operator=(const " << className << "& X);\
\n	void commonInit(void);\
\n	void setKey(const pKeyType _db_newKey, const int32 _db_keyNum=0);\
\n	int32 getUnique(void) const { ";
	genUnique(hf, fldList, 1);
	hf << " }\
\n	bool hasUnique(void) const { ";
	genUnique(hf, fldList, 2);
	hf << " }";
	genUnique(hf, fldList, 0);
	hf << "\
\n	int32 numStrings(void) const { return " << stringCount << "; }\
";
	if (stringCount)
		hf << "\
\n	_db_strStruct strArray[" << stringCount << "];\
\n	dbData* getString(const int32 num) const { return strArray[num].theString; }\
\n	int32* getLong(const int32 num) const { return strArray[num].theLong; }\
";
	hf << "\
\n	const _db_strStruct* getArray(void) const { return \
";
	hf << (stringCount?"strArray":"0") << ";}\
\n	operator dbError();\
\n	const char* verStr(void) const { return \"$Id: generate.cpp,v 1.5 2001/04/20 10:15:01 georgetasker Exp $\"; }\
\n};\
\n\
";
	generateIterators(hf, className, idxList, fldList);

	if (!cf) return false;

	cf << "\
\n\
\n//\
\n// Definitions for class " << structName << "\
\n//\
\n\
\nconst char* " << className << "::relName = \"" << className << "\";\
\nconst bool " << className << "::uniqueFields[" << idxList->numIndicies << "] = {";
	genUniqueArray(cf, idxList, fldList);
	cf << "};\
\nconst int32 " << className << "::keyLengths[" << idxList->numIndicies << "] = {";
	genArray(cf, className, idxList);
	cf << "};\
\n\
\nvoid " << className << "::commonInit(void)\
\n{\
\n	memset((" << structName << "*)this, 0, sizeof(" << structName << "));\
\n	setUnique(-1);\
";
	int i=0,j=0;
	for (fieldInfo *f = fldList->fields; f; f=f->nextFld, j++) {
		if (defaults[j].len())
		{
			if (strcasecmp(defaults[j],"unknown") == 0)
				defaults[j]=getUnknown(f->fldType);

			//
			// CHAR and ICHAR types
			//
			if ((f->fldType == D_CHAR || f->fldType == D_ICHAR) && 
				f->fldSize != 1) 
			{
				cf << "\
\n	strNcpy((char*)" << f->fldName << ", " << defaults[i] << ", " << f->fldSize << ");\
";
			}
			// az: if the field length is 1, then just assign the bytes
			else if (f->fldType == D_BYTE && f->fldSize != 1) 
			{
				cf << "\
\n	copyMem((char*)" << defaults[i] << ", " << f->fldName << ", " << f->fldSize << ");\
";
			}
			else
				cf << "\
\n	" << f->fldName << " =" << defaults[j] << ";";
		}
		if (f->fldType == D_STRING || f->fldType == D_DATA)
			cf << "\
\n	_off_" << f->fldName << "=0;\
\n	strArray[" << i++ << "] = _db_strStruct(&_off_" << f->fldName << ",&" << f->fldName << ");\
";
	}
	cf << "\
\n}\
\n\
";
	genConsDefns(cf, className, structName, fldList, consList);
	if (!voidCons)
		cf << "\
\n" << className << "::" << className << "() : diaRel(\"" << className << "\")," << structName << "()\
\n{\
\n	commonInit();\
\n}\
";
	cf << "\
\n\
\n" << className << "::" << className << "(pKeyType _db_newKey, int32 _db_numKey) : diaRel(\"" << className << "\"), " << structName << "()\
\n{\
\n	commonInit();\
\n	setKey(_db_newKey, _db_numKey);\
\n}\
\n\
\n" << className << "& " << className << "::operator=(const " << className << "& X)\
\n{\
\n	if (&X != this) {\
\n		diaRel::operator=(X);\
\n		*((" << structName << "*)this) = (" << structName << ")(X);\
\n	}\
\n	return *this;\
\n}\
\n\
\npKeyType " << className << "::getKey(const int32 _db_keyNum) const\
\n{\
\n	char* _db_buffer = new char[getKeyLength(_db_keyNum)];\
\n\
\n	switch (_db_keyNum)\
\n	{";

	genGetSetCases(cf, className, idxList, fldList, true);

	cf << "\
\n	}\
\n	return (pKeyType)_db_buffer;\
\n}\
\n\
\nvoid " << className << "::setKey(const pKeyType _db_buffer, const int32 _db_keyNum)\
\n{\
\n	switch (_db_keyNum)\
\n	{";

	genGetSetCases(cf, className, idxList, fldList, false);

	cf << "\
\n	}\
\n}\
\n\
\n" << className << "::operator dbError()\
\n{\
\n	return status;\
\n}\
\n\
\nint32 " << className << "::compare(const pKeyType _db_key, const int32 _db_keyNum) const\
\n{\
\n	//char* myKey = (char*)getKey(_db_keyNum);\
\n	int32 res=0;\
\n\
\n	switch (_db_keyNum)\
\n	{\
";
	bool label = genCompareCases(cf, className, idxList, fldList);

	cf << "\
\n	}\
\n";
	if (label)
		cf << "\
\nend_compare:";
	cf << "\
\n\
\n	//delete myKey;\
\n	return res;\
\n}\
";
	return true;
}

// generateEnd:
// End off the include file and close the streams.

bool generateEnd(ofstream& hf, ofstream& cf)
{
	if (!hf) return false;
	if (!cf) return false;

	hf << "\
\n\
\n#endif" << endl;

	cf << endl;

	hf.close();
	cf.close();
	return true;
}

void genDeriveCons(ofstream& of, const dbString& base, const dbString& derive, const fieldList* fldList, const indexList* consList, const bool doCode)
{
	indexInfo* c = consList->indicies;

	while (c)
	{
		int j;
		if (doCode)
			of << "\
\n" << derive << "::" << derive << "(";
		else
			of << "\
\n	" << derive << "(";

        for (j=0; j<MAX_FIELDS_IN_INDEX && c->idxFields[j] != -1;  j++)
        {
            fieldInfo* f = fldList->fields;
            for (int k=0; k < c->idxFields[j]; k++)
                f=f->nextFld;
			if (j)
				of << ", ";
			of << typeName(f->fldType,true);
			if (f->fldSize != mySizeOf(f->fldType))
				of << "*";
			of << " _cons_" << f->fldName;
        }

		of << ")";

		if (doCode)
		{
			of << " : " << base << "(";
			for (j=0; j<MAX_FIELDS_IN_INDEX && c->idxFields[j] != -1;  j++)
			{
				fieldInfo* f = fldList->fields;
				for (int k=0; k < c->idxFields[j]; k++)
					f=f->nextFld;
				if (j)
					of << ", ";
				of << " _cons_" << f->fldName;
			}
			of << ")\
\n{\
\n	// Something in here would be nice\
\n}\
\n\
";
		}
		else
		{
			of << ";";
		}

        c=c->nextIdx;
	}
}

bool
generateDerived(const dbString& base, const dbString& derive, const dbString& deriveName, const fieldList* fldList, const indexList* consList, const unsigned char* basePath, const unsigned char* baseHeader)
{
	dbString s;
	s = deriveName + ".h";
	ofstream hf(s);
	s = deriveName + ccFileExtension;
	ofstream cf(s);

	cf << "\
\n//\
\n// Derived class " << derive << " from base class " << base << ".\
\n// Generated by DiamondBase\
\n// $Id: generate.cpp,v 1.5 2001/04/20 10:15:01 georgetasker Exp $\
\n\
";
	if ((basePath != 0) && (basePath[0] != 0))
		cf << "\n#include \"" << basePath << "/" << baseHeader << ".h\"";
	else
		cf << "\n#include \"" << baseHeader << ".h\"";

	cf << "\
\n#include \"" << deriveName << ".h\"\
\n\
";

	hf << "\
\n//\
\n// Derived class " << derive << " from base class " << base << ".\
\n// Generated by DiamondBase\
\n// $Id: generate.cpp,v 1.5 2001/04/20 10:15:01 georgetasker Exp $\
\n\
\n#ifndef __" << deriveName << "_H__\
\n#define __" << deriveName << "_H__\
\nclass " << derive << " : public " << base << "{\
\npublic:\
";
	genDeriveCons(hf, base, derive, fldList, consList, false);
	genDeriveCons(cf, base, derive, fldList, consList, true);

	hf << "\
\n};\
\n#endif";
	return true;
}

///////////////////////////////////////////////////////////////////////////
// Revision History:
//
// $Log: generate.cpp,v $
// Revision 1.5  2001/04/20 10:15:01  georgetasker
// Cygwin compilation fixes, compilation speed enhancements
// Code modified to get rid of some compiler warnings
//
// Revision 1.4  2001/04/19 21:12:54  georgetasker
// cygwin compilation fixes
//
// Revision 1.3  2001/04/05 01:22:33  georgetasker
// Same fix for Borland as I just did for MSVC so that <strstrea.h> is used if it is either of these two compilers, otherwise use <strstream.h>
//
// Revision 1.1  2001/04/04 12:05:49  aleksandras
// forgotten diamond .cpps
//
// Revision 1.75  1996/08/27  12:45:10  daz
// Fixed limited filename lengths for db.
//
// Revision 1.74  1996/08/22  11:35:20  dmb
// Cleaned up warning message
//
// Revision 1.73  1996/08/20 15:40:38  daz
// FIxed byte support
//
// Revision 1.72  1996/08/20  13:25:23  daz
// Changes by Alex Zeltser to support NT using MSVC.
//
// Revision 1.71  1996/08/06  10:46:07  daz
// Changes to incorporate the 'byte' type and the Windows NT patches.
//
// Revision 1.70  1996/03/05  21:42:35  dmb
// Preliminary modifications to get Dos/Windows 0.4 portability.
//
// Revision 1.69  1996/02/01  22:22:03  dmb
// long->int32.
//
// Revision 1.68  1995/10/05  07:22:26  kevinl
// Added 32bit and 64bit types and fixed sorting order for them
//
// Revision 1.67  1995/09/14 04:28:11  kevinl
// TypeName returns int32.
// Started iterators
//
// Revision 1.66  1995/06/27 13:27:16  kevinl
// Fixes to repair the gcc 2.7.0 carnage:
// modified for loop local variable scope semantics
// some new errors invented.
// moved some inline friends out of classes (stoopid - mystring)
// Modified the setUnique generated member
//
// Revision 1.65  1995/05/18  01:54:21  darrenp
// Added explicit (char *) cast to dbString parameters to strrchr
// Needed to keep borland happy.
//
// Revision 1.64  1994/12/22  03:55:38  kevinl
// s/bcopy/copyMem/g
//
// Revision 1.63  1994/12/17  04:12:23  kevinl
// Put in some int32's
//
// Revision 1.62  1994/12/12  03:43:31  kevinl
// stdlib.h and remove virtual on getKey
//
// Revision 1.61  1994/10/25  01:53:24  kevinl
// Removed an inline from getKey
//
// Revision 1.60  1994/08/01  00:12:32  darrenp
// Changed TRUE->true FALSE->false
//
// Revision 1.59  1994/07/04  12:18:34  kevinl
// Moved some inlines around, fixed some statics
//
// Revision 1.58  1994/05/15  04:58:17  kevinl
// Type fixups. Replace many char*'s with dbString
//
// Revision 1.57  1994/03/21  01:49:15  kevinl
// Small pointer casting problemmy thing in the assiognmetn opertaor
//
// Revision 1.56  1994/03/17  04:34:45  kevinl
// Added relName static
//
// Revision 1.55  1994/02/24  06:35:30  darrenp
// DOS/Windows modifications.
//
// Revision 1.54  1994/02/10  01:26:44  kevinl
// Fixed diamond/ in generated code.
//
// Revision 1.53  1994/02/03  06:01:32  kevinl
// Moved include files to diamond/ and fixed HAVE_MALLOC_H
//
// Revision 1.52  1993/12/23  22:58:48  kevinl
// dbErr uses dbString, autoconf stuff, moved logs, added const and size_t
//
// Revision 1.51  1993/11/18  02:58:33  kevinl
// Moved logs
// Fixed variable shadows
// Renamed some badle named variables in the generated code to remove clashes
//
// Revision 1.50  1993/11/15  04:30:19  davison
// Sorry - the last checkin had bugs. Damn management wanting early
// handovers (hi Kev :-)
//
// Revision 1.49  1993/11/15  04:18:51  davison
// A few fixes....
// Header wrapper added
// include files in derived .cc file
//
// Revision 1.48  1993/11/11  07:30:54  davison
// Derived classes now output true types, not the database reps for them.
//
// Revision 1.47  1993/11/08  10:25:50  kevinl
// Removed const from getUnknown.
// Removed ifdef OS2 for stricmp
// Added blank lines on the end of the header and code (some compiler
// burp with unterminated lines)
//
// Revision 1.46  1993/11/08  07:06:17  davison
// the keyword "unknown" has been added as a possible default type.
// The appropriate value for each data type is determined by   (^^^ um value)
// the function getUnknown.
//
// Revision 1.45  1993/11/05  12:45:05  kevinl
// Fixed problems that CC picked up (and some multi bugs)
//
// Revision 1.44  1993/11/03  12:19:27  kevinl
// Added ctype.h to generated code
//
// Revision 1.43  1993/11/03  12:09:11  davison
// Replaced missing brace in ichar comparision code.
//
// Revision 1.42  1993/11/03  11:51:31  davison
// Removed the default value from the (pKeyType,long) constructor.
//
// Revision 1.41  1993/11/03  10:04:46  kevinl
// Added ichar and utils.{h,cc}
//
// Revision 1.40  1993/10/28  00:40:08  davison
// Another missing backslash.
//
// Revision 1.39  1993/10/25  08:32:51  kevinl
// Let's try dbData constructors
//
// Revision 1.38  1993/10/24  15:35:45  kevinl
// Added derived class generation
//
// Revision 1.37  1993/10/24  09:14:26  kevinl
// Fixed unused idxList in genCons????
//
// Revision 1.36  1993/10/23  06:35:34  davison
// Changed bzero reference to memset.
//
// Revision 1.35  1993/10/19  14:27:37  kevinl
// Fixed constructors/retrievers
//
// Revision 1.34  1993/10/19  11:43:02  kevinl
// Default values
//
// Revision 1.33  1993/10/19  02:36:08  kevinl
// Added get to genConsDefn
//
// Revision 1.32  1993/10/19  01:06:40  davison
// Fixed a missing slash...
//
// Revision 1.31  1993/10/18  08:01:41  kevinl
// Added constructors and fixed some probs
//
// Revision 1.30  1993/09/27  01:15:44  kevinl
// Changed ORD_COMPARE to use < and >
//
// Revision 1.29  1993/09/26  06:40:32  kevinl
// Added dbData support
//
// Revision 1.28  1993/09/06  13:25:57  kevinl
// Removed a remnant hard coded classname, replaced with structName
//
// Revision 1.27  1993/08/29  12:56:27  kevinl
// Fixed problems of assigning relations to each other.
//
// Revision 1.26  1993/07/19  11:53:42  kevinl
// Removed some Borland warnings re incorrect assignments and a non-
// terminated string
//
// Revision 1.25  1993/07/11  09:42:05  kevinl
// Changed String to dbString
//
// Revision 1.24  1993/07/09  04:02:36  kevinl
// Fixed return in setUnique
// Nuked some ints
//
// Revision 1.23  1993/07/01  13:27:16  kevinl
// Pointers to uniques no longer returned. {has,get,set}Unqiue members
//
// Revision 1.22  1993/06/23  05:21:22  kevinl
// Mallocs are now in angular brackets
//
// Revision 1.21  1993/06/20  13:41:59  kevinl
// Fixed multiple mallocs
// Added String support
//
// Revision 1.20  1993/05/27  07:31:29  kevinl
// Some missing \'s which gcc -Wall didn't pick up!!!
//
// Revision 1.19  1993/05/26  00:58:24  kevinl
// Arrays for uniques and field lengths now not static
// Fixed inlines with multiple returns
// MALLOC_H_MISSING
//
// Revision 1.18  1993/05/12  02:19:25  kevinl
// Added verStr to generated class
// Fixed REAL_COMPARE
//
// Revision 1.17  1993/05/06  04:26:30  kevinl
// SASC for malloc.h
// Now uses key structures to align the keys
// Also fixed empty unique arrays
//
// Revision 1.16  1993/05/01  14:41:47  kevinl
// Conditional operator instead of trusting value of !=
//
// Revision 1.15  1993/04/27  07:10:23  kevinl
// Added uniqueArray and isUnique
// Comments
//
// Revision 1.14  1993/04/15  04:21:52  kevinl
// Moved malloc.h
//
// Revision 1.13  1993/04/14  14:17:15  kevinl
// Now we close the output streams
//
// Revision 1.12  1993/04/11  00:57:15  davison
// Added a dbError() cast operator to return the error status for the class.
//
// Revision 1.11  1993/04/08  15:40:23  kevinl
// Adjusted spacing in generated constructor
//
// Revision 1.10  1993/04/06  23:23:09  kevinl
// Bye Bye bcopy
//
// Revision 1.9  1993/04/04  23:59:34  kevinl
// Generate structs to hold aligned keys
//
// Revision 1.8  1993/04/01  11:23:23  kevinl
// Set unique field to -1 in constructor
//
// Revision 1.7  1993/03/30  14:39:02  kevinl
// Generate address of structure members so struct packing is not a factor
//
// Revision 1.6  1993/03/30  06:54:16  kevinl
// Added getUnique member
//
// Revision 1.5  1993/03/29  08:20:09  darrenp
// Added new malloc library support
//
// Revision 1.4  1993/03/25  22:20:10  davison
// Fixed last patch - needed a space after "public" in the
// class definition.
//
// Revision 1.3  1993/03/25  18:17:16  davison
// Make the generated class inherit the structure publicly.
//
// Revision 1.2  1993/03/24  06:17:54  kevinl
// fixed include file name, struct and class name
// Fixed duplicate/unused a loop variable counter, indentation etc
// Added theData() function
//
// Revision 1.1  1993/03/21  23:36:54  kevinl
// Initial revision
//
///////////////////////////////////////////////////////////////////////////
