/*  cmdline.cxx   */
/*  Copyright 1991 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stream.h>
#include <fstream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "portable.h"
#include "outtok.h"
#include "cmdline.h"
/* #include "error.h" */
#include "package.h"
#include "dirdes.h"
#include "slist.h"
#include "cgidbg.h"
#include "mkstr.h"
#include "texutil.h"

int GlobalMacrosInMake = 1;
int TiC30Option  = 0 ;

struct CmdOption ;
static const char * EndOfArgumentList = "\01\01\01\01" ;

class CmdLine{
	int CommandLineError ;
	int ArgC;
	const char ** ArgV;
	CmdOption ** TheOptions ;
	PackageDesc * NewPackage ;
public:
	int cfiles;
	int hfiles;
	int usrfiles;
	int NoCollect;
	int MenuFiles;
	const char * MenuFlag;
	const char * MenuDir;
	const char * Objective ;
	CmdLine(const int argc,const char ** argv) ;

	int Count() { return ArgC; }
	int IsError() {return CommandLineError;}

	const char * String(int i) ;

	void Error(const char * msg, int n) ;
	void NewError() {CommandLineError = 1 ;}
	void AnyError();
	const char * GetOneArg(int& i);
	ConstStringList * GetTwoArgs(int& i);
	ConstStringList * GetListArg(int& i);
	int CheckForArguments(const char * Msg, int i) ;
	void SetCommandOptions(CmdOption ** opts){TheOptions = opts;}
	void DoCommandOptions();
	void CheckCommandOptions();
	void ExecuteCommandOptions();
	PackageDesc * GetPackage() {return NewPackage;}
} ;


CmdLine::CmdLine(const int argc,const char ** argv):
	Objective(0)
{
	ArgC=argc;
	ArgV=argv;
	CommandLineError=0;
	NewPackage = 0 ;

	cfiles = hfiles = usrfiles = NoCollect = MenuFiles = 0;
	MenuFlag = MenuDir = 0 ;
}

const char * CmdLine::String(int i)
{
	if (i>=ArgC) {
		fprintf(stderr,"i = %d, ArgC = %d\n",i,ArgC);
		// LogOut << "i,ArgC= " << i << ArgC <<"\n" ;
		InternalError("CmdLine::String");
	}
	// cout << "String(" << i << ") = `" << ArgV[i] << "'\n" ;
	return ArgV[i] ;
}


void CmdLine::Error(const char * msg,int n)
{
	cerr << "Command line error at item " << n << " (" << ArgV[n] <<
		"): " << msg << ".\n" ;
	CommandLineError = 1;
}

void CmdLine::AnyError()
{
	if (CommandLineError) {
		cerr <<
		"Execution aborted because of above command line error(s).\n";
		cerr << "Execute `makemake' without parameters to get usage.\n" ;
		exit(2);
	}
}

int CmdLine::CheckForArguments(const char * Msg, int i)
{
	if (i+2 > ArgC) Error(Msg,i);
	else if (ArgV[i+1][0] == '-') Error(Msg,i);
	else return 1 ;
	return 0 ;
}

const char * CmdLine::GetOneArg(int& i)
{
	if (!CheckForArguments( "missing single argument", i)) return 0 ;
	if (i+2 < ArgC) if (ArgV[i+2][0] != '-')
		Error("more than one argument",i);
	return ArgV[++i];
}

ConstStringList * CmdLine::GetTwoArgs(int& i)
{
	if (!CheckForArguments( "missing first argument from pair", i)) return 0 ;
	if (!CheckForArguments( "missing second argument from pair", i+1)) return 0 ;
	if (i+3 < ArgC) if (ArgV[i+3][0] != '-')
        Error("more then two argument",i);

	ConstStringList * Return = new ConstStringList;
	int arg_count = 0 ;
	i++ ;
	for (;i<ArgC;i++) if(ArgV[i][0] == '-') {i--;break;}
	else {
		Return->Append(ArgV[i]);
		arg_count++ ;
	}
	if (arg_count != 2) Error("exactly two arguments required",i);
	return Return ;
}

ConstStringList * CmdLine::GetListArg(int& i)
{
	if (!CheckForArguments( "missing argument list", i++)) return 0 ;
	ConstStringList * Return = new ConstStringList;
	for (;i<ArgC;i++) if(ArgV[i][0] == '-') {i--;break;}
	else Return->Append(ArgV[i]);
	return Return ;

}


	
enum Parameter { ParamNone, ParamOne, ParamTwo, ParamList, ParamInteger} ;

enum FlagOptions {MultipleOccurences=1} ;

struct CmdOption {
typedef void (CmdOption::*ParamAction)(CmdLine& Cmds);
typedef int (CmdOption::*SpecialCheckAction)(CmdLine& Cmds);
	const char * option;
	const char * Usage ;
	const char * Meaning ;
	Parameter HasParameter ;
	ParamAction TheAction ;
	const char ** default_value ;
	CmdOption ** Check ;
	SpecialCheckAction TheSpecialCheck ;
	uint32 Flags ;
	// last static initialized field
	ConstStringList * TheData ;
	int OptionSet ;

	int DoCommandOption(CmdLine& Commands, int& i);
	void AppendOneArg(const char * Arg);
	void AppendListArg(ConstStringList * Arg);
	void DoCheck(CmdLine& Cmds);
	void Execute(CmdLine& Cmds);
	void DoExclusiveCheck(CmdLine& TheCmd) ;
	void TeXOut(CmdLine& Cmds) ;

	void SetCdir(CmdLine& Cmds);
	void SetHdir(CmdLine& Cmds);
	void SetLibDir(CmdLine& Cmds);
	void SetMdir(CmdLine& Cmds);
	void SetUsrDir(CmdLine& Cmds);
	void SetMenuOptions(CmdLine& Cmds);
	void SetNoCollDir(CmdLine& Cmds);
	void SetSubdirectory(CmdLine& Cmds);
	void SetRootDirectory(CmdLine& Cmds);
	void SetIncludeAll(CmdLine& Cmds);
	void SetMakeMakeDepend(CmdLine& Cmds);
	void SetIncludeMain(CmdLine& Cmds);
	void SetUsrSubdirectory(CmdLine& Cmds) ;
	void SetNoCC(CmdLine& Cmds);

	void AddMakeList(CmdLine& Cmds);
	void AddLibDirList(CmdLine& Cmds);
	void AddInternalLibMake(CmdLine& Cmds);
	void AddExternalLibMake(CmdLine& Cmds);
	void SetSearchLibList(CmdLine& Cmds);
	void SetLibList(CmdLine& Cmds);
	void SetLibDirList(CmdLine& Cmds);
	void SetLibUpDirList(CmdLine& Cmds);

	void SetNoExecutable(CmdLine& Cmds);
	void SetLibraryRepeat(CmdLine& Cmds);
	void SetCommandFile(CmdLine& Cmds);
	void SetObjSuffix(CmdLine& Cmds);

	void list_files(CmdLine& Cmds);
	void user_copyright(CmdLine& Cmds);
	void Set_ranlib(CmdLine& Cmds);
	void Set_og(CmdLine& Cmds);
	void Set_lkf(CmdLine& Cmds);
	void Set_arith(CmdLine& Cmds);
	void CreateSourceDirList(CmdLine& Cmds);
	void Set_dbXtra(CmdLine& Cmds);
	void SetNoSourceNoWarn(CmdLine& Cmds);
	void Set_gf(CmdLine& Cmds);
	void Set_gfb(CmdLine& Cmds);
	void Set_gfs(CmdLine& Cmds);

	void CheckUsrC(CmdLine& Cmds);
	void SetColl(CmdLine& Cmds);
	void SetDirSpace(CmdLine& Cmds);
	void SetLibNm(CmdLine& Cmds);
	void SetMenuFlag(CmdLine& Cmds);
	void SetObjective(CmdLine& Cmds);

	int CheckCollectSet(CmdLine& Cmds);
	int CheckMenuSet(CmdLine& Cmds);
	void UsageOut(OutTokens& Out);
	void MeaningOut(OutTokens& Out);
	void OnlyOnce(CmdLine& cmd);

	void Set_tic30(CmdLine& Cmds);
};

void CmdOption::UsageOut(OutTokens& Out)
{
	Out.NextOut(option);
	if (Usage) Out.NextFillOut(Usage) ;
}

void CmdOption::MeaningOut(OutTokens& Out)
{
	Out.NextConcat("\t") ;
	UsageOut(Out);
	Out.NewLine();
	Out.NextConcat("Specifies" );
	Out.NextFillOut(Meaning);
	if (default_value) if (*default_value) {
		Out.NextFillOut("(the default is:");
		Out.NextFillOut(*default_value);
		Out.NextConcat(")");
	}
	Out.NextConcat(".");
	Out.NewLine();
}

void CmdOption::OnlyOnce(CmdLine& cmd)
{
	cerr << "Option `" << option << "' can only occur once.\n" ;
	cmd.NewError();
}

int CmdOption::DoCommandOption(CmdLine& Commands, int& i)
{
	if (strcmp(Commands.String(i),option)) return 0 ;
	if (OptionSet) if (!(Flags & MultipleOccurences)){
		OnlyOnce(Commands);
		return 1;
	}
	OptionSet = 1 ;
	switch (HasParameter) {
case ParamNone:
		break ;
case ParamOne:
case ParamInteger:
		AppendOneArg(Commands.GetOneArg(i));
		break ;
case ParamTwo:
		AppendListArg(Commands.GetTwoArgs(i));
		break ;
case ParamList:
		AppendListArg(Commands.GetListArg(i));
		break ;
	}
	return 1 ;
}

void CmdOption::AppendOneArg(const char * Arg)
{
	if (!TheData) TheData = new ConstStringList(Arg) ;
	else TheData->Append(Arg);
}

void CmdOption::AppendListArg(ConstStringList* Arg)
{
	if (Flags & MultipleOccurences) Arg->Append(EndOfArgumentList);
	if (!TheData) TheData = new ConstStringList(Arg);
	else TheData->Append(Arg);
}

void CmdOption::DoExclusiveCheck(CmdLine& TheCmds)
{
	if (!TheData) return ;
	for (CmdOption ** ExcCheck = Check ; *ExcCheck; ExcCheck++) {
		if (*ExcCheck == this) continue ;
		CmdOption& Exc = **ExcCheck ;
		if (!Exc.TheData) continue ;

		ConstStringListIterator Next(*TheData) ;
		ConstStringListIterator NextA(*(Exc.TheData));
		const char * StringCheck ;
		const char * CheckAgainst ;


		while (StringCheck = Next())
			while (CheckAgainst = NextA())
				if (!strcmp(StringCheck,CheckAgainst)) {
			cerr << "Parameter `" << StringCheck <<
				"' is specified under "
				<< "option `" << option << "'\nand option `" <<
				Exc.option << "'.\n" ;
			TheCmds.NewError();
		}
	}
}

void CmdOption::DoCheck(CmdLine& TheCmd)
{
	if (!OptionSet) return ;
/*
 *	LogOut << "Checking for `" << option << ", Error = " <<
 *		TheCmd.IsError() << "'\n" ;
 */
	if (Check) DoExclusiveCheck(TheCmd);
	if (TheSpecialCheck) if(!(this->*TheSpecialCheck)(TheCmd)) {
		// LogOut << "DoCheck error for `" << option << "'\n" ;
	}
}

static int DupCheck(const char * str, DirNames *DirN, const char * msg)
{
	const char * DirName ;
	for (int i = 0 ; DirName=DirN->GetDirName(i) ; i ++)
		if (!strcmp(DirName,str)) {
		cerr << "Warning: Duplicate " << msg << " at entry " << i
			<<".\n" ;
		cerr << "Second entry ignored.\n" ;
		return 1 ;
	}
	return 0 ;
}

static void PutListInPackage(DirNames *  AddTo, ConstStringList& TakeFrom,
	const char * Msg, int LibFlag=0)
{
	const char * ToAdd ;
	while(ToAdd = TakeFrom.Get()) {
		// LogOut << "Adding `" << ToAdd << "'\n" ;
		if (!DupCheck(ToAdd,AddTo,Msg)) {
			AddTo->AddName(ToAdd,LibFlag) ;
		}
		else AddTo->Decrement();
	}
}

void CmdOption::SetCdir(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) Cmds.cfiles += TheData->Size();
	else PutListInPackage(Package->CDirs,*TheData,"C file");
}

void CmdOption::SetHdir(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) Cmds.hfiles += TheData->Size();
	else PutListInPackage(Package->HDirs,*TheData,"H file");
}

void CmdOption::SetLibDir(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) Cmds.cfiles += TheData->Size();
	else PutListInPackage(Package->CDirs,*TheData,"C library file",1);
}

void CmdOption::SetMdir(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) Cmds.cfiles += TheData->Size();
	else {
		if (!TheData->Size()) return ;
		Package->CDirs->AddMainName(TheData->Get()) ;
		PutListInPackage(Package->CDirs,*TheData,"C file");
	}
}

void CmdOption::SetUsrDir(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) Cmds.usrfiles += TheData->Size();
	else PutListInPackage(Package->UsrDirs,*TheData,"User file",1);
}

void CmdOption::SetMenuOptions(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) Cmds.MenuFiles = 	TheData->Size();
	else {
		const char * MenuListElement ;
		while (MenuListElement = TheData->Get()) Package->
			AddMenuName(MenuListElement);
	}
}

void CmdOption::SetNoCollDir(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) Cmds.NoCollect = 	TheData->Size();
	else {
		const char * NoColName ;
		while (NoColName = TheData->Get()) Package->
			AddNoCollectName(NoColName);
	}
}

void CmdOption::AddMakeList(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) return ;
	const char ** Names = new const char * [TheData->Size()+1];
	int i=0 ;
	while(Names[i++]=TheData->Get()) ;
	const char ** Start = Names ;
	for (const char ** Check = Names ; *Check ; Check++)
		if(!strcmp(EndOfArgumentList,*Check)) {
			*Check = 0 ;
			if (*Start) Package->AddMakeDirList(Start);
			Start = Check + 1 ;
	}
}

void CmdOption::AddInternalLibMake(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
    if (!Package) return ;
	
	for  (;;) {
		const char * dir = TheData->Get() ;
		if (!dir) break ;
		const char * lib = TheData->Get() ;
		Package->add_move_lib(dir,lib);
		int error = 1 ;
		const char * test = TheData->Get() ;
		if (!strcmp(EndOfArgumentList,test)) error = 0 ;
		if (error) {
			cerr << "Internal problem with add_int_lib.\n" ;
			Cmds.NewError();
		}
	}
	// const char * dir = TheData->Get() ;
	// const char * lib = TheData->Get() ;
	// Package->add_move_lib(dir,lib);

}
void CmdOption::AddExternalLibMake(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
    if (!Package) return ;
	for  (;;) {
		const char * dir = TheData->Get() ;
		if (!dir) break ;
		const char * lib = TheData->Get() ;
		Package->add_make_ext_lib(dir,lib);
		int error = 1 ;
		const char * test = TheData->Get() ;
		if (!strcmp(EndOfArgumentList,test)) error = 0 ;
		if (error) {
			cerr << "Internal problem with add_ext_lib.\n" ;
			Cmds.NewError();
		}
	}

}

void CmdOption::AddLibDirList(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) return ;
	const char ** Names = new const char * [TheData->Size()+1];
	int i=0 ;
	// cerr << "Size = " << TheData->Size() << "\n" ;
	while(Names[i++]=TheData->Get()) ;
/*
 *		CHANGE ABOVE `;' to `{' TO ADD THIS DEBUGGING OUTPUT
 *		if (!strcmp(Names[i-1], EndOfArgumentList)) cerr << "EOL\n" ;
 *		else cerr << "Added " << Names[i-1] <<"\n" ;
 *	}
 */
	const char ** Start = Names ;
	for (const char ** Check = Names ; *Check ; Check++)
		if(!strcmp(EndOfArgumentList,*Check)) {
			*Check = 0 ;
			if (*Start) Package->AddLibDirList(Start);
			Start = Check + 1 ;
	}
}

void CmdOption::SetSearchLibList(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->LibList) {
			OnlyOnce(Cmds);
			return ;
		}
		const char ** Names = new const char * [TheData->Size()+1];
		int i=0 ;
		while(Names[i++]=TheData->Get());
		Package->SetLibSearchList(Names);
	}
}

void CmdOption::SetLibList(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->LibList) {
			OnlyOnce(Cmds);
			return ;
		}
		const char ** Names = new const char * [TheData->Size()+1];
		int i=0 ;
		while(Names[i++]=TheData->Get());
		Package->SetLibList(Names);
	}
}

void CmdOption::SetLibDirList(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->LibList) {
			OnlyOnce(Cmds);
			return ;
		}
		const char ** Names = new const char * [TheData->Size()+1];
		int i=0 ;
		while(Names[i++]=TheData->Get());
		Package->SetLibDirList(Names);
	}
}

void CmdOption::SetLibUpDirList(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->LibList) {
			OnlyOnce(Cmds);
			return ;
		}
		const char ** Names = new const char * [TheData->Size()+1];
		int i=0 ;
		while(Names[i++]=TheData->Get());
		Package->SetLibUpDirList(Names);
	}
}


void CmdOption::SetNoCC(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) {
		OmitCCLine = new const char * [TheData->Size()+1] ;
		const char * Entry ;
		const char ** AddPt = OmitCCLine ;
		*AddPt = 0 ;
		int i = 0 ;
		while(Entry = TheData->Get()) {
			int NoError = 1 ;
			for (const char **pt = OmitCCLine ;*pt; pt++)
				if(!strcmp(*pt,Entry)) {
				NoError = 0 ;
				cerr << "Warning duplicate entry for -nc (" <<
					Entry << ") at position " << i <<
					".\nSecond occurrence ignored.\n" ; 
				break ;
			}
			i++;
			if (NoError) *AddPt++ = Entry ;
		}
		*AddPt = 0 ;
	}
}

void CmdOption::SetUsrSubdirectory(CmdLine& Cmds)
{
	// LogOut << "SetUsrSubdirectory for `" << option << "'\n" ;
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		const char * usr_dir = Package->UsrDirs->GetSubDir() ;
		int only_once = 0 ;
		if (usr_dir) {
			only_once = 1 ;
			const char * c_dir =  Package->CDirs->GetSubDir();
			if (c_dir) if (!strcmp(usr_dir,c_dir)) only_once = 0 ;
		}
		if (only_once) {
			OnlyOnce(Cmds);
			return ;
		}
		const char * SubDir = TheData->Get();
		Package->UsrDirs->SetSubDir(SubDir);
		// cerr << "Set usr sub dir to `" << SubDir << "'\n" ;
	}
}

void CmdOption::SetRootDirectory(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->root_directory()) {
			OnlyOnce(Cmds) ;
			return ;
		}
		const char * dir = TheData->Get();
		Package->root_directory(dir);
	}
}

void CmdOption::SetMakeMakeDepend(CmdLine& Cmds)
{
    if (!TheData) return ;
    PackageDesc * Package = Cmds.GetPackage();
    if (!Package) return ;
    if (Package->makemake_depend()) {
        OnlyOnce(Cmds);
        return ;
    }
	const char ** Names = new const char * [TheData->Size()+1];
    int i=0 ;
    while(Names[i++]=TheData->Get()) ;
    Package->makemake_depend(Names);
}

void CmdOption::SetIncludeAll(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->include_all()) {
			OnlyOnce(Cmds);
			return ;
		}
		const char * file = TheData->Get();
		Package->include_all(file);
	}
}
		

void CmdOption::SetIncludeMain(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->include_main()) {
			OnlyOnce(Cmds);
			return ;
		}
		const char * file = TheData->Get();
		Package->include_main(file);
	}
}
		
void CmdOption::SetSubdirectory(CmdLine& Cmds)
{
	// LogOut << "SetSubdirectory for `" << option << "'\n" ;
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->CDirs->GetSubDir()) {
			OnlyOnce(Cmds);
			return ;
		}
		const char * SubDir = TheData->Get();
		Package->CDirs->SetSubDir(SubDir);
		if (!Package->UsrDirs->GetSubDir()) {
			Package->UsrDirs->SetSubDir(SubDir);
			// cerr<<"Def:Set usr sub dir to `" <<SubDir << "'\n" ;
		}
	}
}

void CmdOption::SetNoExecutable(CmdLine& Cmds)
{
	// LogOut << "SetNoExecutable\n" ;
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		Package->SetNoExecutable();
	}
}

void CmdOption::SetDirSpace(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) return ;
	const char * Tmp = TheData->Get();
	// if (!*Tmp) return ;
	char * buf = strcpy(new char[strlen(Tmp)+1],Tmp);
	int Value = 0;
	if (sscanf(buf,"%d",&Value)==1) Package->set_extra_directory_space(Value);
	else {
		cerr << "The parameter for `" << option <<
			"' must be an integer.\n" ;
		Cmds.NewError();
	}
}

void CmdOption::SetLibraryRepeat(CmdLine& Cmds)
{
	if (!TheData) return ;
	PackageDesc * Package = Cmds.GetPackage();
	if (!Package) return ;
	const char * Tmp = TheData->Get();
	// if (!*Tmp) return ;
	char * buf = strcpy(new char[strlen(Tmp)+1],Tmp);
	int Value = 0;
	if (sscanf(buf,"%d",&Value)==1) Package->RepeatLibrarys(Value);
	else {
		cerr << "The parameter for `" << option <<
			"' must be an integer.\n" ;
		Cmds.NewError();
	}
}

void CmdOption::SetObjSuffix(CmdLine& Cmds)
{
	if (!TheData) return ;
	if (!Cmds.GetPackage()) return ;
	const char * Tmp = TheData->Get();
	if (Tmp) if (*Tmp) {
		ObjSuffix = Concatenate(Tmp);
		return ;
	}
	cerr << "The parameter for `" << option << "' is missing.\n" ;
	Cmds.NewError();
}


void CmdOption::SetCommandFile(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) {
		cerr << "The `" << option <<
		"' must be the first and only command line option.\n" 
		<< "All other options will be taken from the file " <<
		"argument to this command.\n" ;
		Cmds.NewError();
	}
}


GlobalMacrosFilesOptions MacrosFilesOptions = GlobalMacrosFilesNone ;
// These are global externals used by other routines

void CmdOption::list_files(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) return ;
	if (Cmds.GetPackage()->list_files()) OnlyOnce(Cmds);
	else Cmds.GetPackage()->list_files(1);
}

void CmdOption::user_copyright(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) return ;
	if (Cmds.GetPackage()->user_copyright()) OnlyOnce(Cmds);
	else Cmds.GetPackage()->set_user_copyright();
}

void CmdOption::Set_ranlib(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) return ;
	if (Cmds.GetPackage()->ranlib_set()) OnlyOnce(Cmds);
	else Cmds.GetPackage()->set_ranlib();
}

void CmdOption::Set_og(CmdLine& Cmds)
{
	if (Cmds.GetPackage()) return ;
	if (!GlobalMacrosInMake) OnlyOnce(Cmds);
	GlobalMacrosInMake = 0 ;
}


void CmdOption::Set_tic30(CmdLine& Cmds)
{
	if (Cmds.GetPackage()) return ;
	if (TiC30Option) OnlyOnce(Cmds);
	else TiC30Option = 1 ;
}

void CmdOption::Set_arith(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) return ;
	if (Cmds.GetPackage()->IsArith()) OnlyOnce(Cmds);
	else Cmds.GetPackage()->SetArith() ;
}

void CmdOption::Set_lkf(CmdLine& Cmds)
{
	if (Cmds.GetPackage()) return ;
	if (Use_makemake_link) OnlyOnce(Cmds);
	else Use_makemake_link = 1 ;
}

void CmdOption::Set_dbXtra(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) return ;
	if (Cmds.GetPackage()->Set_dbXtra()) OnlyOnce(Cmds);
}

void CmdOption::SetNoSourceNoWarn(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) return ;
	const char ** Names = new const char * [TheData->Size()+1];
	int i=0 ;
	while(Names[i++]=TheData->Get()) ;
	Cmds.GetPackage()->SetNoSourceNoWarnList(Names);
}

void CmdOption::CreateSourceDirList(CmdLine& Cmds)
{
	if (!Cmds.GetPackage()) return ;
	Cmds.GetPackage()->SetCreateSourceDirList();
}

void CmdOption::Set_gf(CmdLine& Cmds)
{
	if (Cmds.GetPackage()) return ;
	if (MacrosFilesOptions != GlobalMacrosFilesNone) OnlyOnce(Cmds);
	else MacrosFilesOptions = GlobalMacrosFiles ;
}

void CmdOption::Set_gfb(CmdLine& Cmds)
{
	if (Cmds.GetPackage()) return ;
	MacrosFilesOptions = GlobalMacrosFilesBackSlash ;
}

void CmdOption::Set_gfs(CmdLine& Cmds)
{
	if (Cmds.GetPackage()) return ;
	MacrosFilesOptions = GlobalMacrosFilesOneLine ;
}


void CmdOption::CheckUsrC(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) if (!Package->CheckUsrC()) {
		cerr << "Some `.usr' directories are not `.cxx' directories\n" ;
		cerr << "and the `" << option << "' is set.\n" ;
		Cmds.NewError();
	}
}


void CmdOption::SetColl(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->IsCollectLibrarys()) OnlyOnce(Cmds);
		Package->CollectLibrarys();
	}
}

void CmdOption::SetLibNm(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (Package->IsUpLibraryName()) OnlyOnce(Cmds);
		Package->SetUpLibraryName();
	}
}

void CmdOption::SetObjective(CmdLine& Cmds)
{
	if (!TheData) return ;
	if (Cmds.GetPackage()) return ;
	if (Cmds.Objective) OnlyOnce(Cmds) ;
	else Cmds.Objective = TheData->Get();
}

void CmdOption::SetMenuFlag(CmdLine& Cmds)
{
	if (Cmds.GetPackage()) return ;
	if (Cmds.MenuFlag) OnlyOnce(Cmds) ;
	if (!TheData) return ;
	Cmds.MenuFlag = TheData->Get();
}

void CmdOption::Execute(CmdLine& Cmds)
{
	if (TheAction == 0) {
		cerr << "Internal error: No action for command `" <<
			option << "'.\n" ;
		Cmds.NewError();
		return ;
	}
/*
 *	LogOut << "Execute for `" << option << "', OptionSet = " <<
 *		OptionSet << "\n" ;
 */
	if (OptionSet) (this->*TheAction)(Cmds);
}


extern CmdOption * OnlyOneClist[] ;

static CmdOption HdirOption = {"-h", "hdir1 hdir2 ... hdirn",
	"the list of directories with `.h' header files", ParamList,
	&CmdOption::SetHdir};

static CmdOption CdirOption = {"-c", "cdir1 cdir2 ... cdirn",
	"the list of directories with `.cxx' program files", ParamList,
	&CmdOption::SetCdir,0,OnlyOneClist};

static CmdOption MdirOption = {"-m", "cdir_link cdir2 ... cdirn",
	"the list of directories with `.cxx' program files beginning with directory for global (across directory) linking",
	ParamList, &CmdOption::SetMdir,0,OnlyOneClist};

static CmdOption UsrDirOption = {"-usr", "usrdir1 usrdir2 ... usrdirn",
	"the list of directories with `.usr' program files (usually each `.usr' directory must also be a header (-h) directory and either a library (-l) or C (-c) directory)",
	ParamList, &CmdOption::SetUsrDir};

static CmdOption LibDirOption = {"-l", "libdir1 libdir2 ... libdirn",
	"the list of library directories with `.cxx' program files",
	ParamList, &CmdOption::SetLibDir,0,OnlyOneClist};

static CmdOption MenuOption = {
	"-menu", "MenuDir MenuInput MenuIncDir file1 file2 ... filen",
	"the directory for creating menu files, the menu input file, the menu includes directory and the list of files created by the menu generator",
	ParamList, &CmdOption::SetMenuOptions};

static CmdOption NcollDirOption = {"-ncoll", "dir1 dir2 ... dirn",
"the directories to skip in collecting object files (the -coll option must be set)",
	ParamList,
	&CmdOption::SetNoCollDir,0,0,&CmdOption::CheckCollectSet};

static CmdOption NoCCOption = {"-nc", "file1 file2 ... filen",
"the `.cxx' files for which no CC command will be created (usually the files are compiled with special options using `Makefile.tail')",
	ParamList,
	&CmdOption::SetNoCC};

static CmdOption CheckMakeList = {"-check_make_lst",
	"dir1 dir2 ... dirn",
"a list of directories to connect to and do a make before anything else",
	ParamList,
	&CmdOption::AddMakeList,0,0,0,
	MultipleOccurences};

static CmdOption InternalLibMake = {"-int_lib_make",
	"dir lib",
"move library created in (use -l)  `dir' to  `lib'",
	ParamTwo,
	&CmdOption::AddInternalLibMake,0,0,0,
	MultipleOccurences};

static CmdOption ExternalLibMake = {"-ext_lib_make",
	"dir lib",
"go to directory `dir' to `make' library `lib'",
	ParamTwo,
	&CmdOption::AddExternalLibMake,0,0,0,
	MultipleOccurences};

static CmdOption LibCollDirList = {"-libcolldirlst",
	"dir_lib dir1 dir2 ... dirn",
"a list of directories from which files will be linked in a single library directory `dir_lib'",
	ParamList,
	&CmdOption::AddLibDirList,0,0,0,
	MultipleOccurences};

static CmdOption SearchLibList = {
	"-libs", "[dir1/]file1 [dir2/]file2 ... [dirn/]filen",
"the list of librarys to be searched in building the executable",
	ParamList,
	&CmdOption::SetSearchLibList};

static CmdOption LibList = {"-liblst", "dir1/file1 dir2/file2 ... dirn/filen",
"the list of library names",
	ParamList,
	&CmdOption::SetLibList};

static CmdOption LibDirList = {"-libdirlst", "dir1 dir2 ... dirn",
"the list of directorys where librarys with names Libdiri are contained",
	ParamList,
	&CmdOption::SetLibDirList};

static CmdOption LibUpDirList = {"-libupdirlst", "dir1 dir2 ... dirn",
"the list of directorys where librarys with name LibUpDiri are contained where UpDiri is the parent directory of diri",
	ParamList,
	&CmdOption::SetLibUpDirList};

static CmdOption IncludeMainOption = {"-incm","main make include file",
"a file to be included in the master Makefile",
	ParamOne,&CmdOption::SetIncludeMain};
	
static CmdOption MakeMakeDependOption = {"-makemake",
	"command [file1 file2 ... filen]",
"command to make `Makefile' and list of dependencies",
	ParamList,&CmdOption::SetMakeMakeDepend};
	
static CmdOption IncludeAllOption = {"-inc","make include file",
"a file to be included in all Makefiles generated",
	ParamOne,&CmdOption::SetIncludeAll};
	
static CmdOption SubdirectoryOption = {"-sc","subdirectory name",
"the directory name (as a subdirectory of the C and library directories) to put the object and library generated files (if this subdirectory does not exist it will be created)",
	ParamOne,&CmdOption::SetSubdirectory};
	
static CmdOption SubUsrdirectoryOption = {"-scu","usr subdirectory name",
"set usr subdirectory name (by default it is set to the C subdirectory name this overides that default)",
	ParamOne,&CmdOption::SetUsrSubdirectory};
	
static CmdOption SetRootDirectoryOption = {"-root","root directory name",
"set root name",
	ParamOne,&CmdOption::SetRootDirectory,&default_root_directory};
	
static CmdOption ListFilesOption = {"-list_files",0,
	"write lists of various file types in `ALL_type'",
	ParamNone,&CmdOption::list_files};

static CmdOption UserCopyrightOption = {"-user_copyright",0,
	"add user copyright notice to each Makefile generated",
	ParamNone,&CmdOption::user_copyright};

static CmdOption RanlibOption = {"-rl",0,
	"use ranlib",
	ParamNone,&CmdOption::Set_ranlib};

static CmdOption OmitGlobalOption = {"-og",0,
	"no global macros in the `Makefile' (if these macros are too long, some versions of make abort with a core dump)",
	ParamNone,&CmdOption::Set_og};

static CmdOption LinkFileOption = {"-lkf",0,
	"that the final link is to be done using file `makemake_link'",
	ParamNone,&CmdOption::Set_lkf};

static CmdOption ArithOption = {"-arith",0,
	"that C files supporting multiple arithmetic models are supported",
	ParamNone,&CmdOption::Set_arith};

static CmdOption CreateSoureDirListOption = {"-sourcelist",0,
	"creation in each library and executable directory of file `source.lst' containing all directories that provide source code",
	ParamNone,&CmdOption::CreateSourceDirList};

static CmdOption dbXtraFileOption = {"-dbXtra",0,
	"create file dbXtra.in to give list of source directories",
	ParamNone,&CmdOption::Set_dbXtra};

static CmdOption NoSourceNoWarnDirListOption = {
	"-no_source_list", "dir1 dir2 ... dirn",
"the list of library directories for which source code is not available. If `-dbXtra' or `-sourcelist' is set, any library directory not containing file `source.list' and not in the list specified by this command gives a warning",
	ParamList,
	&CmdOption::SetNoSourceNoWarn};

static CmdOption GlobalFileOption = {"-gf",0,
	"that the global macro lists are to be output to files",
	ParamNone,&CmdOption::Set_gf};

static CmdOption GlobalFileBackSlashOption = {"-gfb",0,
	"each line of the global macro files will end with a back slash (\\)",
	ParamNone,&CmdOption::Set_gfb};

static CmdOption GlobalFileSingleLineOption = {"-gfs",0,
	"no newlines in the global macro files",
	ParamNone,&CmdOption::Set_gfs};
	
static CmdOption CheckUsrCOption = {"-ckusrc",0,
	"insure that all -usr files are also -c file",
	ParamNone,&CmdOption::CheckUsrC};
	
static CmdOption CollectOption = {"-coll",0,
"the collection of all object files not in library directories in a gloabl library",
	ParamNone,&CmdOption::SetColl};
	
static CmdOption DirectorySpaceOption = {"-dir_space","N",
"the extra space for directories (default 32 should be adequate)",
	ParamInteger,&CmdOption::SetDirSpace};
	
static CmdOption LibNameOption = {"-libnm",0,
"the use of the parent directory name to generate the library name",
	ParamNone,&CmdOption::SetLibNm};
	
static CmdOption LibraryRepeatOption = {"-Lib","N",
"N repetitions of the library references in the final link (this may be needed with some linkers to pull in all referenced files)",
	ParamInteger,&CmdOption::SetLibraryRepeat};
	
static CmdOption SetObjectiveOption = {"-o","executable",
	"name of executable to create (default is `a.out')",
	ParamOne,&CmdOption::SetObjective};
	
static CmdOption SetMenuFlagOption = {"-menuflag","flag",
	"pass the parameter to the `domenus' command (requires -menu)",
	ParamOne,&CmdOption::SetMenuFlag,0,0,&CmdOption::CheckMenuSet};
	
static CmdOption NoExecutableOption = {"-NE",0,
	"creation of librarys only with no executable",
	ParamNone,&CmdOption::SetNoExecutable};
	
static CmdOption CommandFileOption = {"-f","command_line_file",
	"that all other options are to be read from the file argument",
	ParamOne,&CmdOption::SetCommandFile};
	
static CmdOption ObjSuffixOption = {"-obj","obj_suffix",
	"the suffix for object files (default is o)",
	ParamOne,&CmdOption::SetObjSuffix};
	
static CmdOption SetTiC30Option = {"-tic30",0,
	"create line file names for tic30",
	ParamNone,&CmdOption::Set_tic30};

static CmdOption TexUsageOption = {"-tex",0,
	"output command line options in tex format",ParamNone,
	&CmdOption::TeXOut};

int CmdOption::CheckCollectSet(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (!Package->IsCollectLibrarys()) {
			cerr << "When option `" << option <<
				"' is set, option `" << CollectOption.option
				<< "' must be set too.\n" ;
			Cmds.NewError();
			return 0 ;
		}
	}
	return 1 ;
}

int CmdOption::CheckMenuSet(CmdLine& Cmds)
{
	PackageDesc * Package = Cmds.GetPackage();
	if (Package) {
		if (!Package->GetMenuSize()) ;
		cerr << "When option `" << option << "' is set, option `"
			<< MenuOption.option << "' must be set too.\n" ;
		Cmds.NewError();
		return 0 ;
	}
	return 1;
}

static CmdOption * TheCmdOptions[] = {
	&MdirOption,
	&CdirOption,
	&HdirOption,
	&UsrDirOption,
	&LibDirOption,
	&MenuOption,
	&NcollDirOption,
	&NoCCOption,
	&IncludeMainOption,
	&IncludeAllOption,
	&MakeMakeDependOption,
	&SubdirectoryOption,
	&SubUsrdirectoryOption,
	&SetRootDirectoryOption,
	&OmitGlobalOption,
	&RanlibOption,
	&UserCopyrightOption,
	&ListFilesOption,
	&CreateSoureDirListOption,
	&dbXtraFileOption,
	&NoSourceNoWarnDirListOption,
	&GlobalFileOption,
	&LinkFileOption,
	&ArithOption,
	&GlobalFileBackSlashOption,
	&GlobalFileSingleLineOption,
	&CollectOption,
	&DirectorySpaceOption,
	&LibNameOption,
	&LibraryRepeatOption,
	&SetObjectiveOption,
	&SetMenuFlagOption,
	&NoExecutableOption,
	&CommandFileOption,
	&ObjSuffixOption,
	&CheckUsrCOption,
	&SearchLibList,
	&LibList,
	&LibDirList,
	&LibUpDirList,
	&LibCollDirList,
	&CheckMakeList,
	&InternalLibMake,
	&ExternalLibMake,
	&SetTiC30Option,
	&TexUsageOption,
	0
};

CmdOption * OnlyOneClist[] = {
	&CdirOption,
	&LibDirOption,
	&MdirOption,
	0
};

static int cmd_compare(const void * a, const void *b)
{
	CmdOption ** A = (CmdOption **) a ;
    CmdOption ** B = (CmdOption **) b ;
    if (!A) if (!B) return 0 ; else return -1 ;
    if (!B) return 1 ;

	CmdOption *AA = * A ;
	CmdOption *BB = * B ;
    if (!AA) if (!BB) return 0 ; else return -1 ;
    if (!BB) return 1 ;

    const char * Aa = AA->option ;
    const char * Bb = BB->option ;
    if (!Aa) if (!Bb) return 0; else return -1 ;
    if (!Bb) return 1 ;
    return TeXstrcmp(Aa,Bb);

}


static void sort_commands(CmdOption **  TheCommands)
{
	for (int size = 0 ; TheCommands[size]; size++);
	qsort((char *) TheCommands, size, sizeof(TheCommands[0]),cmd_compare);
	
}

void CmdOption::TeXOut(CmdLine& Cmds)
{
	CmdOption **  TheCommands ;
	sort_commands(TheCmdOptions);
	OutTokens Out(&cout,0,""," ","    ",60,35,OutTokens::ReturnPage) ;
	Out.ReturnOr(2);
	Out.NextConcat("Usage is:") ;
	Out.NewLine();
	int OutOr = 0 ;
	for (TheCommands= TheCmdOptions; *TheCommands;
		TheCommands++) {
		if (OutOr) Out.NextFillOut("|");
		OutOr = 1 ;
		Out.NextOut("[");
		(*TheCommands)->UsageOut(Out);
		Out.NextOut("]");
	}
	Out.NewLine();
	Out.NextConcat("The interpretation of these options is:") ;
	Out.NewLine();
	for (TheCommands= TheCmdOptions; *TheCommands; TheCommands++)
		(*TheCommands)->MeaningOut(Out);
	Out.ReturnOr(5);
	exit(0);
}


static void NoArguments()
{
	CmdOption **  TheCommands ;
	sort_commands(TheCmdOptions);
	OutTokens Out(&cerr,0,"") ;
	Out.NextConcat("Usage is:") ;
	Out.NewLine();
	int OutOr = 0 ;
	for (TheCommands= TheCmdOptions; *TheCommands;
		TheCommands++) {
		if (OutOr) Out.NextFillOut("|");
		OutOr = 1 ;
		Out.NextOut("[");
		(*TheCommands)->UsageOut(Out);
		Out.NextOut("]");
	}
	Out.NewLine();
	Out.NextConcat("The interpretation of these options is:") ;
	Out.NewLine();
	for (TheCommands= TheCmdOptions; *TheCommands; TheCommands++)
		(*TheCommands)->MeaningOut(Out);
	exit(1);
}

static void  ReadCmdFile(const char * Name, int& argc, char *** argv)
{
	ifstream arguments(Name);
	if (!arguments) {
		cerr << "Cannot open command line file `" << Name << "'.\n" ;
		exit(3);
	}
	const Bump = 100 ;
	int Size = Bump ;
	*argv = new char * [Size] ;
	char Buf[4096];
	(*argv)[0] = "EMPTY" ;
	argc = 1 ;
	while (arguments >> Buf) {
		// cerr << "Read: `" << Buf << "'.\n" ;
		if (argc > Size - 3) {
			char ** tmp = new char * [Size+=Bump];
			for (int i = 0 ; i < argc; i++) tmp[i] = (*argv)[i] ;
			delete *argv ;
			*argv = tmp ;
		}
		char ** Dest = &((*argv)[argc++]) ;
		*Dest = new char[strlen(Buf)+1];
		strcpy(*Dest,Buf);
	}
}

	
void CmdLine::DoCommandOptions()
{
	CmdOption ** TheCommands ;
	for (int i = 1 ; i < ArgC; i++) {
		for (TheCommands= TheOptions; *TheCommands; TheCommands++)
			if ((*TheCommands)->DoCommandOption(*this,i)) break ;
		if (!*TheCommands) {
			cerr << "Unknown command line option `" <<
				ArgV[i] << "'.\n" ;
			NewError();
		}
	}
}

void CmdLine::CheckCommandOptions()
{
	CmdOption ** TheCommands ;
	for (TheCommands= TheOptions; *TheCommands; TheCommands++)
		(*TheCommands)->DoCheck(*this);
}

void CmdLine::ExecuteCommandOptions()
{
	CmdOption ** TheCommands ;
	// LogOut << "FirstExecute, NewPackage = " << NewPackage << "\n" ;
	for (TheCommands= TheOptions; *TheCommands; TheCommands++)
		(*TheCommands)->Execute(*this);
	NewPackage = new PackageDesc(cfiles, hfiles, usrfiles,
		NoCollect, MenuFiles,MenuFlag,MenuDir) ;
/*
 *	LogOut << "cfiles = " << cfiles << ", hfiles = " << hfiles <<
 *		", usrfiles = " << usrfiles << "\n" ;
 *	LogOut << "NoCollect = " << NoCollect << ", MenuFiles = " <<
 *		MenuFiles << ", MenuFlag = " << MenuFlag
 *		<< ", MenuDir = " << MenuDir << "\n" ;
 */
	AnyError();
	// LogOut << "SecondExecute, NewPackage = " << NewPackage << "\n" ;
	for (TheCommands= TheOptions; *TheCommands; TheCommands++)
		(*TheCommands)->Execute(*this);
	NewPackage->SetObject(Objective);
}


PackageDesc * CmdLineParse(const int Theargc, const char ** Theargv)
{
	int Main = 0;
	const char ** FileArgs = 0 ;
	int argc = Theargc ;
/*
 *	LogOut << "Theargc = " << Theargc << ", Theargv[2] = " <<
 *		Theargv[2] << "\n" ;
 */
	if (Theargc==3) if (!strcmp(Theargv[1],"-f"))
		ReadCmdFile(Theargv[2], argc, (char ***) &FileArgs);
	const char ** argv = FileArgs ? FileArgs : Theargv ;
	CmdLine * Cmd = new CmdLine(argc,argv);
	if (Cmd->Count() == 1) NoArguments();
	int cfiles = 0;
	int hfiles = 0;
	int NoCollect = 0 ;
	int ncfiles = 0;
	int usrfiles = 0;
	int NoExecutableFlag = 0 ;
	int MenuFiles = 0;
	int CollectFlag = 0;
	int LibraryRepeat = 0 ;
	const char * MenuFlag = 0 ;
	const char * MenuDir = 0 ;
	Cmd->SetCommandOptions(TheCmdOptions);
	Cmd->DoCommandOptions();
	Cmd->CheckCommandOptions();
	Cmd->AnyError();
	Cmd->ExecuteCommandOptions();
	Cmd->AnyError();
	return Cmd->GetPackage();
}

