// $Header: /MeWin/Src/DBTOOLS.S 18    3/01/96 14:45 Dan $

macro_file DBTOOLS;                     // Db Tools with CommandSet API

#include METOOLS.SH
#include DBTOOLS.SH
#include MENUS.SH

#ifdef _DEBUG_
	#include DBUG.SH
  #include MSGLOG.SH
#endif


void DbToolsExit( int Result, int DbFlags, int OrgWin ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Support routine for the DbTools routines.  Will check DbFlags for
						the no_delete and no_switch window flags and will delete the
						current if it is not OrgWin window and switch to OrgWin when the
						flags are not set.  Also clears some global strings attached to the
						deleted window by some of the DbTools routines.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	if ( ( Result != _ErrorNoFile ) && ( Window_Id != OrgWin ) ) {
		if ( ( DbFlags & _dbf_NoDelWin ) == 0 ) {
			Set_Global_Str( _db_PageGlob + Str( Window_Id ), "" );
			Set_Global_Str( _db_RecGlob + Str( Window_Id ), "" );
			Set_Global_Str( _db_CmdGlob + Str( Window_Id ), "" );
			if ( File_Changed ) {
				Save_File;
			}
			Delete_Window;
			DbFlags = 0;
		}
		if ( ( DbFlags & _dbf_NoSwitchWin ) == 0 ) {
			Switch_Win_Id( OrgWin );
		}
	}
}  // DbToolsExit

int DbGetFile( str FName[ 128 ] ) {
/******************************************************************************
															 Multi-Edit Macro
															 20-Feb-95  16:46

	Function: Locate and load the db file FName.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _NoError;
	int PathSpecified = Get_Path( FName ) != '';

	str Path = '';

	if ( Get_Extension( FName ) == '' ) {
		if ( Global_Str( '@DB_EXTENSION' ) == '' ) {
			FName += '.DB';
		}
		else {
			FName += '.' + Global_Str( '@DB_EXTENSION' );
		}
	}
	if ( !PathSpecified ) {
		Path = GetUserPath;
	}
	FName = Caps( FName );
	if ( Switch_File( Path + FName ) ) {
		FName = Path + FName;
		Error_Level = 0;
	}
	else {
		if ( !PathSpecified ) {
			FName = CreateUserPath( FName, True );
		}
		Error_Level = 0;
		if ( !Switch_File( FName ) ) {
			Switch_Window( Window_Count );
			Create_Window;
			Load_File( FName );
			if ( Error_Level ) {
				File_Name = FName;
				File_Changed = False;
			}
			Window_Attr = 0x81;
			Result = _ErrorNoFile;
		}
	}
	return ( Result );

}  // DbGetFile

int DbGetPage( str FName[ 128 ], str DbPage[ 80 ], int &FirstRec, int &NumRec ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Locate a DbPage and return the line number of the firt record and
						the number of record in the page.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int OrgWin = Window_Id;
	int Result = _ErrorNoDbPage;
	int TRefresh = Refresh;
	int Fnd;

	str FndStr = "^(\f)";

	Refresh = False;
	if ( FName == "" ) {
		Tof;
		Fnd = Find_Text( "^\f" + DbPage + "$| ", 0, _RegExp );
	}
	else {
		Fnd = LocateDbPage( FName, DbPage, FirstRec );
	}
	if ( Caps( Get_Extension( DbPage ) ) != ".HDR" ) {
		FndStr += "|(" + Make_Literal_X( "*****START*****" ) + ")";
	}
	NumRec = 0;
	if ( Fnd == True ) {
		Mark_Pos;
		Result = _NoError;
		FirstRec = C_Line;
		Eol;
		if ( Find_Text( FndStr, 0, _RegExp ) == True ) {
			if ( Cur_Char != "\f" ) {
				FirstRec = C_Line;
				if ( Find_Text( "^\f", 0, _RegExp ) == False ) {
					Eof;
					if ( C_Col > 1 ) {
						Down;
					}
				}
			}
		}
		else {
			Eof;
			if ( C_Col > 1 ) {
				Down;
			}
		}
		++FirstRec;
		NumRec = C_Line - FirstRec;
		Goto_Mark;
	}
	else if ( Error_Level != 0 ) {
		Delete_Window;
		Switch_Win_Id( OrgWin );
		Error_Level = 0;
		Result = _ErrorNoFile;
	}
	Refresh = TRefresh;
	return ( Result );

}  // DbGetPage

int DbDelPage( str FName[ 128], str DbPage[ 80 ], int &Flags ) {
/******************************************************************************
															 Multi-Edit Macro
															 30-Mar-95  11:55

	Function: Locate a DbPage and delete it and all records under it.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int DbFlags = Flags;
	int OrgWin = Window_Id;
	int TRefresh = Refresh;
	int FirstRec = 0;
	int Result;

	Refresh = False;
	Result = DbGetPage( FName, DbPage, FirstRec, Flags );
	if ( Result == _NoError ) {
		Block_Begin;
		Goto_Line( C_Line + Flags );
		Delete_Block;
		if ( DbFlags & _dbf_SaveFile ) {
			Save_File;
		}
	}
	DbToolsExit( Result, DbFlags, OrgWin );	 // Del and switch windows if needed
	Refresh = TRefresh;
	return ( Result );

}  // DbDelPage

int DbGetRecord( str FName[ 128 ], str DbPage[ 80 ], str DbField, str &DbData,
				int &RecNum, int &Flags, str Misc )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Finds and returns a specified record from DbPage in the db file
						FName.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int DbFlags = Flags;
	int OrgWin = Window_Id;
	int TRefresh = Refresh;
	int FirstRec = DbFlags & _dbf_CreatePage;
	int NumRec;
	int Result;

	Refresh = False;
	Result = DbGetPage( FName, DbPage, FirstRec, NumRec );
	if ( Result == _NoError ) {
		Flags = NumRec;
		Result = _ErrorNoDbRecord;
		if ( NumRec < 1 ) {
			Down;
			Goto Exit;
		}

		char Delim = Parse_Str( "/LD=", Misc );
		if ( Delim == "\x0" ) {
			Delim = _db_Delimit;
		}

		int L = 0, C, R, O;
		int FndFlag = _RegExp | ( DbFlags & _sf_CaseSensitive );
		int LastRec = NumRec + FirstRec;

		if ( RecNum > 0 ) {
			if ( RecNum > NumRec ) {
				if ( DbFlags & _sf_ErrorNoRec ) {
					Down;
					Goto Exit;
				}
				RecNum = 1;
			}
			Result = _NoError;
			Goto_Line( FirstRec + RecNum - 1 );
		}
		else {
			Goto_Line( FirstRec );
		}
		if ( ( DbFlags & _sf_Position ) && !( DbFlags & _dbf_NoDups ) ) {
			Result = _ErrorNoDbRecord;
			if ( RecNum < 1 ) {
				Get_Mark_Record( 2, 3, L, C, R, O );
				if ( L != 0 ) {
					Goto_Line( L );
				}
			}
			if ( DbData == "" ) {
				DbData = Global_Str( _db_RecGlob + Str( Window_Id ) );
				DbField = Parse_Str( "/F=", DbData );
				DbData = Parse_Str( "/D=", DbData );
			}
			NumRec = 0;
			if ( DbFlags & _sf_Backwards ) {
				FndFlag |= _sf_Backwards;
				if ( C_Line > FirstRec ) {
					NumRec = C_Line - FirstRec;
				}
				Up;
				Eol;
			}
			else {
				Down;
				Goto_Col( 1 );
				if ( C_Line < LastRec ) {
					NumRec = LastRec - C_Line;
				}
			}
			if ( NumRec < 1 ) {
				Goto Exit;
			}
		}
		if ( Result != _NoError ) {

			str FndStr = Delim + DbField + "=";

			if ( DbFlags & _sf_FirstField ) {
				FndStr = "^" + FndStr;
			}
			if ( ( DbFlags & _sf_RegExp ) == 0 ) {
				DbData = Make_Literal_X( DbData );
			}
			if ( DbFlags & _sf_Contains ) {
				FndStr += "[^" + Delim + "]*" + DbData;
			}
			else {
				FndStr += DbData + Delim + "|$";
			}

			if ( Find_Text( FndStr, NumRec, FndFlag ) == True ) {
				Result = _NoError;
			}
		}
		if ( Result == _NoError ) {
			if ( !( DbFlags & _dbf_NoDups ) ) {
				Set_Global_Str( _db_RecGlob + Str( Window_Id ), "/F=" + DbField +
						"/D=" + DbData );
				Set_Mark_Record( 2, 3, C_Line, C_Col, 1, 1 );
			}
			DbData = Get_Line;
			RecNum = C_Line - FirstRec + 1;
		}
		else if ( !( DbFlags & _sf_ErrorNoRec ) ) {
			DbData = Get_Line;
			RecNum = C_Line - FirstRec + 1;
		}
	}

Exit:
	DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
	Refresh = TRefresh;
	return ( Result );

}  // DbGetRecord

int DbDelRecord( str FName[ 128 ], str DbPage[ 80 ], str DbField, str &DbData,
				int &RecNum, int &Flags, str Misc )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Finds and deletes a specified record from DbPage in the db file
						FName.  Returns the deleted record data.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int DbFlags = Flags;
	int OrgWin = Window_Id;
	int TRefresh = Refresh;
	int Result;

	Refresh = False;
	Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
	Result = DbGetRecord( FName, DbPage, DbField, DbData, RecNum, Flags, Misc );
	if ( Result == _NoError ) {
		Del_Line;
		if ( DbFlags & _dbf_SaveFile ) {
			Save_File;
		}
	}
	DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
	Refresh = TRefresh;
	return ( Result );

}  // DbDelRecord

int DbPutRecord( str FName[ 128 ], str DbPage[ 80 ], str DbField, str &DbData,
				int &RecNum, int &Flags, str Misc, str NewData[ Max_Line_Length ] )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Insert a new record or replace a specified record in DbPage in the
						db file FName with the record in NewData.  Returns the found and/or
						replaced record data.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( NewData != "" ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;
		int TInsert_Mode = Insert_Mode;
		int DupRecord = False;
		int DoUpdate = ( DbFlags & ( _dbf_ReplaceRec | _dbf_Update ) );
		int ReplaceRec = ( DoUpdate == _dbf_ReplaceRec );
		int TRecNum = RecNum;
		int AddRecNum = False;

		Refresh = False;
		Insert_Mode = True;
		Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
    if ( ( DbField != "" ) && ( DbFlags & _dbf_NoDups ) && !ReplaceRec ) {
//    if ( ( DbField != "" ) && ( DbFlags & _dbf_NoDups ) ) {

			char Delim = Parse_Str( "/LD=", Misc );
			if ( Delim == "\x0" ) {
				Delim = _db_Delimit;
			}

			str TStr = Parse_Str( Delim + DbField + "=", NewData );

			int TFlags = Flags;

			Flags &= 0xFFFF ^ _sf_Contains;
			Flags |= _dbf_NoDups;
			RecNum = 0;
			Result = DbGetRecord( FName, DbPage, DbField, TStr, RecNum, Flags, Misc );
			switch ( Result ) {

				case _NoError :
					DupRecord = True;
					Result = _ErrorDupRecord;
					if ( DbFlags & _dbf_Update ) {
						break;
					}
					DbData = TStr;

				case _ErrorNoFile :
				case _ErrorNoDbPage :
					goto Exit;
			}
			FName = "";
			Flags = TFlags;
			RecNum = TRecNum;
		}
		if ( DbData == "" ) {
			if ( RecNum == 0 ) {
				if ( ReplaceRec ) {
					goto Exit;
				}
				if ( !( DbFlags & _sf_ErrorNoRec ) ) {
					Flags |= _sf_Contains;
				}
			}
		}
		else {
			if ( AddRecNum = ( ( DbFlags & _dbf_Update ) && RecNum ) ) {
				RecNum = 0;
			}
		}
		Flags &= 0xFFFF ^ _dbf_NoDups;
		Result = DbGetRecord( FName, DbPage, DbField, DbData, RecNum, Flags, Misc );
		switch ( Result ) {

			case _NoError :
				if ( !DoUpdate ) {
					if ( DbFlags & _dbf_InsertBefore ) {
						Up;
						--RecNum;
					}
					Eol;
					Cr;
					++RecNum;
					++Flags;
				}
				break;

			case _ErrorNoDbRecord :
				if ( ReplaceRec || DupRecord ) {
					if ( DupRecord ) {
						Result = _ErrorDupRecord;
					}
					break;
				}
				DoUpdate = False;
				Result = _NoError;
				Up;
				if ( ( AddRecNum ) && ( TRecNum > 0 ) && ( TRecNum <= Flags ) ) {
					Goto_Line( C_Line + TRecNum );
					if ( DbFlags & _dbf_InsertBefore ) {
						Up;
						RecNum = TRecNum;
					}
					else {
						RecNum = TRecNum + 1;
					}
				}
				else if ( DbFlags & _dbf_InsertBefore ) {
					RecNum = 1;
				}
				else {
					Goto_Line( C_Line + Flags );
					RecNum = Flags + 1;
				}
				Eol;
				Cr;
				++Flags;
				break;
		}
		if ( Result == _NoError ) {
			if ( DoUpdate ) {

				str Fields = Parse_Str( "/F=", Misc );
				str TStr[ Max_Line_Length ];

				if ( XPos( "/X", Misc, 1 ) ) {
					DbUpdateFields( NewData, DbData, Fields );
				}
				else {
					TStr = DbData;
					DbUpdateFields( TStr, NewData, Fields );
					NewData = TStr;
				}
			}
			Put_Line( NewData );
			if ( DbFlags & _dbf_SaveFile ) {
				Save_File;
			}
		}

Exit:
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Insert_Mode = TInsert_Mode;
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbPutRecord

int DbCheckDupFields( str DbPage, str DbLine[ Max_Line_Length ],
				str FieldData, ... )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Searches DbPage in the current window for duplicate fields (one or
						more) that matches FieldData and return True when duplicates found.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Dup = False;
	int TRefresh  = Refresh;
	int FirstRec = 0;
	int NumRec;

	Refresh = False;
	if ( DbGetPage( "", DbPage, FirstRec, NumRec ) == _NoError ) {

		int LastRec = FirstRec + NumRec;
		int Pc = 0;
		int Found = False;

		str FStr;

		while ( Get_Param_Type( ++Pc ) == 1 ) {
			FStr = Get_Param_Str( Pc ) + FieldData;
			FStr = Make_Literal_X( FStr ) + Copy( FStr, 1, 1 ) + "|$";
			Goto_Line( FirstRec );
			while ( Find_Text( FStr, NumRec, _RegExp ) ) {
				if ( DbLine != Get_Line( ) ) {
					Dup = True;
					break;
				}
				else {
					if ( Found ) {                // Check for duplicate entry
						Dup = True;
						break;
					}
					Found = True;                 // Current entry found
				}
				Goto_Col( 1 );
				Down;
				NumRec = LastRec - C_Line;
			}
			if ( Dup ) {
				break;
			}
			NumRec = LastRec - FirstRec;
		}
	}
	Refresh = TRefresh;
	return ( Dup );

}  // DbCheckDupFields

str DbGetKeyStr( int K1, int K2 ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Return a command mapping key string suitable for use with the
						command set APIs.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	str Result = "";

	if ( ( K1 != 0 ) || ( K2 != 0 ) ) {
		Result = "/KL=" + Make_Key_Name( Make_Word( K1, K2 ) );
		Result += "/K1=" + str( K1 ) + "/K2=" + str( K2 );
	}
	return ( Result );

}  // DbGetKeyStr

int DbCheckWCmd( str &DbRecord ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Scans the command map db in the current window for a duplicate WCmd,
						creates a unique WCmd and return the updated DbRecord and WCmd.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = True;
	int TRefresh = Refresh;

	str WCmdStr = _db_Delimit + _db_cmd_WCmd + "=";
	str WCmdVal = Parse_Str( WCmdSTr, DbRecord );

	Refresh = False;
	Mark_Pos;
	if ( WCmdVal != "" ) {
		Result = DbCheckDupFields( _db_cmd_CMPage, DbRecord, WCmdVal, WCmdStr );
	}
	if ( Result == True ) {

		int TWin = Window_Id;
		int SortWin;
		int FirstRec = 0;
		int NumRec;
		int NumSort = 0;
		int UseWCmd = _db_cmd_UserWCmd;

		Create_Window;
		SortWin = Cur_Window;
		Switch_Win_Id( TWin );

		if ( DbGetPage( "", _db_cmd_CMPage, FirstRec, NumRec ) == _NoError ) {
			Goto_Line( FirstRec );
			while ( NumRec > 0 ) {
				Result = Parse_Int( _db_Delimit + _db_cmd_WCmd + "=", Get_Line( ) );
				Down;
				--NumRec;
				if ( Result >= _db_cmd_UserWCmd ) {
					Put_Line_To_Win( Str( Result ), NumSort + 1, SortWin, 0 );
					++NumSort;
				}
			}
		}
		Switch_Window( SortWin );
		if ( NumSort ) {
			QSort_Lines( 1, NumSort, 1, 1, 5, 0 );
			Tof;
			while ( !At_Eof ) {
				Val( Result, Get_Line( ) );
				if ( Result > UseWcmd ) {
					break;
				}
				else {
					UseWCmd = Result + 1;
				}
				Down;
			}
		}
		Delete_Window;
		Switch_Win_Id( TWin );

		Var_Remove_Str( _db_Delimit + _db_cmd_WCmd + "=", DbRecord );
		DbRecord += _db_Delimit + _db_cmd_WCmd + "=" + Str( UseWcmd );
	}
	Result = Parse_Int( WCmdStr, DbRecord );
	Goto_Mark;
	Refresh = TRefresh;
	return ( Result );

}  // DbCheckWCmd

int DbCheckDupKeys( str DbRecord[ 1024 ] ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Check the command map db in the current window for duplicate key
						assignments.  Returns True if duplicates found and the current line
						will contain the record with the duplicate key.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = False;
	int TRefresh = Refresh;

	str KStr = _db_Delimit + _db_cmd_Key + "=";
	str K2Str = _db_Delimit + _db_cmd_Key2 + "=";
	str KVal = Parse_Str( KStr, DbRecord );

	Refresh = False;
	if ( KVal != "" ) {
		Result = DbCheckDupFields( _db_cmd_CMPage, DbRecord, KVal, KStr, K2Str);
	}
	KVal = Parse_Str( K2Str, DbRecord );
	if ( ( Result == False ) && ( KVal != "" ) ) {
		Result = DbCheckDupFields( _db_cmd_CMPage, DbRecord, KVal, KStr, K2Str);
	}
	Refresh = TRefresh;
	return ( Result );

}  // DbCheckDupKeys

str DbCmdMap2Rec( struct TCmdMap CmdMap ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Converts the data in a CmdMap structure to a DbRecord as stored in
						CmdSet DB files.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	str Delim = _db_Delimit;
	str DbRecord[ 1024 ] = "";

	if ( CmdMap.Name != "" ) {
		DbRecord += Delim + _db_cmd_Name + "=" + CmdMap.Name;
	}
	if ( CmdMap.Cmd != "" ) {
		DbRecord += Delim + _db_cmd_Cmd + "=" + CmdMap.Cmd;
	}
	if ( CmdMap.Type != 0 ) {
		DbRecord += Delim + _db_cmd_Type + "=" + Str( CmdMap.Type );
	}
	if ( CmdMap.KeyStr != "" ) {
		DbRecord += Delim + _db_cmd_Key + "=" + CmdMap.KeyStr;
	}
	if ( CmdMap.Key2Str != "" ) {
		DbRecord += Delim + _db_cmd_Key2 + "=" + CmdMap.Key2Str;
	}
	if ( CmdMap.Icon != "" ) {
		DbRecord += Delim + _db_cmd_Icon + "=" + CmdMap.Icon;
	}
	if ( CmdMap.Icon2 != "" ) {
		DbRecord += Delim + _db_cmd_Icon2 + "=" + CmdMap.Icon2;
	}
	if ( CmdMap.TBText != "" ) {
		DbRecord += Delim + _db_cmd_TBText + "=" + CmdMap.TBText;
	}
	if ( CmdMap.HlpCtx != "" ) {
		DbRecord += Delim + _db_cmd_HlpCtx + "=" + CmdMap.HlpCtx;
	}
	if ( CmdMap.OffId != "" ) {
		DbRecord += Delim + _db_cmd_OffId + "=" + CmdMap.OffId;
	}
	if ( CmdMap.Checked != "" ) {
		DbRecord += Delim + _db_cmd_Checked + "=" + CmdMap.Checked;
	}
	if ( CmdMap.WCmdId != "" ) {
		DbRecord += Delim + _db_cmd_WCmdId + "=" + CmdMap.WCmdId;
	}
	if ( CmdMap.Flags != 0 ) {
		if ( CmdMap.Flags & _db_cf_XMin ) {
			DbRecord += Delim + _db_cmd_XMin + "=1";
		}
	}
	if ( CmdMap.WCmd != 0 ) {
		DbRecord += Delim + _db_cmd_WCmd + "=" + Str( CmdMap.WCmd );
	}
	return ( DbRecord );

}  // DbCmdMap2Rec

void DbRec2CmdMap( struct TCmdMap CmdMap, str DbRecord[ 1024 ] ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Fills a TCmdMap structure with the data from a CmdSet Db record.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	str Delim = _db_Delimit;

	CmdMap.Name = Parse_Str( Delim + _db_cmd_Name + "=", DbRecord );
	CmdMap.Cmd = Parse_Str( Delim + _db_cmd_Cmd + "=", DbRecord );
	CmdMap.Type = Parse_Int( Delim + _db_cmd_Type + "=", DbRecord );
	CmdMap.KeyStr = Parse_Str( Delim + _db_cmd_Key + "=", DbRecord );
	CmdMap.Key2Str = Parse_Str( Delim + _db_cmd_Key2 + "=", DbRecord );
	CmdMap.Icon = Parse_Str( Delim + _db_cmd_Icon + "=", DbRecord );
	CmdMap.Icon2 = Parse_Str( Delim + _db_cmd_Icon2 + "=", DbRecord );
	CmdMap.TBText = Parse_Str( Delim + _db_cmd_TBText + "=", DbRecord );
	CmdMap.HlpCtx = Parse_Str( Delim + _db_cmd_HlpCtx + "=", DbRecord );
	CmdMap.OffId = Parse_Str( Delim + _db_cmd_OffId + "=", DbRecord );
	CmdMap.Checked = Parse_Str( Delim + _db_cmd_Checked + "=", DbRecord );
	CmdMap.WCmdId = Parse_Str( Delim + _db_cmd_WCmdId + "=", DbRecord );
	CmdMap.WCmd = Parse_Int( Delim + _db_cmd_WCmd + "=", DbRecord );
	CmdMap.Flags = 0;
	if ( Parse_Int( Delim + _db_cmd_XMin + "=", DbRecord ) != 0 ) {
		CmdMap.Flags |= _db_cf_XMin;
	}
}  // DbRec2CmdMap

int DbGetCmdSection( str FName[ 128 ], str &Section, int &Flags ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Search for and return a command section header from the db file
						FName that matches Section based upon the search Flags.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int DbFlags = Flags;
	int OrgWin = Window_Id;
	int TRefresh = Refresh;
	int Result;
	int RecNo = 0;

	str SecStr = Make_Literal_X( _db_cmd_Section );

	Refresh = False;
	Flags |= _sf_FirstField | _dbf_NoDelWin | _dbf_NoSwitchWin;
	Result = DbGetRecord( FName, _db_cmd_CMPage, SecStr, Section, RecNo, Flags,
			"" );

	if ( Result == _NoError ) {
		Section = Copy( Section, 5, Svl( Section ) - 4 );
		Mark_Pos;
		Down;
		Goto_Col( 1 );
		Flags -= RecNo;
		if ( Flags > 0 ) {

			int FirstRec = C_Line;

			SecStr = "^" + _db_Delimit + SecStr + "=";
			if ( Find_Text( SecStr, Flags, _RegExp ) == True ) {
				Flags = C_Line - FirstRec;
			}
		}
		Goto_Mark;
	}
	DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
	Refresh = TRefresh;
	return ( Result );

}  // DbGetCmdSection

int DbDelCmdSection( str FName[ 128 ], str &Section, int &Flags ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Delete a command Section header and all commands define under it
						from the db file FName.  Returns the full section name and number
						of command records deleted upon success.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( Section != "" ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;

		Refresh = False;
		Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
		Result = DbGetCmdSection( FName, Section, Flags );
		if ( Result == _NoError ) {
			Block_Begin;
			Goto_Line( C_Line + Flags );
			Delete_Block;
			if ( DbFlags & _dbf_SaveFile ) {
				Save_File;
			}
		}
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbDelCmdSection

int DbPutCmdSection( str FName[ 128 ], str &Section, int &Flags, str NewSection ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Add or update a section header in the command list in the FName DB
						file.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( NewSection != "" ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;
		int TInsert_Mode = Insert_Mode;
		int TFlags = Flags | _dbf_NoDelWin | _dbf_NoSwitchWin | _dbf_NoDups;
		int DoUpdate = ( DbFlags & ( _dbf_ReplaceRec | _dbf_Update ) );
		int ReplaceRec = ( DoUpdate == _dbf_ReplaceRec );

		str TStr = NewSection;

		Refresh = False;
		Insert_Mode = True;
		Flags = TFlags & ( 0xFFFF ^_sf_Contains );
		if ( DbGetCmdSection( FName, TStr, Flags ) == _NoError ) {
			Result = _ErrorDupRecord;
			Section = TStr;
			Goto Exit;                        //  Section already exists
		}
		FName = "";
		Flags = TFlags;
		if ( Section == "" ) {
			if ( ReplaceRec ) {
				Goto Exit;
			}
			Flags &= 0xFFFF ^ _sf_Contains;
		}
		Flags &= 0xFFFF ^ _dbf_NoDups;
		Result = DbGetCmdSection( FName, Section, Flags );
		switch ( Result ) {

			case _NoError :                   // Section found
				if ( !DoUpdate ) {
					if ( DbFlags & _dbf_InsertBefore ) {
						Up;
					}
					else {
						Goto_Line( C_Line + Flags );
					}
					Eol;
					Cr;
					Flags = 0;
				}
				break;

			case _ErrorNoDbRecord :           // No section found
				if ( ReplaceRec ) {
					break;
				}
				if ( DbFlags & _dbf_InsertBefore ) {
					Up;
				}
				else {
					if ( Find_Text( "^\f", 0, _RegExp ) == True ) {  // Find end of DbPage
						Up;
					}
					else {
						Eof;
						if ( C_Col == 1 ) {
							Up;
						}
					}
				}
				Eol;
				Cr;
				Flags = 0;
				Result = _NoError;
				break;
		}
		if ( Result == _NoError ) {
			Put_Line( _db_Delimit + _db_cmd_Section + "=" + NewSection );
			if ( DbFlags & _dbf_SaveFile )  {
				Save_File;
			}
		}

Exit:
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Insert_Mode = TInsert_Mode;
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbPutCmdSection

int DbGetCmd( str FName[ 128 ], str &Section, str &CmdRecord, int &Flags )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Search for and return data about a command from the FName db file.
						Commands can be searched for in a number of ways depending upon
						Flags and the data in CmdRecord.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int DbFlags = Flags;
	int OrgWin = Window_Id;
	int Result;
	int TRefresh = Refresh;
	int NumRec = _sf_Contains | _dbf_NoDelWin | _dbf_NoSwitchWin | _dbf_NoDups;
	int FirstRec = DbFlags & _dbf_CreatePage;

	Refresh = False;
	if ( Section != "" ) {
		Result = DbGetCmdSection( FName, Section, NumRec );
		FirstRec = C_Line + 1;
		Flags = FirstRec + NumRec;
		if ( Result != _NoError ) {
			Section = "";
		}
	}
	else {
		Result = DbGetPage( FName, _db_cmd_CMPage, FirstRec, NumRec );
	}
	if ( Result == _NoError ) {
		Result = _ErrorNoDbRecord;
		if ( NumRec < 1 ) {
			Down;
			Goto Exit;
		}

		int L = 0, C, R, O;
		int LastRec = FirstRec + NumRec;
		int FndFlag = _RegExp;
		int WCmd;

		str Delim = _db_Delimit;
		str CmdGlobal = _db_CmdGlob + Str( Window_Id );
		str FndStr;
		str KeyStr;
		str Name;

		Goto_Line( FirstRec );
		if ( ( DbFlags & _sf_Position ) && !( DbFlags & _dbf_NoDups ) ) {
			Get_Mark_Record( 3, 3, L, C, R, O );
			if ( L != 0 ) {
				Goto_Line( L );
			}
			if ( CmdRecord == "" ) {
				CmdRecord = Global_Str( CmdGlobal );
			}
			NumRec = 0;
			if ( DbFlags & _sf_Backwards ) {
				FndFlag |= _sf_Backwards;
				if ( C_Line > FirstRec ) {
					NumRec = C_Line - FirstRec;
				}
				Up;
				Eol;
			}
			else {
				Down;
				Goto_Col( 1 );
				if ( C_Line < LastRec ) {
					NumRec = LastRec - C_Line;
				}
			}
			if ( NumRec < 1 ) {
				Goto Exit;
			}
		}
		Var_Parse_Int( Delim + _db_cmd_WCmd + "=", CmdRecord, WCmd );
		Var_Parse_Str( Delim + _db_cmd_Key + "=", CmdRecord, KeyStr );
		Var_Parse_Str( Delim + _db_cmd_Name + "=", CmdRecord, Name );
		if ( WCmd != 0 ) {
			FndStr = Delim + _db_cmd_WCmd + "=" + Str( WCmd ) + Delim + "|$";
		}
		else if ( KeyStr != "" ) {
			FndStr = Delim + "(" + _db_cmd_Key + ")|(" + _db_cmd_Key2 + ")=" +
					Make_Literal_X( KeyStr ) + Delim + "|$";
		}
		else {
			FndFlag |= ( DbFlags & _sf_CaseSensitive );
			FndStr = "^" + Delim + _db_cmd_Name + "=";
			if ( ( DbFlags & _sf_RegExp ) == 0 ) {
				Name = Make_Literal_X( Name );
			}
			if ( DbFlags & _sf_Contains ) {
				FndStr += "[^" + Delim + "]*" + Name;
			}
			else {
				FndStr += Name + Delim + "|$";
			}
		}
		if ( Find_Text( FndStr, NumRec, FndFlag ) == True ) {
			Result = _NoError;
			CmdRecord = Get_Line( );
			Flags = Parse_Int( _db_Delimit + _db_cmd_WCmd + "=", CmdRecord );
			if ( !( DbFlags & _dbf_NoDups ) ) {
				Set_Global_Str( CmdGlobal, CmdRecord );
				Set_Mark_Record( 3, 3, C_Line, C_Col, 1, 1 );
			}
			Mark_Pos;
			NumRec = C_Line - FirstRec;
			Up;
			Eol;
			Delim += _db_cmd_Section + "=";
			if ( Find_Text( "^" + Make_Literal_X( Delim ), NumRec,
					_RegExp | _Backward ) == True ) {
				Section = Parse_Str( Delim, Get_Line( ) );
			}
			Goto_Mark;
		}
	}

Exit:
	DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
	Refresh = TRefresh;
	return ( Result );

}  // DbGetCmd

int DbDelCmd( str FName[ 128 ], str &Section, str &CmdRecord, int &Flags )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Delete the command specified in Section/CmdRecord from the db file
						FName and return the deleted command data.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( CmdRecord != "" ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;

		Refresh = False;
		Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
		Result = DbGetCmd( FName, Section, CmdRecord, Flags );
		if ( Result == _NoError ) {
			Del_Line;
			if ( DbFlags & _dbf_SaveFile ) {
				Save_File;
			}
		}
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbDelCmd

int DbPutCmd( str FName[ 128 ], str &Section, str &CmdRecord, int &Flags,
				str NewCmdRecord[ 1024 ], str Misc )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Update the command specified in Section/CmdRecord or add the
						command in NewCmdRecord to the db file FName and return the
						replaced command data and WCmd for the new command.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( NewCmdRecord != "" )  {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;
		int TInsert_Mode = Insert_Mode;
		int WCmd = Parse_Int( _db_Delimit + _db_cmd_WCmd + "=", NewCmdRecord );
		int DupRecord = False;
		int DoUpdate = ( DbFlags & ( _dbf_ReplaceRec | _dbf_Update ) );
		int ReplaceRec = ( DoUpdate == _dbf_ReplaceRec );

		Refresh = False;
		Insert_Mode = True;
		Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
    if ( ( DbFlags & _dbf_NoDups ) && !ReplaceRec ) {
//    if ( DbFlags & _dbf_NoDups ) {

			str CmdNameStr = _db_Delimit + _db_cmd_Name + "=";
			str TStr = CmdNameStr + Parse_Str( CmdNameStr, NewCmdRecord );

			int TFlags = Flags;

			Flags &= 0xFFFF ^ _sf_Contains;
			Result = DbGetCmd( FName, Section, TStr, Flags );
			switch ( Result ) {
				case _NoError :
					DupRecord = True;
					Result = _ErrorDupRecord;
					if ( DbFlags & _dbf_Update ) {
						break;
					}
					CmdRecord = TStr;

				case _ErrorNoFile :
				case _ErrorNoDbPage :
					goto Exit;
			}
			FName = "";
			Flags = TFlags;
		}
		if ( CmdRecord == "" ) {
			if ( ReplaceRec ) {
				goto Exit;
			}
			if ( !( DbFlags & _sf_ErrorNoRec ) ) {
				Flags |= _sf_Contains;
			}
		}
		Flags &= 0xFFFF ^ _dbf_NoDups;
		Result = DbGetCmd( FName, Section, CmdRecord, Flags );
		switch ( Result ) {
			case _NoError :
				if ( !DoUpdate ) {
					if ( DbFlags & _dbf_InsertBefore ) {
						Up;
					}
					Eol;
					Cr;
				}
				break;

			case _ErrorNoDbRecord :
				if ( ReplaceRec || DupRecord ) {
					if ( DupRecord ) {
						Result = _ErrorDupRecord;
					}
					break;
				}
				DoUpdate = False;
				if ( Section != "" ) {
					if ( !( DbFlags & _dbf_InsertBefore ) ) {
						Goto_Line( Flags );
					}
					Up;
				}
				else {
					if ( Find_Text( "^\f", 0, _RegExp ) == True ) {
						Up;
					}
					else {
						Eof;
						if ( C_Col == 1 ) {
							Up;
						}
					}
				}
				Eol;
				Cr;
				Result = _NoError;
				break;
		}
		if ( Result == _NoError ) {
			if ( DoUpdate ) {

				str Fields = Parse_Str( "/F=", Misc );
				str TStr[ 1024 ];

				if ( XPos( "/X", Misc, 1 ) ) {
					DbUpdateFields( NewCmdRecord, CmdRecord, Fields );
				}
				else {
					TStr = CmdRecord;
					DbUpdateFields( TStr, NewCmdRecord, Fields );
					NewCmdRecord = TStr;
				}
			}
			if ( Section == "" ) {
				Mark_Pos;
				Goto_Col( 1 );
				do {
					Up;
					if ( XPos( _db_Delimit + _db_cmd_Section + "=", Get_Line( ), 1 ) ) {
						Section = Parse_Str( _db_Delimit + _db_cmd_Section + "=",
								Get_Line( ) );
						break;
					}
				} while ( ( Cur_Char != "\f" ) && ( C_Col != 1 ) );
				Goto_Mark;
			}
			if ( Section != "" ) {
				Mark_Pos;
				Put_Line( NewCmdRecord );
				Flags = DbCheckWCmd( NewCmdRecord );
				Put_Line( NewCmdRecord );
				if ( DbCheckDupKeys( NewCmdRecord ) == True ) {
					Result = _ErrorDupKey;
					Pop_Mark;
				}
				else {
					Goto_Mark;
				}
				if ( DbFlags & _dbf_SaveFile ) {
					Save_File;
				}
			}
			else {
				Result = _ErrorNoDbRecord;
			}
		}

Exit:
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Insert_Mode = TInsert_Mode;
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbPutCmd

str DbExpandChar( str TStr[ Max_Line_Length ], str XChar[ 1 ] ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Makes a regular expressing with XChar and '*' inserted before every
						character in TStr.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/


	int StrLen = Svl( TStr );
	int Cnt = 0;

	str NStr[ Max_Line_Length ] = "";

	while ( Cnt < StrLen ) {
		if(str_char(tstr,cnt+1) == "\\")
		{
			NStr += XChar + "@" + Copy( TStr, ++Cnt, 2 );
			++cnt;
		}
		else
		{
			NStr += XChar + "@" + Copy( TStr, ++Cnt, 1 );
		}
	}
	return ( NStr );

}  // DbExpandChar

str DbMenu2Rec( str MenuText, int Level, int WCmd ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Returns a menu record as stored in the command set db files, for
						MenuText, Level and WCmd.  Can be passed to the DbMenuXXX routines.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	str MenuRec;
	str Sep = "0";

	if ( WCmd == _db_mnu_SepWCmd ) {
		MenuText = "";
		Sep = "1";
	}
	MenuRec = _db_Delimit + _db_mnu_Menu + "=" + _db_mnu_Text + MenuText +
			_db_mnu_Level + Str( Level ) + _db_mnu_Sep + Sep +
			_db_Delimit + _db_mnu_WCmd + "=" + Str( WCmd );

	return ( MenuRec );

}  // DbMenu2Rec

void DbRec2Menu( str &MenuText, int &Level, int &WCmd, str MenuRec ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Fills the passed variables with the data from a MenuRec db record.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	str TStr = Parse_Str( _db_Delimit + _db_mnu_Menu + "=", MenuRec );

	MenuText = Parse_Str( _db_mnu_Text, TStr );
	Level = Parse_Int( _db_mnu_Level, TStr );
	WCmd = Parse_Int( _db_Delimit + _db_mnu_WCmd + "=", MenuRec );

}  // DbRec2Menu

int DbGetMenu( str FName[ 128 ], str MenuPage[ 80 ], struct TMenus MenuStr,
				int &Level, int &WCmd, int &Flags )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Find and return data about a specified menu entry.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;
	if ( ( Level >= 0 ) && ( Level < 5 ) ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;
		int Cnt = 1;
		int TFlags = Flags | _dbf_NoDelWin | _dbf_NoSwitchWin;
		int RecNo = 0;
		int NumRec;
    int Jx;

		str CurMenu[ 1024 ];
		str CurField;
		str LevelSep = _db_mnu_Level + '.' + _db_mnu_Sep + '.';
    str TStr;

		Refresh = False;
		if ( MenuPage == '' ) {
			MenuPage = DefaultMenu;
			if ( MenuPage == '' ) {
				MenuPage = _db_mnu_MainPage;
			}
		}
		if ( Get_Extension( MenuPage ) == '' ) {
			MenuPage += _db_mnu_PageExt;
		}
		if ( WCmd != 0 ) {
			CurField = _db_mnu_WCmd;
			CurMenu = Str( WCmd );
			TFlags &= 0xFFFF ^ _sf_FirstField;
		}
		else {
			CurField = _db_mnu_Menu;
			TFlags |= _sf_RegExp | _sf_FirstField;
			while ( True ) {
				switch ( Cnt ) {
					case 1 :
						CurMenu = MenuStr.Menu1;
						break;

					case 2 :
						CurMenu = MenuStr.Menu2;
						break;

					case 3 :
						CurMenu = MenuStr.Menu3;
						break;

					case 4 :
						CurMenu = MenuStr.Menu4;
						break;
				}
				if ( CurMenu != "" ) {
					CurMenu = Make_Literal_X( CurMenu );
					if ( XPos( "&", CurMenu, 1 ) == 0 ) {
						CurMenu = DbExpandChar( CurMenu, "&" );
					}
					if ( DbFlags & _sf_Contains ) {
						CurMenu += ".*";
					}
					else {
						CurMenu = _db_mnu_Text + CurMenu;
					}
					if ( Cnt >= Level ) {
						if ( Level != 0 ) {
							LevelSep = _db_mnu_Level + Str( Cnt ) + _db_mnu_Sep + ".";
						}
						CurMenu += LevelSep;
						break;
					}
					LevelSep = _db_mnu_Level + Str( Cnt ) + _db_mnu_Sep + ".";
					CurMenu += LevelSep;
					NumRec = TFlags;

					Result = DbGetRecord( FName, MenuPage, CurField, CurMenu, RecNo,
							NumRec, "" );
					if ( Result != _NoError ) {
						Goto Exit;
					}
					FName = "";
					TFlags |= _sf_Position;
					TFlags &= 0xFFFF ^ _dbf_NoDups;
				}
				else if ( Cnt >= Level ) {
					break;
				}
				++Cnt;
			}
		}
		NumRec = TFlags;
		Result = DbGetRecord( FName, MenuPage, CurField, CurMenu, RecNo, NumRec,
				"" );
		if ( Result == _NoError ) {
			Level = Parse_Int( _db_mnu_Level, CurMenu );
			WCmd = Parse_Int( _db_Delimit + _db_mnu_WCmd + "=", CurMenu );
			Flags = 0;
			if ( WCmd == 0 ) {
				// Set Flags to number of menu entries under this menu.
				Mark_Pos;
				Down;
				Cnt = C_Line;
				Flags = NumRec - RecNo;
        TStr = Str( Level );
        // Check for all levels below current level
        for ( Jx = Level - 1; Jx; --Jx ) {
          TStr += "|" + Str( Jx );
        }
        Tstr = "(\f)|(" + _db_mnu_Level + TStr + ")";
        if ( Find_Text( TStr, Flags, _RegExp ) ) {
					Flags = C_Line - Cnt;
				}
				Goto_Mark;
			}
			Mark_Pos;
			Cnt = Level;
			MenuStr.Menu1 = "";
			MenuStr.Menu2 = "";
			MenuStr.Menu3 = "";
			MenuStr.Menu4 = "";
			while ( True ) {
				switch ( Cnt ) {
					case 1 :
						MenuStr.Menu1 = Parse_Str( _db_mnu_Text, CurMenu );
						break;

					case 2 :
						MenuStr.Menu2 = Parse_Str( _db_mnu_Text, CurMenu );
						break;

					case 3 :
						MenuStr.Menu3 = Parse_Str( _db_mnu_Text, CurMenu );
						break;

					case 4 :
						MenuStr.Menu4 = Parse_Str( _db_mnu_Text, CurMenu );
						break;
				}
				--Cnt;
				if ( Cnt == 0 ) {
					break;
				}
				NumRec = C_Line;
				if ( Find_Text( _db_mnu_Level + Str( Cnt ), RecNo, _Backward ) ) {
					CurMenu = Get_Line( );
					RecNo -= NumRec - C_Line;
				}
				else {
					break;
				}
			}
			Goto_Mark;
		}

Exit:
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbGetMenu

int DbDelMenu( str FName[ 128 ], str MenuPage[ 80 ], struct TMenus MenuStr,
				int &Level, int &WCmd, int &Flags )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Find and delete a specified menu entry and all submenus under it.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int DbFlags = Flags;
	int OrgWin = Window_Id;
	int TRefresh = Refresh;
	int Result = _ErrorNoDbRecord;

	Refresh = False;
	Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
	Result = DbGetMenu( FName, MenuPage, MenuStr, Level, WCmd, Flags );
	if ( Result == _NoError ) {
		Block_Begin;
		Goto_Line( C_Line + Flags );
		Delete_Block;
		if ( DbFlags & _dbf_SaveFile ) {
			Save_File;
		}
	}
	DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
	Refresh = TRefresh;
	return ( Result );

}  // DbDelMenu

int DbPutMenu( str FName[ 128 ], str MenuPage[ 80 ], struct TMenus MenuStr,
				int &Level, int &WCmd, int &Flags, str &NewMenuRec )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Insert or replace a specified menu entry in MenuPage in the db file
						FName.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( NewMenuRec != "" ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;
		int TInsert_Mode = Insert_Mode;
		int NewWCmd;
		int NewLevel;
		int DupRecord = False;
		int DoUpdate = ( DbFlags & ( _dbf_ReplaceRec | _dbf_Update ) );
		int ReplaceRec = ( DoUpdate == _dbf_ReplaceRec );
    int Jx;

		str NewMenu;
    str TStr;

		Refresh = False;
		Insert_Mode = True;
		DbRec2Menu( NewMenu, NewLevel, NewWCmd, NewMenuRec );
		if ( NewWCmd == _db_mnu_SepWCmd ) {
			NewMenu = "";
			Flags &= 0xFFFF ^ _dbf_NoDups;
		}
		Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
    if ( ( Flags & _dbf_NoDups ) && !ReplaceRec ) {
//    if ( Flags & _dbf_NoDups ) {

			int TLevel = Level;
			int TWCmd = WCmd;
			int TFlags = Flags;

      str TMenuStr[ 530 ];

			Struct_To_Str( TMenuStr, MenuStr );  // Save Menus

			Level = NewLevel;
			WCmd = 0;
			if ( NewMenu != "" ) {
				switch ( Level ) {
					case 1 :
						MenuStr.Menu1 = NewMenu;
						break;

					case 2 :
						MenuStr.Menu2 = NewMenu;
						break;

					case 3 :
						MenuStr.Menu3 = NewMenu;
						break;

					case 4 :
						MenuStr.Menu4 = NewMenu;
						break;
				}
			}
			Flags &= 0xFFFF ^ _sf_Contains;
			Result = DbGetMenu( FName, MenuPage, MenuStr, Level, WCmd, Flags );
			switch ( Result ) {
				case _NoError :
					DupRecord = True;
					Result = _ErrorDupRecord;
					if ( DbFlags & _dbf_Update ) {
						break;
					}

				case _ErrorNoFile :
				case _ErrorNoDbPage :
					goto Exit;
			}
			FName = "";
			Str_To_Struct( MenuStr, TMenuStr );
			Level = TLevel;
			WCmd = TWCmd;
			Flags = TFlags;
		}
		Flags &= 0xFFFF ^ ( _dbf_NoDups | _sf_Contains );
		Result = DbGetMenu( FName, MenuPage, MenuStr, Level, WCmd, Flags );
		switch ( Result ) {
			case _NoError :
				Result = _ErrorNoDbRecord;
				if ( DbFlags & _dbf_ReplaceRec ) {
					if ( ( WCmd == 0 ) && ( NewWCmd != 0 ) ) {
						break;
					}
					if ( ( NewLevel == 0 ) || ( NewLevel == Level ) ) {
						Result = _NoError;
						NewMenuRec = DbMenu2Rec( NewMenu, Level, NewWCmd );
					}
				}
				else {
					if ( WCmd == 0 ) {
						if ( NewLevel == 0 ) {
							NewLevel = Level + 1;
						}
						if ( NewLevel > ( Level + 1 ) ) {
							break;
						}
						if ( DbFlags & _dbf_InsertBefore ) {
							Up;
						}
						else {
							Goto_Line( C_Line + Flags );
						}
						++Flags;
					}
					else {
						if ( NewLevel == 0 ) {
							NewLevel = Level;
						}
						if ( NewLevel > Level ) {
							break;
						}
						if ( DbFlags & _dbf_InsertBefore ) {
							Up;
						}
						Flags = 0;
					}
					Eol;
					Cr;
					Result = _NoError;
					NewMenuRec = DbMenu2Rec( NewMenu, NewLevel, NewWCmd );
				}
				break;

			case _ErrorNoDbRecord :
				if ( ReplaceRec || DupRecord ) {
					if ( DupRecord ) {
						Result = _ErrorDupRecord;
					}
					break;
				}
				if ( DbFlags & _dbf_InsertBefore ) {
					if ( NewLevel > 1 ) {
						if ( !Find_Text( "(^\f)|(" + _db_mnu_Level + Str( NewLevel - 1 ) +
								"/SP=0\x7FWCMD=0$)", 0, _RegExp | _Backward ) ) {
							break;
						}
					}
					else {
						Up;
					}
				}
				else {

					str FndStr = "^\f";

					if ( NewLevel > 1 ) {
						FndStr = "(" + FndStr + ")|(" + _db_mnu_Level +
								Str( NewLevel - 1 ) + "/SP=0\x7FWCMD=0$)";
					}
					if ( Find_Text( FndStr, 0, _RegExp ) == True ) {
						Up;
					}
					else {
						if ( NewLevel > 1 ) {
							break;
						}
						Eof;
						if ( C_Col == 1 ) {
							Up;
						}
					}
				}
				Eol;
				Cr;
				DoUpdate = False;
				Result = _NoError;
				NewMenuRec = DbMenu2Rec( NewMenu, NewLevel, NewWCmd );
				Flags = 0;
				break;
		}
		if ( Result == _NoError ) {
			Put_Line( NewMenuRec );
			if ( DbFlags & _dbf_SaveFile )  {
				Save_File;
			}
		}

Exit:
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Insert_Mode = TInsert_Mode;
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbPutMenu

str DbTBox2Rec( str TBoxName, int BoxShow, int BoxStyle, int UseIcon ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Returns a toolbox record as stored in command set db files.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	str TBoxRec;

	TBoxRec = _db_Delimit + _db_tbx_BoxId + "=" + TBoxName +
			_db_Delimit + _db_tbx_BoxShow + "=" + Str( BoxShow ) +
			_db_Delimit + _db_tbx_BoxStyle + "=" + Str( BoxStyle ) +
			_db_Delimit + _db_tbx_UseIcon + "=" + Str( UseIcon );

	return ( TBoxRec );

}  // DbTBox2Rec

void DbRec2TBox( str &TBoxName, int &BoxShow, int &BoxStyle, int &UseIcon,
				str TBoxRec )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Fills the passed variables with data from a ToolBox db record.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	TBoxName = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", TBoxRec );
	BoxShow = Parse_Int( _db_Delimit + _db_tbx_BoxShow + "=", TBoxRec );
	BoxStyle = Parse_Int( _db_Delimit + _db_tbx_BoxStyle + "=", TBoxRec );
	UseIcon = Parse_Int( _db_Delimit + _db_tbx_UseIcon + "=", TBoxRec );

}  // DbRec2TBox

int DbGetToolBox( str FName[ 128 ], str &TBoxName, int &Flags ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Find and return a toolbox record from the BOXLIST.DB page in the db
						file FName which matches TBoxName using search Flags.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int DbFlags = Flags;
	int OrgWin = Window_Id;
	int TRefresh = Refresh;
	int Result;
	int RecNo = 0;

	Refresh = False;
	Flags |= _sf_FirstField | _dbf_NoDelWin | _dbf_NoSwitchWin;
	Result = DbGetRecord( FName, _db_tbx_TBPage, _db_tbx_BoxId, TBoxName, RecNo,
			Flags, "" );
	if ( Result == _NoError ) {

		str FndTBox = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", TBoxName );

		Mark_Pos;
		RecNo = 0;
		DbGetPage( "", FndTBox + _db_tbx_PageExt, RecNo, Flags );
		Goto_Mark;
	}
	DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
	Refresh = TRefresh;
	return ( Result );

}  // DbGetToolBox

int DbDelToolBox( str FName[ 128 ], str &TBoxName, int &Flags ) {
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Delete a toolbox record matching TBoxName using search Flags from
						the BOXLIST.DB page in the db file FName and also delete all
						associated buttons. Returns the deleted toolbox record in TBoxName
						and the number of deleted buttons in Flags.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( TBoxName != "" ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;
		int RecNo = 0;

		Refresh = False;
		Flags |= _sf_FirstField | _dbf_NoDelWin | _dbf_NoSwitchWin;
		Result = DbGetRecord( FName, _db_tbx_TBPage, _db_tbx_BoxId, TBoxName, RecNo,
				Flags, "" );
		if ( Result == _NoError ) {

			str FndTBox = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", TBoxName );

			Del_Line;
			Mark_Pos;
			RecNo = 0;
			if ( DbGetPage( "", FndTBox + _db_tbx_PageExt, RecNo, Flags ) ==
					_NoError ) {
				Block_Begin;
				Goto_Line( C_Line + Flags );
				Delete_Block;
			}
			Goto_Mark;
			if ( DbFlags & _dbf_SaveFile ) {
				Save_File;
			}
		}
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbDelToolBox

int DbPutToolBox( str FName[ 128 ], str &TBoxName, int &Flags, str NewTBRec,
				str Misc )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Insert a new toolbox or update a specified toolbox matching
						TBoxName, using search Flags, from the BOXLIST.DB page in the db
						file FName with NewTBRec. Returns the found and/or updated toolbox
						record in TBoxName and the number of buttons in Flags.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( NewTBRec != "" ) {

		str NewTBox = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", NewTBRec );

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;
		int TInsert_Mode = Insert_Mode;
		int DupRecord = False;
		int DoUpdate = ( DbFlags & ( _dbf_ReplaceRec | _dbf_Update ) );
		int ReplaceRec = ( DoUpdate == _dbf_ReplaceRec );
		int Junk;

		Refresh = False;
		Insert_Mode = True;
		Flags |= _dbf_NoDelWin | _dbf_NoSwitchWin;
    if ( ( DbFlags & _dbf_NoDups ) && !ReplaceRec ) {
//    if ( DbFlags & _dbf_NoDups ) {

			int TFlags = Flags;

			Flags &= 0xFFFF ^_sf_Contains;
			Result = DbGetToolBox( FName, NewTBox, Flags );
			switch ( Result ) {
				case _NoError :
					DupRecord = True;
					Result = _ErrorDupRecord;
					if ( DbFlags & _dbf_Update ) {
						break;
					}
					TBoxName = NewTBox;

				case _ErrorNoFile :
				case _ErrorNoDbPage :
					goto Exit;
			}
			FName = "";
			Flags = TFlags;
		}
		if ( TBoxName == "" ) {
			if ( ReplaceRec ) {
				goto Exit;
			}
			if ( !( DbFlags & _sf_ErrorNoRec ) ) {
				Flags |= _sf_Contains;
			}
		}
		Flags &= 0xFFFF ^ _dbf_NoDups;
		Result = DbGetToolBox( FName, TBoxName, Flags );
		switch ( Result ) {
			case _NoError :
				if ( !DoUpdate ) {
					if ( DbFlags & _dbf_InsertBefore ) {
						Up;
					}
					Eol;
					Cr;
					Flags = 0;
				}
				break;

			case _ErrorNoDbRecord :
				if ( ReplaceRec || DupRecord ) {
					if ( DupRecord ) {
						Result = _ErrorDupRecord;
					}
					break;
				}
				DoUpdate = False;
				if ( DbFlags & _dbf_InsertBefore ) {
					Up;
				}
				else {
					if ( Find_Text( "^\f", 0, _RegExp ) == True ) {
						Up;
					}
					else {
						Eof;
						if ( C_Col == 1 ) {
							Up;
						}
					}
				}
				Eol;
				Cr;
				Flags = 0;
				Result = _NoError;
				break;
		}
		if ( Result == _NoError ) {

			str OldTBox = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", TBoxName ) +
							_db_tbx_PageExt;

			if ( DoUpdate ) {

				str Fields = Parse_Str( "/F=", Misc );
				str TStr;

				if ( XPos( "/X", Misc, 1 ) ) {
					DbUpdateFields( NewTBRec, TBoxName, Fields );
				}
				else {
					TStr = TBoxName;
					DbUpdateFields( TStr, NewTBRec, Fields );
					NewTBRec = TStr;
				}
			}
			Put_Line( NewTBRec );
			Mark_Pos;
			NewTBox = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", NewTBRec ) +
					_db_tbx_PageExt;
			Junk = 0;
			if ( ( DbGetPage( "", OldTBox, Junk, Flags ) != _NoError ) || !DoUpdate ) {
				Eof;
				if ( C_Col != 1 ) {
					Cr;
				}
				Flags = 0;
			}
			Put_Line( "\f" + NewTBox );
			Goto_Mark;
			if ( DbFlags & _dbf_SaveFile )  {
				Save_File;
			}
		}

Exit:
		DbToolsExit( Result, DbFlags, OrgWin );  // Del and switch windows if needed
		Insert_Mode = TInsert_Mode;
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbPutToolBox

int DbGetTBButton( str FName[ 128 ], str &TBoxName, int &WCmd, int &BtnNo,
				int &Flags )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Find and return a specified toolbox button record from the TBoxName
						toolbox in the db file FName.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( TBoxName != "" ) {

		int TRefresh = Refresh;

		str WCmdData = "";

		Refresh = False;
		if ( XPos( _db_Delimit, TBoxName, 1 ) ) {  // See if toolbox record
			TBoxName = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", TBoxName );
		}
		if ( Get_Extension( TBoxName ) == "" ) {
			TBoxName += _db_tbx_PageExt;      // Add page extension
		}
		Flags |= _sf_Contains | _sf_FirstField;
		if ( WCmd != 0 ) {
			WCmdData = Str( WCmd );
			Flags &= 0xFFFF ^ ( _sf_Contains | _sf_RegExp );  // Force exact match
		}
		Result = DbGetRecord( FName, TBoxName, _db_tbb_WCmd, WCmdData, BtnNo, Flags,
				"" );
		if ( Result == _NoError ) {
			WCmd = Parse_Int( _db_Delimit + _db_tbb_WCmd + "=", WCmdData );
		}
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbGetTBButton

int DbDelTBButton( str FName[ 128 ], str &TBoxName, int &WCmd, int &BtnNo,
				int &Flags )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Delete a specified toolbox button record from the TBoxName toolbox
						in the db file FName.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( TBoxName != "" ) {

		int DbFlags = Flags;
		int OrgWin = Window_Id;
		int TRefresh = Refresh;

		str WCmdData = "";

		Refresh = False;
		if ( XPos( _db_Delimit, TBoxName, 1 ) ) {  // See if toolbox record
			TBoxName = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", TBoxName );
		}
		if ( Get_Extension( TBoxName ) == "" ) {
			TBoxName += _db_tbx_PageExt;    // Add page extension
		}
		Flags |= _sf_FirstField;
		if ( WCmd != 0 ) {
			WCmdData = Str( WCmd );
			Flags &= 0xFFFF ^ ( _sf_Contains | _sf_RegExp );  // Force exact match
		}
		Result = DbDelRecord( FName, TBoxName, _db_tbb_WCmd, WCmdData, BtnNo, Flags,
				"" );
		if ( Result == _NoError ) {
			WCmd = Parse_Int( _db_Delimit + _db_tbb_WCmd + "=", WCmdData );
			--Flags;
		}
		Refresh = TRefresh;
	}
	return ( Result );

}  // DbDelTBButton

int DbPutTBButton( str FName[ 128 ], str &TBoxName, int &WCmd, int &BtnNo,
				int &Flags, int NewWCmd )
{
/******************************************************************************
															 Multi-Edit Macro
															 14-Dec-94  15:27

	Function: Insert or replace a specified toolbox button record in the toolbox
						TBoxName in the db file FName.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	int Result = _ErrorNoDbRecord;

	if ( NewWCmd != 0 ) {
		if ( TBoxName != "" ) {

			int TRefresh = Refresh;

			str WCmdData = "";
			str NewWCmdData = _db_Delimit + _db_tbb_WCmd + "=" + Str( NewWCmd );

			Refresh = False;
			if ( XPos( _db_Delimit, TBoxName, 1 ) ) {  // See if toolbox record
				TBoxName = Parse_Str( _db_Delimit + _db_tbx_BoxId + "=", TBoxName );
			}
			if ( Get_Extension( TBoxName ) == "" ) {
				TBoxName += _db_tbx_PageExt;    // Add page extension
			}
			Flags |= _sf_FirstField;
			if ( WCmd != 0 ) {
				WCmdData = Str( WCmd );
				Flags &= 0xFFFF ^ ( _sf_Contains | _sf_RegExp );  // Force exact match
			}
			if ( NewWCmd == _db_mnu_SepWCmd ) {
				Flags &= 0xFFFF ^ _dbf_NoDups;
			}
			Result = DbPutRecord( FName, TBoxName, _db_tbb_WCmd, WCmdData, BtnNo,
					Flags, "", NewWCmdData );
			if ( Result == _NoError ) {
				WCmd = Parse_Int( _db_Delimit + _db_tbb_WCmd + "=", WCmdData );
			}
			Refresh = TRefresh;
		}
	}
	return ( Result );

}  // DbPutTBButton

void DbUpdateFields( str &NewRecord, str UpdData[ Max_Line_Length ], str Fields ) {
/******************************************************************************
															 Multi-Edit Macro
															 01-Feb-95  12:30

	Function: Replace all field data in NewRecord with the data from
						UpdData for the field names specified in Fields.

							 Copyright (C) 1995 by American Cybernectics, Inc.
********************************************************************( ldh )***/

	if ( Fields != "" ) {

		int FPos1 = 1;
		int FPos2;
		int Done = False;
		int Jx;

		str TStr;
		str Field;

		while ( !Done ) {
			FPos2 = XPos( " ", Fields, FPos1 );
			if ( FPos2 == 0 ) {
				FPos2 = Svl( Fields ) + 1;
				Done = True;
			}
			Field = Copy( Fields, FPos1, FPos2 - 1 );
			FPos1 = FPos2 + 1;
			if ( Field != "" ) {
				Jx = XPos( Field, NewRecord, 1 );
				if ( Jx ) {
					Var_Remove_Str( Field, NewRecord );
				}
				else {
					Jx = Svl( NewRecord ) + 1;
				}
				TStr = Parse_Str( Field, UpdData );
				if ( TStr != "" ) {
					NewRecord = Str_Ins( Field + TStr, NewRecord, Jx );
				}
			}
		}
	}
	else {
		NewRecord = UpdData;
	}
}  // DbUpdateFields
