/*
 *	PROGRAM:	JRD Access Method
 *	MODULE:		codes.epp
 *	DESCRIPTION:	Code and message include file generator
 *
 * The contents of this file are subject to the Interbase Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy
 * of the License at http://www.Inprise.com/IPL.html
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code was created by Inprise Corporation
 * and its predecessors. Portions created by Inprise Corporation are
 * Copyright (C) Inprise Corporation.
 *
 * All Rights Reserved.
 * Contributor(s): ______________________________________.
 *                 Mark O'Donohue skywalker@users.sourceforge.net
 */

#include "firebird.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "../jrd/common.h"
#include "../jrd/ibase.h"
#include "../jrd/msg_encode.h"

// The MSG.GDB database is found under the /msgs component subdirectory.
//   Use links or logicals to point it there, thanks!   AMG

DATABASE DB = FILENAME "msg.fdb";

const char* CODES_PAS	= "gds_codes.pas";
const char* CODES_FTN	= "gds_codes.ftn";
//const char* CODES_H	= "codes.h"
const char* IBERROR_H	= "iberror.h";
const char* CODETEXT_H	= "codetext.h";
const char* MSGS_H		= "msgs.h";
const char* SQL_CODES_H	= "sql_code.h";
const char* MSGFAC_H	= "msg_facs.h";
const char* SQL_STATES_H	= "sql_state.h";


enum formatStyle
{
	CSTYLE 		=	1,
	PASSTYLE	=	2,
	FTNSTYLE	=	3,
	CPPSTYLE	=	4
};


//static void build_codes_h();
static void build_iberror_h();
static void build_other_headers();
static void build_msgfac_h();
static void move_if_not_identical(const char *, const char *, const char*);
static void add_text(FILE*, formatStyle, const char**);

static const char* genDirectory = NULL;
static const char* genFtnPasDirectory = NULL;

static void check_open(FILE*& f, const char* name, const char* tmp, const char* mode)
{
	f = fopen(tmp, mode);
	if (!f)
	{
		fprintf(stderr, "CODES: Unable to open %s in filemode %s for %s\n",
			tmp, mode, name);
		exit(FINI_ERROR);
   }
}

static void check_close(FILE* f, const char* name, const char* tmp, const char* directory)
{
	fclose(f);
	move_if_not_identical(name, tmp, directory);
}

const int FAC_SQL_POSITIVE	= 14;	// facility for positive SQL codes
const int FAC_SQL_NEGATIVE	= 13;	// ditto for negative codes

const int gds_facility_local	= 20;
const int gds_base_local		= gds_facility_local << 24;
const int gds_factor_local		= 1;


static const char *license[] = {
	"The contents of this file are subject to the Interbase Public",
	"License Version 1.0 (the \"License\"); you may not use this file",
	"except in compliance with the License. You may obtain a copy",
	"of the License at http://www.Inprise.com/IPL.html",
	"",
	"Software distributed under the License is distributed on an",
	"\"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express",
	"or implied. See the License for the specific language governing",
	"rights and limitations under the License.",
	"",
	"The content of this file was generated by the Firebird project",
	"using the program jrd/codes.epp",
	0
};

static const char *warn[] = {
	"",
	"*** WARNING *** - This file is automatically generated by codes.epp - do not edit!",
	"",
	0
};

static const SCHAR *gds_c_boiler_plate[] = {
	"#define gds_arg_end		0	/* end of argument list */",
	"#define gds_arg_gds		1	/* generic DSRI status value */",
	"#define gds_arg_string		2	/* string argument */",
	"#define gds_arg_cstring	3	/* count & string argument */",
	"#define gds_arg_number		4	/* numeric argument (long) */",
	"#define gds_arg_interpreted	5	/* interpreted status code (string) */",
	"#define gds_arg_vms		6	/* VAX/VMS status code (long) */",
	"#define gds_arg_unix		7	/* UNIX error code */",
	"#define gds_arg_domain		8	/* Apollo/Domain error code */",
	"#define gds_arg_dos		9	/* MSDOS/OS2 error code */",
	"#define gds_arg_mpexl		10	/* HP MPE/XL error code */",
	"#define gds_arg_mpexl_ipc	11	/* HP MPE/XL IPC error code */",

//      #'s 12-14 WERE USED BY TWIN SUN AND THUS MUST BE SKIPPED BY US!
	"#define gds_arg_next_mach	15	/* NeXT/Mach error code */",
	"#define gds_arg_netware	16	/* NetWare error code */",
	"#define gds_arg_win32		17	/* Win32 error code */",
	"#define gds_arg_warning	18	/* warning argument */",
	"",
};

/* It's used only in code that's commented now: build_codes_h().
static const SCHAR *gds_cpp_boiler_plate[] = {
	"const SLONG gds_arg_end			= 0;	// end of argument list",
	"const SLONG gds_arg_gds			= 1;	// generic DSRI status value",
	"const SLONG gds_arg_string			= 2;	// string argument",
	"const SLONG gds_arg_cstring		= 3;	// count & string argument",
	"const SLONG gds_arg_number			= 4;	// numeric argument (long)",
	"const SLONG gds_arg_interpreted	= 5;	// interpreted status code (string)",
	"const SLONG gds_arg_vms			= 6;	// VAX/VMS status code (long)",
	"const SLONG gds_arg_unix			= 7;	// UNIX error code",
	"const SLONG gds_arg_domain			= 8;	// Apollo/Domain error code",
	"const SLONG gds_arg_dos			= 9;	// MSDOS/OS2 error code",
	"const SLONG gds_arg_mpexl			= 10;	// HP MPE/XL error code",
	"const SLONG gds_arg_mpexl_ipc 		= 11;	// HP MPE/XL IPC error code",

//      #'s 12-14 WERE USED BY TWIN SUN AND THUS MUST BE SKIPPED BY US!
	"const SLONG gds_arg_next_mach		= 15;	// NeXT/Mach error code",
	"const SLONG gds_arg_netware		= 16;	// NetWare error code",
	"const SLONG gds_arg_win32			= 17;	// Win32 error code",
	"const SLONG gds_arg_warning		= 18;	// warning argument",
	"",
};
*/

// At first glance, it wasn't evident why isc_facility had to be undefined,
// but it turns that this file includes ibase.h,
// but ibase.h includes iberror.h that's generated by this program!
// The solution is that iberror.h adopts a C++ section like codes.h does.
// Appending "_local" here saves us from any clash.
const int isc_facility_local	= 20;
const SLONG isc_base_local		= (SLONG) isc_facility_local << 24;
const SLONG isc_factor_local	= 1;


static const SCHAR *isc_c_boiler_plate[] = {
	"#define isc_arg_end		0	/* end of argument list */",
	"#define isc_arg_gds		1	/* generic DSRI status value */",
	"#define isc_arg_string		2	/* string argument */",
	"#define isc_arg_cstring	3	/* count & string argument */",
	"#define isc_arg_number		4	/* numeric argument (long) */",
	"#define isc_arg_interpreted	5	/* interpreted status code (string) */",
	"#define isc_arg_vms		6	/* VAX/VMS status code (long) */",
	"#define isc_arg_unix		7	/* UNIX error code */",
	"#define isc_arg_domain		8	/* Apollo/Domain error code */",
	"#define isc_arg_dos		9	/* MSDOS/OS2 error code */",
	"#define isc_arg_mpexl		10	/* HP MPE/XL error code */",
	"#define isc_arg_mpexl_ipc	11	/* HP MPE/XL IPC error code */",

//      #'s 12-14 WERE USED BY TWIN SUN AND THUS MUST BE SKIPPED BY US!

	"#define isc_arg_next_mach	15	/* NeXT/Mach error code */",
	"#define isc_arg_netware	16	/* NetWare error code */",
	"#define isc_arg_win32		17	/* Win32 error code */",
	"#define isc_arg_warning	18	/* warning argument */",
	"#define isc_arg_sql_state	19	/* SQLSTATE */",
	"",
};

static const SCHAR *isc_cpp_boiler_plate[] = {
	"const ISC_STATUS isc_arg_end			= 0;	// end of argument list",
	"const ISC_STATUS isc_arg_gds			= 1;	// generic DSRI status value",
	"const ISC_STATUS isc_arg_string		= 2;	// string argument",
	"const ISC_STATUS isc_arg_cstring		= 3;	// count & string argument",
	"const ISC_STATUS isc_arg_number		= 4;	// numeric argument (long)",
	"const ISC_STATUS isc_arg_interpreted	= 5;	// interpreted status code (string)",
	"const ISC_STATUS isc_arg_vms			= 6;	// VAX/VMS status code (long)",
	"const ISC_STATUS isc_arg_unix			= 7;	// UNIX error code",
	"const ISC_STATUS isc_arg_domain		= 8;	// Apollo/Domain error code",
	"const ISC_STATUS isc_arg_dos			= 9;	// MSDOS/OS2 error code",
	"const ISC_STATUS isc_arg_mpexl			= 10;	// HP MPE/XL error code",
	"const ISC_STATUS isc_arg_mpexl_ipc		= 11;	// HP MPE/XL IPC error code",

//      #'s 12-14 WERE USED BY TWIN SUN AND THUS MUST BE SKIPPED BY US!

	"const ISC_STATUS isc_arg_next_mach		= 15;	// NeXT/Mach error code",
	"const ISC_STATUS isc_arg_netware		= 16;	// NetWare error code",
	"const ISC_STATUS isc_arg_win32			= 17;	// Win32 error code",
	"const ISC_STATUS isc_arg_warning		= 18;	// warning argument",
	"const ISC_STATUS isc_arg_sql_state		= 19;	// SQLSTATE",
	"",
};


static const SCHAR *pas_boiler_plate[] = {
	"\tgds_arg_end\t\t= 0;\t(* end of argument list *)",
	"\tgds_arg_gds\t\t= 1;\t(* generic DSRI status value *)",
	"\tgds_arg_string\t\t= 2;\t(* string argument *)",
	"\tgds_arg_cstring\t\t= 3;\t(* count & string argument *)",
	"\tgds_arg_number\t\t= 4;\t(* numeric argument (long) *)",
	"\tgds_arg_interpreted\t= 5;\t(* interpreted status code (string) *)",
	"\tgds_arg_vms\t\t= 6;\t(* VAX/VMS status code (long) *)",
	"\tgds_arg_unix\t\t= 7;\t(* UNIX error code *)",
	"\tgds_arg_domain\t\t= 8;\t(* Apollo/Domain error code *)",
	"\tgds_arg_dos\t\t= 9;\t(* MSDOS/OS2 error code *)",
	"",
};

static const SCHAR *ftn_boiler_plate_data[] = {
	"",
	"      PARAMETER (",
	"     + GDS_ARG_END          = 0,   { end of argument list }",
	"     + GDS_ARG_GDS          = 1,   { generic DSRI status value }",
	"     + GDS_ARG_STRING       = 2,   { string argument }",
	"     + GDS_ARG_CSTRING      = 3,   { count & string argument }",
	"     + GDS_ARG_NUMBER       = 4,   { numeric argument (long) }",
	"     + GDS_ARG_INTERPRETED  = 5,   { interpreted status code (string) }",
	"     + GDS_ARG_VMS          = 6,   { VAX/VMS status code (long) }",
	"     + GDS_ARG_UNIX         = 7,   { UNIX error code }",
	"     + GDS_ARG_DOMAIN       = 8,   { Apollo/Domain error code }",
	"     + GDS_ARG_DOS	         = 9)   { Apollo/Domain error code }",
	"",
};

static const SCHAR *ftn_boiler_plate_decls[] = {
	"      INTEGER*4",
	"     + GDS_ARG_END,          { end of argument list }",
	"     + GDS_ARG_GDS,          { generic DSRI status value }",
	"     + GDS_ARG_STRING,       { string argument }",
	"     + GDS_ARG_CSTRING,      { count & string argument }",
	"     + GDS_ARG_NUMBER,       { numeric argument (long) }",
	"     + GDS_ARG_INTERPRETED,  { interpreted status code (string) }",
	"     + GDS_ARG_VMS,          { VAX/VMS status code (long) }",
	"     + GDS_ARG_UNIX,         { UNIX error code }",
	"     + GDS_ARG_DOMAIN,       { Apollo/Domain error code }",
	"     + GDS_ARG_DOS           { MSDOS/OS2 error code }",
	"",
};


int CLIB_ROUTINE main( int argc, char *argv[])
{
/*************************************
*
*	m a i n
*
**************************************
*
* Functional description
*	Generate both code definition
*	include file and message text
*	include file.
*
**************************************/

	READY
	ON_ERROR
		printf("Attach failed.\n");
		isc_print_status(gds_status);
		exit(FINI_ERROR);
	END_ERROR;

	// See if the user has specified an optional output directory
	if (argc >= 1 ) {
		genDirectory = argv[1];
	}
	if (argc >= 2 ) {
		genFtnPasDirectory = argv[2];
	}

	START_TRANSACTION;

	build_iberror_h();
	// build_codes_h();
	build_msgfac_h();
	build_other_headers();

	COMMIT;
	FINISH;

	// exit(0);
	return 0;
}


static void build_msgfac_h()
{
/*******************************************
*
*	b u i l d _ m s g f a c _ h
*
*******************************************
*
* Functional desciption
*	Function creates the msgfac.h
*	file which contains a listing of valid
*	facilities for the error codes.
*
*******************************************/
	FILE *msgfac = NULL;

	// Open msgfac.h in the current directory
	check_open(msgfac, MSGFAC_H, "tmpcode1", "w");

	add_text(msgfac, CSTYLE, license);
	add_text(msgfac, CSTYLE, warn);
	fprintf(msgfac, "/*\n");
	fprintf(msgfac, " *	MODULE:		msg_facs.h\n");
	fprintf(msgfac, " *	DESCRIPTION:	ISC message facilities\n");
	fprintf(msgfac, " *\n");
	fprintf(msgfac, " */\n");
	fprintf(msgfac, "\n\n\n");
	fprintf(msgfac, "/******************************/\n");
	fprintf(msgfac, "/*   ISC message facilities   */\n");
	fprintf(msgfac, "/******************************/\n");
	fprintf(msgfac, "\n");

	fprintf(msgfac, "struct _facilities\n");
	fprintf(msgfac, "{\n");
	fprintf(msgfac, "\tint fac_code;\n");
	fprintf(msgfac, "\tconst char* facility;\n");
	fprintf(msgfac, "};\n\n");

	fprintf(msgfac, "static const struct _facilities facilities[] =\n");
	fprintf(msgfac, "{\n");
	FOR F IN FACILITIES WITH ANY FAC_CODE IN SYSTEM_ERRORS OVER FAC_CODE
		fprintf(msgfac, "\t{%d,  \"%-10.10s\"},\n", F.FAC_CODE, F.FACILITY);
	END_FOR;

	fprintf(msgfac, "\t{0, NULL}\n};\n\n");
	fprintf(msgfac, "\n");
	check_close(msgfac, MSGFAC_H, "tmpcode1", genDirectory);
}

static void build_iberror_h()
{
/*******************************************
*
*	b u i l d _ i b e r r o r _ h
*
*******************************************
*
* Functional desciption
*	Function creates the iberror.h
*	file which contains c and isc_
*	style error codes.
*
*******************************************/
	FILE *iberror = NULL;
	int i, code = 1, last_code = code - 1;
	unsigned long new_code = 0L;

	// Open iberror.h in the current directory.
	check_open(iberror, IBERROR_H, "tmpcode2", "w");

	fprintf(iberror, "\n#ifndef JRD_GEN_IBERROR_H\n");
	fprintf(iberror, "#define JRD_GEN_IBERROR_H\n");
	add_text(iberror, CSTYLE, license);
	add_text(iberror, CSTYLE, warn);
	fprintf(iberror, "/*\n");
	fprintf(iberror, " *	MODULE:		iberror.h\n");
	fprintf(iberror, " *	DESCRIPTION:	ISC error codes\n");
	fprintf(iberror, " *\n");
	fprintf(iberror, " */\n");
	fprintf(iberror, "\n\n\n");
	fprintf(iberror, "/***********************/\n");
	fprintf(iberror, "/*   ISC Error Codes   */\n");
	fprintf(iberror, "/***********************/\n");
	fprintf(iberror, "\n");

	fprintf(iberror, "\n#ifdef __cplusplus /* c++ definitions */\n\n");

	fprintf(iberror,
			   "const ISC_STATUS isc_facility	= %d;\n", isc_facility_local);
	fprintf(iberror,
			   "const ISC_STATUS isc_base = %"SLONGFORMAT"L;\n", isc_base_local);
	fprintf(iberror,
			   "const ISC_STATUS isc_factor = %"SLONGFORMAT";\n", isc_factor_local);

// Append the ISC C++ boiler plate

	int numLines = FB_NELEM(isc_cpp_boiler_plate);
	for (i = 0; i < numLines; i++) {
		fprintf(iberror, "%s\n", isc_cpp_boiler_plate[i]);
	}

// Fetch error codes from SYSTEM_ERRORS relation.
//   Write them out using the ISC prefix.

	FOR S IN SYSTEM_ERRORS CROSS N IN MESSAGES OVER NUMBER WITH
		N.FAC_CODE = S.FAC_CODE SORTED BY N.FAC_CODE, S.NUMBER
		// The only facility that is guaranteed to have all codes in
		// system errors is JRD
		if (last_code + 1 != N.CODE && N.FAC_CODE == 0)
		{
			fprintf(stderr,
				"Warning: missing codes between %d and %d (exclusive)\n",
				last_code, (int) N.CODE);
		}
		last_code = N.CODE;
		new_code = ENCODE_ISC_MSG(S.NUMBER, N.FAC_CODE);
		fprintf(iberror, "const ISC_STATUS isc_%-32.32s = %luL;\n", S.GDS_SYMBOL, new_code);
		++code;
	END_FOR;

	--code;
	fprintf(iberror, "const ISC_STATUS isc_%-32.32s = %d;\n", "err_max", code);
	fprintf(iberror, "\n");


	fprintf(iberror, "#else /* c definitions */\n\n");


	// Reset counters for a new iteration.
	code = 1;
	last_code = code - 1;
	new_code = 0L;

	fprintf(iberror, "#define isc_facility %d\n", isc_facility_local);
	fprintf(iberror, "#define isc_base %"SLONGFORMAT"L\n", isc_base_local);
	fprintf(iberror, "#define isc_factor %"SLONGFORMAT"\n", isc_factor_local);
	fprintf(iberror, "\n");

// Append the ISC C boiler plate

	numLines = FB_NELEM(isc_c_boiler_plate);
	for (i = 0; i < numLines; i++) {
		fprintf(iberror, "%s\n", isc_c_boiler_plate[i]);
	}

// Fetch error codes from SYSTEM_ERRORS relation.
//   Write them out using the ISC prefix.

	FOR S IN SYSTEM_ERRORS CROSS N IN MESSAGES OVER NUMBER WITH
		N.FAC_CODE = S.FAC_CODE SORTED BY N.FAC_CODE, S.NUMBER
		// The only facility that is guaranteed to have all codes in
		// system errors is JRD
		if (last_code + 1 != N.CODE && N.FAC_CODE == 0)
		{
			fprintf(stderr,
				"Warning: missing codes between %d and %d (exclusive)\n",
				last_code, (int) N.CODE);
		}
		last_code = N.CODE;
		new_code = ENCODE_ISC_MSG(S.NUMBER, N.FAC_CODE);
		fprintf(iberror, "#define isc_%-32.32s %luL\n", S.GDS_SYMBOL, new_code);
		++code;
	END_FOR;

	--code;
	fprintf(iberror, "#define isc_%-32.32s %d\n", "err_max", code);
	fprintf(iberror, "\n");
	fprintf(iberror, "#endif\n\n");
	fprintf(iberror, "#endif /* JRD_GEN_IBERROR_H */\n");
	check_close(iberror, IBERROR_H, "tmpcode2", genDirectory);
}


//static void build_codes_h()
//{
///*******************************************
//*
//*	b u i l d _ c o d e s _ h
//*
//*******************************************
//*
//* Functional desciption
//*	Function creates the codes.h
//*	file which contains c and isc_
//*	style error codes.
//*
//*******************************************/
//	FILE *c_codes = NULL;
//	int i, code = 1, last_code = code - 1;
//	unsigned long new_code = 0L;
//
// Open codes.h in the current directory.
//
//	check_open(c_codes, CODES_H, "tmpcode3", "w");
//	add_text(c_codes, CSTYLE, license);
//	add_text(c_codes, CSTYLE, warn);
//
//	fprintf(c_codes, "\n#ifndef JRD_GEN_CODES_H\n");
//	fprintf(c_codes, "#define JRD_GEN_CODES_H\n");
//
//	fprintf(c_codes, "\n#ifdef __cplusplus /* c++ definitions */\n\n");
//
//	fprintf(c_codes, "const SLONG gds_facility	= %d;\n", gds_facility_local);
//	fprintf(c_codes, "const SLONG gds_err_base = %ldL;\n", gds_base_local);
//	fprintf(c_codes, "const SLONG gds_err_factor = %ld;\n", gds_factor_local);
//
//	// Append the C++ boiler plate
//	int numLines = FB_NELEM(gds_cpp_boiler_plate);
//	for (i = 0; i < numLines; i++) {
//		fprintf(c_codes, "%s\n", gds_cpp_boiler_plate[i]);
//	}
//
//
//	FOR S IN SYSTEM_ERRORS CROSS N IN MESSAGES OVER NUMBER WITH
//		N.FAC_CODE = S.FAC_CODE SORTED BY N.FAC_CODE, S.NUMBER
//		// The only facility that is guaranteed to have all codes in
//		// system errors is JRD
//		if (last_code + 1 != N.CODE && N.FAC_CODE == 0)
//		{
//			fprintf(stderr,
//					   "Warning: missing codes between %d and %d (exclusive)\n",
//					   last_code, (int) N.CODE);
//		}
//		last_code = N.CODE;
//		new_code = ENCODE_ISC_MSG(S.NUMBER, N.FAC_CODE);
//		fprintf(c_codes, "const SLONG gds_%-32.32s = %luL;\n", S.GDS_SYMBOL, new_code);
//		++code;
//
//		/* make sure that the SQL code is also present in the SQLERR facility */
//
//	/**** this part really should be done via a trigger on the database--
//		  commented out for now - deej
//	got_it = 0;
//	if (S.SQL_CODE < 0)
//	{
//		srchnum = 1000 + S.SQL_CODE;
//		**FOR M IN MESSAGES WITH M.FAC_CODE = FAC_SQL_NEGATIVE
//		AND M.NUMBER = srchnum
//			got_it = 1;
//		**END_FOR;
//	}
//	else
//	{
//		srchnum = S.SQL_CODE;
//		**FOR M IN MESSAGES WITH M.FAC_CODE = FAC_SQL_POSITIVE
//		AND M.NUMBER = srchnum
//			got_it = 1;
//		**END_FOR;
//	}
//	if (!got_it)
//	{
//		printf ("codes: msg %d: sql-code %d, is not in the SQLERR facility\n",
//					N.NUMBER, S.SQL_CODE);
//		ROLLBACK;
//		FINISH;
//		exit (FINI_ERROR);
//	} ****/
//
//	END_FOR;
//
//	--code;
//	fprintf(c_codes, "const SLONG gds_%-32.32s = %dL;\n", "err_max", code);
//	fprintf(c_codes, "\n");
//
//
//	fprintf(c_codes, "#else /* c definitions */\n\n");
//
//
//	fprintf(c_codes, "#define gds_facility	%d\n", gds_facility_local);
//	fprintf(c_codes, "#define gds_err_base	%ldL\n", gds_base_local);
//	fprintf(c_codes, "#define gds_err_factor	%ld\n", gds_factor_local);
//
//	// Reset the variables for another trip round the loop.
//	code = 1;
//	last_code = code - 1;
//	new_code = 0L;
//
//	// Append the C boiler plate
//	numLines = FB_NELEM(gds_c_boiler_plate);
//	for (i = 0; i < numLines; i++) {
//		fprintf(c_codes, "%s\n", gds_c_boiler_plate[i]);
//	}
//
//
//	FOR S IN SYSTEM_ERRORS CROSS N IN MESSAGES OVER NUMBER WITH
//		N.FAC_CODE = S.FAC_CODE SORTED BY N.FAC_CODE, S.NUMBER
//		// The only facility that is guaranteed to have all codes in
//		// system errors is JRD
//		if (last_code + 1 != N.CODE && N.FAC_CODE == 0)
//		{
//			fprintf(stderr,
//					   "Warning: missing codes between %d and %d (exclusive)\n",
//					   last_code, (int) N.CODE);
//		}
//		last_code = N.CODE;
//		new_code = ENCODE_ISC_MSG(S.NUMBER, N.FAC_CODE);
//		fprintf(c_codes, "#define gds__%-32.32s %luL\n", S.GDS_SYMBOL, new_code);
//		++code;
//
//		/* make sure that the SQL code is also present in the SQLERR facility */
//
//	/**** this part really should be done via a trigger on the database--
//		  commented out for now - deej
//	got_it = 0;
//	if (S.SQL_CODE < 0)
//	{
//		srchnum = 1000 + S.SQL_CODE;
//		**FOR M IN MESSAGES WITH M.FAC_CODE = FAC_SQL_NEGATIVE
//			AND M.NUMBER = srchnum
//			got_it = 1;
//		**END_FOR;
//	}
//	else
//	{
//		srchnum = S.SQL_CODE;
//		**FOR M IN MESSAGES WITH M.FAC_CODE = FAC_SQL_POSITIVE
//			AND M.NUMBER = srchnum
//			got_it = 1;
//		**END_FOR;
//	}
//	if (!got_it)
//	{
//		printf ("codes: msg %d: sql-code %d, is not in the SQLERR facility\n",
//					 N.NUMBER, S.SQL_CODE);
//		ROLLBACK;
//		FINISH;
//		exit (FINI_ERROR);
//	} ****/
//
//	END_FOR;
//
//	--code;
//	fprintf(c_codes, "#define gds_%-32.32s %d\n", "err_max", code);
//	fprintf(c_codes, "\n");
//
//
//	fprintf(c_codes, "#endif\n\n");
//
//
//
// Append the ISC error codes
//
///*  I have removed the isc errors from codes.h since they are all now placed
//	into iberror.h - If it needs to be replaced then it can be copied back
//	from gen iberror routine.  I now do the include of that file.
//	MOD 17-07-2001
//*/
//	fprintf(c_codes, "#include \"iberror.h\"\n\n");
//
//
//	fprintf(c_codes, "#endif /* JRD_GEN_CODES_H */\n");
//
//	check_close(c_codes, CODES_H, "tmpcode3", genDirectory);
//}


static void build_other_headers()
{
/********************************************
*
*	b u i l d _ o t h e r _ h e a d e r s
*
*********************************************
*
* Functional desciption
*	Function creates the other header
*	files.
*
*********************************************/

	// TEXT is varchar(118) but we need to double quotes and add \ after hardcoded newline for msgs.h
	SCHAR text[512];
	int i;
	unsigned long new_code = 0L;

	// Use the check_open function to open header files in current directory.

	FILE* pas_codes = NULL;
	check_open(pas_codes, CODES_PAS, "tmpcode4", "w");
	FILE* ftn_codes = NULL;
	check_open(ftn_codes, CODES_FTN, "tmpcode5", "w");
	FILE* code_text = NULL;
	check_open(code_text, CODETEXT_H, "tmpcode6", "w");
	FILE* msgs = NULL;
	check_open(msgs, MSGS_H, "tmpcode7", "w");
	FILE* sql_codes = NULL;
	check_open(sql_codes, SQL_CODES_H, "tmpcode9", "w");
	FILE* sql_states = NULL;
	check_open(sql_states, SQL_STATES_H, "tmpcode10", "w");

	// Add license

	add_text(pas_codes, PASSTYLE, license);
	add_text(pas_codes, PASSTYLE, warn);
	add_text(ftn_codes, FTNSTYLE, license);
	add_text(ftn_codes, FTNSTYLE, warn);
	add_text(code_text, CSTYLE, license);
	add_text(code_text, CSTYLE, warn);
	add_text(msgs, CSTYLE, license);
	add_text(msgs, CSTYLE, warn);
	add_text(sql_codes, CSTYLE, license);
	add_text(sql_codes, CSTYLE, warn);
	add_text(sql_states, CSTYLE, license);
	add_text(sql_states, CSTYLE, warn);

	// Start the PASCAL codes file

	fprintf(pas_codes, "\n"
		"const\n"
		"\tgds_facility\t\t= %d;\n"
		"\tgds_err_base\t\t= %d;\n"
		"\tgds_err_factor\t\t= %d;\n",
		gds_facility_local, gds_base_local, gds_factor_local);

	int numLines = FB_NELEM(pas_boiler_plate);
	for (i = 0; i < numLines; i++) {
		fprintf(pas_codes, "%s\n", pas_boiler_plate[i]);
	}

	// Start the FORTRAN codes file

	fprintf(ftn_codes, "\n"
		"      INTEGER*4\n"
		"     + GDS_FACILITY,\n"
		"     + GDS_ERR_BASE,\n"
		"     + GDS_ERR_FACTOR\n");

	numLines = FB_NELEM(ftn_boiler_plate_decls);
	for (i = 0; i < numLines; i++) {
		fprintf(ftn_codes, "%s\n", ftn_boiler_plate_decls[i]);
	}

	fprintf(ftn_codes, "\n"
		"      PARAMETER (\n"
		"     + GDS_FACILITY    = %d,\n"
		"     + GDS_ERR_BASE    = %d,\n"
		"     + GDS_ERR_FACTOR  = %d)\n",
		gds_facility_local, gds_base_local, gds_factor_local);

	numLines = FB_NELEM(ftn_boiler_plate_data);
	for (i = 0; i < numLines; i++) {
		fprintf(ftn_codes, "%s\n", ftn_boiler_plate_data[i]);
	}

	// Start the codetext.h

	fprintf(code_text, "static const struct {\n");
	fprintf(code_text, "\tconst SCHAR *code_string;\n");
	fprintf(code_text, "\tSLONG code_number;\n");
	fprintf(code_text, "} codes[] =\n{\n");

	// Start the MSGS file

	fprintf(msgs, "static const struct {\n");
	fprintf(msgs, "\tSLONG code_number;\n");
	fprintf(msgs, "\tconst SCHAR *code_text;\n");
	fprintf(msgs, "} messages[] = {\n");

	// Start the sqlcodes

	fprintf(sql_codes, "static const struct {\n");
	fprintf(sql_codes, "\tSLONG gds_code;\n");
	fprintf(sql_codes, "\tSSHORT sql_code;\n");
	fprintf(sql_codes, "} gds__sql_code[] = {\n");

	// Start the sqlstates

	fprintf(sql_states, "static const struct {\n");
	fprintf(sql_states, "\tSLONG gds_code;\n");
	fprintf(sql_states, "\tconst char* sql_state;\n");
	fprintf(sql_states, "} gds__sql_states[] = {\n");

	// Fetch error codes from SYSTEM_ERRORS relation.  Write them
	// out to the pascal, fortran, and rdb header files

	int code = 1;
	int last_code = code - 1;

	FOR S IN SYSTEM_ERRORS CROSS N IN MESSAGES OVER NUMBER WITH
		N.FAC_CODE = S.FAC_CODE SORTED BY N.FAC_CODE, S.NUMBER
		// The only facility that is guaranteed to have all codes in
		// system errors is JRD
		if (last_code + 1 != N.CODE && N.FAC_CODE == 0)
		{
			fprintf(stderr,
					   "Warning: missing codes between %d and %d (exclusive)\n",
					   last_code, (int) N.CODE);
		}
		last_code = N.CODE;
		new_code = ENCODE_ISC_MSG(N.NUMBER, N.FAC_CODE);

		fprintf(pas_codes, "\tgds_%-32.32s = %lu;\n", S.GDS_SYMBOL, new_code);
		fprintf(ftn_codes,
				   "      INTEGER*4 GDS__%-32.32s\n      PARAMETER (GDS__%-32.32s = %lu)\n",
				   S.GDS_SYMBOL, S.GDS_SYMBOL, new_code);

		SCHAR* q = text;
		const SCHAR* const end = q + sizeof(text) - 1;
		SCHAR* last = q;
		for (const SCHAR* p = N.TEXT; *p && p < N.TEXT + sizeof(N.TEXT); p++)
		{
			switch (*p)
			{
			case '"': // Double the quotes
			case '\n': // We need a backslash to continue string literals in another line in C++
				if (q + 2 <= end)
				{
					*q++ = '\\';
					*q++ = *p;
				}
				break;
			default:
				if (q + 1 <= end)
					*q++ = *p;
			}

			if (*p != ' ')
				last = q;
		}
		*last = 0;

		q = S.GDS_SYMBOL;
		while (*q && *q != ' ' && q < S.GDS_SYMBOL + sizeof(S.GDS_SYMBOL) - 1)
		      q++;
		*q = 0;

		fprintf(code_text, "\t{\"%s\", %lu},\n", S.GDS_SYMBOL, new_code);
		fprintf(msgs, "\t{%lu, \"%s\"},\t\t/* %d, %s */\n", new_code, text, code, S.GDS_SYMBOL);
		++code;

	END_FOR;

	const int DEFAULT_SQL_CODE = -999;	// Firebird error

	i = 0;
	FOR E IN SYSTEM_ERRORS SORTED BY E.FAC_CODE, E.NUMBER
		// The only facility that is guaranteed to have all codes
		// in system errors is JRD

		if (E.NUMBER != (SSHORT) i + 1 && E.FAC_CODE == 0)
		{
			fprintf(stderr, "Warning: Sequence of error numbers is bad %d!\n", E.NUMBER);
		}
		i = E.NUMBER;

		new_code = ENCODE_ISC_MSG(E.NUMBER, E.FAC_CODE);
		if (E.SQL_CODE.NULL)
		{
			fprintf(sql_codes, "\t{%lu, %4d}, /* %3d %s (default) */\n",
					new_code, DEFAULT_SQL_CODE, E.NUMBER, E.GDS_SYMBOL);
		}
		else {
			fprintf(sql_codes, "\t{%lu, %4d}, /* %3d %s */\n",
					new_code, (int) E.SQL_CODE, E.NUMBER, E.GDS_SYMBOL);
		}

		fprintf(sql_states, "\t{%lu, \"%s\"}, // %3d %s\n",
				new_code, E.SQL_STATE, E.NUMBER, E.GDS_SYMBOL);

	END_FOR;

	// end the codetext.h
	fprintf(code_text, "\t{NULL, 0}\n");
	fprintf(code_text, "};\n");
	// end the msgs.h
	fprintf(msgs, "\t{0, NULL}\n");
	fprintf(msgs, "};\n");
	// end the sqlcodes.h
	fprintf(sql_codes, "\t{0, 0}\n");
	fprintf(sql_codes, "};\n");
	// end the sqlstates.h
	fprintf(sql_states, "\t{0, NULL}\n");
	fprintf(sql_states, "};\n");

	check_close(pas_codes, CODES_PAS, "tmpcode4", genFtnPasDirectory);
	check_close(ftn_codes, CODES_FTN, "tmpcode5", genFtnPasDirectory);
	check_close(code_text, CODETEXT_H, "tmpcode6", genDirectory);
	check_close(msgs, MSGS_H, "tmpcode7", genDirectory);
	check_close(sql_codes, SQL_CODES_H, "tmpcode9", genDirectory);
	check_close(sql_states, SQL_STATES_H, "tmpcode10", genDirectory);
}


static void move_if_not_identical( const char* original, const char* new_file,
   	const char *directory)
{
	char origName[1000];
	char buffer[1000];
#if defined(WIN_NT) && !defined(__GNUC__)
	if ( directory != NULL) {
		sprintf(origName, "%s\\%s", directory, original);
	}
	else {
		sprintf(origName, ".\\%s", original);
	}

	// This is a trick to make on systems previous to W2K were apparently
	// fc does not return a errorlevel, but find return it (1 found 0 not found)
	// FC: appear only where there are no differences (and the switch /B is not used)

	sprintf(buffer, "fc %s %s | find \"FC:\" > nul", new_file, origName);
#else
	if ( directory != NULL) {
		sprintf(origName, "%s/%s", directory, original);
	}
	else {
		sprintf(origName, "./%s", original);
	}
	sprintf(buffer, "cmp -s %s %s", new_file, origName);
#endif

	// If the new file is identical to the original, then don't update the original
	if (system(buffer) == 0) {
		fprintf(stdout, "No need to update %s\n", origName);
		unlink(new_file);
	}
	else {
		// Original file is missing or different
#if defined(WIN_NT) && !defined(__GNUC__)
		sprintf(buffer, "move %s %s > nul", new_file, origName);
#else
		sprintf(buffer, "mv -f %s %s", new_file, origName);
#endif
		if (system(buffer) != 0) {
			fprintf(stderr, "Error moving %s to %s!\n", new_file, origName);
			exit(FINI_ERROR);
		}
		else
			fprintf(stdout, "Updated %s\n", origName);
	}
}


static void add_text(FILE* fd, formatStyle style, const char** text)
{
/*******************************************
*
*       a d d _ t e x t
*
*******************************************
*
* Functional desciption
*       Function adds text as comment
*	in 'style' language
*
*******************************************/
	switch (style)
	{
	case CSTYLE:
		fprintf(fd, "/*\n");
		break;

	case PASSTYLE:
		fprintf(fd, "(*\n");
		break;

	case FTNSTYLE:
		fprintf(fd, "C --\n");
		break;

	case CPPSTYLE:
		fprintf(fd, "//\n");
		break;

	default:
		fprintf(stderr, "Error writing license\n");
		exit(FINI_ERROR);
		break;
	}

	for (const char** ptr = text; *ptr; ++ptr)
	{
		switch (style)
		{
		case CSTYLE:
		case PASSTYLE:
			fprintf(fd, " *");
			break;

		case FTNSTYLE:
			fprintf(fd, "C --");
			break;

		case CPPSTYLE:
			fprintf(fd, "//");
			break;
		}

		const char* space = **ptr ? " " : "";
		fprintf(fd, "%s%s\n", space, *ptr);
	}

	switch (style)
	{
	case CSTYLE:
		fprintf(fd, " */\n");
		break;

	case PASSTYLE:
		fprintf(fd, " *)\n");
		break;

	case FTNSTYLE:
		fprintf(fd, "C --\n");
		break;

	case CPPSTYLE:
		fprintf(fd, "//\n");
		break;
	}
}

