//+++keyword+++ "@(#)%v %n, %f, %w"
// ""

#ifdef _Debug_
	#include DBUG.SH
#endif

//#include KEYS.SH
#include LANGUAGE.SH
#include TEMPLATE.SH
#include DBTOOLS.SH
#include DIALOG.SH
#include WINDOWS.SH

macro_file PERL;
/* ******************************************************************************
														MULTI-EDIT MACRO FILE

Name:   PERL

Description:  This macro file contains language support macros for PERL, based
              on "C.S".

Per_ind        - Smart indent for Perl
PerMtch        - Construct/brace matching for Perl
BUILDCMT       - Builds a shell of a comment block for Perl
PerlSetX       - Sets up the Perl specific global variable for template expansion
Perl_CLOSE_PAREN - Highlights to matching open paren when closing paren is entered.
Perl_CLOSE_BRACE - Sets the indenting correctly when a closing brace is entered.
Perl_COMMENT     - Handles formatting of EOL comments while entering.

This perl support is based off of American Cybernetics' C.S macro.  Modifications
 were made by Bill Glicker, and thanks go out to him for providing this PERL
 support for all Multi-Edit users.  Bill also provided PERL.TPT (Perl template file)
****************************************************************************** */

#define _c_prop_IndentStyle		"/IS="
#define _c_prop_Options				"/O="
#define _c_prop_EolCol				"/EC="

#define _c_opt_ParenHi				0x0001
#define _c_opt_BraceHi				0x0002
#define _c_opt_BraceAdj				0x0004
#define _c_opt_EolCmtAdj			0x0008
#define _c_opt_AutoCmt				0x0010

#define id_StyleFrame					500
#define id_StyleTxt1					501
#define id_StyleTxt2					502
#define id_StyleTxt3					503
#define id_StyleTxt4					504

#define id_IndentStyle				1002
#define id_EolCol							1003

#define id_ParenHiCB					1100
#define id_BraceHiCB					1101
#define id_BraceAdjCB					1102
#define id_EolCmtAdjCB				1103
#define id_AutoCmtCB					1104

// Comment character structure
structure tCProperties {
	str Language;
	int IndStyle;
	int Options;
	int EolCol;
}

prototype C {
	int C_In_Eol_Cmt( int &Start, int CMac );
	int C_Next_Quote( int CMac );
	int C_Prev_Quote( int CMac );
  int CGetProperties( struct tCProperties rCP );
}

void PerlSetProperties( str GStr = Parse_Str( "/GSTR=", MParm_Str ) ) {
/******************************************************************************
															 Multi-Edit Macro
															 20-Oct-95  14:03

	Function: Set C and CMAC specific properties.  Should only be run by the
						LangSetProperties macro.

	Entry   : str GStr			- Name of global string containing properties.
	Exit    : None

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

	str Properties = Global_Str( GStr );

	int IndStyle = Parse_Int( _c_prop_IndentStyle, Properties );
	int Options	 = Parse_Int( _c_prop_Options, Properties );
	int EolCol	 = Parse_Int( _c_prop_EolCol, Properties );
	int Dlg;
	int X;
	int Y;

	DlgCreate( Dlg );

	DlgAddCtrl( Dlg, dlg_Static, "&Indent Style:",
			1, 1,
			0, 0,
			-1, 0, "" );
	DlgAddCtrl( Dlg, dlg_Choice, "0()1()2()3()",
			dlg_PosOffset + 13, dlg_PosOffset + 0,
			9, 0,
			id_IndentStyle, 0,
      "/CHANGEMAC=C^_CShowIndStyle"
			);
	DlgSetInt( Dlg, id_IndentStyle, IndStyle + 1 );

	DlgAddCtrl( Dlg, dlg_BlackFrame, "",
			dlg_PosOffset + 12, dlg_PosOffset + 0,
			15, 3,
			id_StyleFrame, 0, "" );

	DlgAddCtrl( Dlg, dlg_Static, "&Eol Cmt Col:",
			1, dlg_PosOffset + 1,
			0, 0,
			-1, 0, "" );
	DlgAddCtrl( Dlg, dlg_Integer, "",
			dlg_PosOffset + 13, dlg_PosOffset + 0,
			6, 0,
			id_EolCol, 0, "/MIN=0/MAX=" + Str( Max_Line_Length ) );
	DlgSetInt( Dlg, id_EolCol, EolCol );

	DlgAddCtrl( Dlg, dlg_GroupBox, "Options",
			1, dlg_PosOffset + 2,
			40, 6,
			-1, 0, "" );

	DlgAddCtrl( Dlg, dlg_CheckBox, "&Paren Closure Highlight",
			dlg_PosOffset + 1, dlg_PosOffset + 1,
			0, 0,
			id_ParenHiCB, 0, "" );
	DlgSetInt( Dlg, id_ParenHiCB, ( Options & _c_opt_ParenHi ) != 0 );

	DlgAddCtrl( Dlg, dlg_CheckBox, "&Brace Closure Highlight",
			dlg_PosOffset + 0, dlg_PosOffset + 1,
			0, 0,
			id_BraceHiCB, 0, "" );
	DlgSetInt( Dlg, id_BraceHiCB, ( Options & _c_opt_BraceHi ) != 0 );

	DlgAddCtrl( Dlg, dlg_CheckBox, "&Auto Brace Closure Adjust",
			dlg_PosOffset + 0, dlg_PosOffset + 1,
			0, 0,
			id_BraceAdjCB, 0, "" );
	DlgSetInt( Dlg, id_BraceAdjCB, ( Options & _c_opt_BraceAdj ) != 0 );

	DlgAddCtrl( Dlg, dlg_CheckBox, "A&uto Eol Comment Adjust",
			dlg_PosOffset + 0, dlg_PosOffset + 1,
			0, 0,
			id_EolCmtAdjCB, 0, "" );
	DlgSetInt( Dlg, id_EolCmtAdjCB, ( Options & _c_opt_EolCmtAdj ) != 0 );

	DlgAddCtrl( Dlg, dlg_CheckBox, "I&nsert * on CR in block comment",
			dlg_PosOffset + 0, dlg_PosOffset + 1,
			0, 0,
			id_AutoCmtCB, 0, "" );
	DlgSetInt( Dlg, id_AutoCmtCB, ( Options & _c_opt_AutoCmt ) != 0 );

  DlgAddCtrl( Dlg, dlg_PushButton, "OK",
			1, dlg_PosOffset | dlg_Units | dlg_Units | ( dlg_Units_HLine * 3 ),
			dlg_StanBtnWidth, 0,
			2001, dlgf_DefButton, "/R=1" );

  DlgAddCtrl( Dlg, dlg_PushButton, "Cancel",
			dlg_PosOffset + dlg_StanBtnWidth + 2, dlg_PosOffset + 0,
			dlg_StanBtnWidth, 0,
			2000, 0, "/R=0" );

  DlgAddCtrl( Dlg, dlg_PushButton, "&Help",
			dlg_PosOffset + dlg_StanBtnWidth + 2, dlg_PosOffset + 0,
			dlg_StanBtnWidth, 0,
			2002, 0, "/R=2" );

	DlgGetXY( Dlg, id_StyleFrame, X, Y );
	DlgAddCtrl( Dlg, dlg_Static, "",
			dlg_Units | X + 1, dlg_Units | Y,
			10, 0,
			id_StyleTxt1, 0, "" );
	DlgAddCtrl( Dlg, dlg_Static, "",
			dlg_PosOffset + 0, dlg_PosOffset | dlg_Units | 8,
			10, 0,
			id_StyleTxt2, 0, "" );
	DlgAddCtrl( Dlg, dlg_Static, "",
			dlg_PosOffset + 0, dlg_PosOffset | dlg_Units | 8,
			10, 0,
			id_StyleTxt3, 0, "" );
	DlgAddCtrl( Dlg, dlg_Static, "",
			dlg_PosOffset + 0, dlg_PosOffset | dlg_Units | 8,
			10, 0,
			id_StyleTxt4, 0, "" );

  int Result = DlgExecute( Dlg, id_IndentStyle, "Perl Properties Setup", "",
      "/INITMAC=C^_CShowIndStyle"
			, 0 );
	Return_Int = 0;
	if ( Result ) {
		Options = 0;
		if ( DlgGetInt( Dlg, id_ParenHiCB ) != 0 ) {
			Options |= _c_opt_ParenHi;
		}
		if ( DlgGetInt( Dlg, id_BraceHiCB ) != 0 ) {
			Options |= _c_opt_BraceHi;
		}
		if ( DlgGetInt( Dlg, id_BraceAdjCB ) != 0 ) {
			Options |= _c_opt_BraceAdj;
		}
		if ( DlgGetInt( Dlg, id_EolCmtAdjCB ) != 0 ) {
			Options |= _c_opt_EolCmtAdj;
		}
		if ( DlgGetInt( Dlg, id_AutoCmtCB ) != 0 ) {
			Options |= _c_opt_AutoCmt;
		}
		IndStyle = DlgGetInt( Dlg, id_IndentStyle) - 1;
		Properties = _c_prop_EolCol + Str( DlgGetInt( Dlg, id_EolCol ) ) +
				_c_prop_IndentStyle + Str( IndStyle ) +
				_c_prop_Options + Str( Options );
		Set_Global_Str( GStr, Properties );
	}
	DlgKill( Dlg );

}  // PerlSetProperties

void PerlIndentTmp(
			str Set = Parse_Str( "/S=", MParm_Str ),
			str Name = Parse_Str( "/N=", MParm_Str )
) {
/******************************************************************************
															 Multi-Edit Macro
															 25-Oct-95  12:45

	Function: Insert the indent style template Name from the template set Set.
	Entry   : str Set     - The template set
						str Name		- The template name
	Exit    : None

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

	struct tCProperties rCP;

	CGetProperties( rCP );
	TmpExpand( Set, Name + Str( rCP.IndStyle ) );

}  // PerlIndentTmp

void Per_Ind( ) {
/* ******************************************************************************
																MULTI-EDIT MACRO

Name: Per_IND

Description: Performs a smart indent each time the Carriage Return or Enter
key is pressed.  Is called by the macro CR.

Written by Todd M. Johnson and William M. Miller.

The algorithm used is to look at the line-ending characters (ignoring
comments, including C++ '//' comments, labels, and white space) for the
current and preceding lines, then decide whether and how much to change the
indentation from that of the current line.  In addition, if the CR was done
inside a (C-style, / * * /) comment, the "line of asterisks" style of comment
continuation is automatically supplied.

							 (C) Copyright 1992 by American Cybernetics, Inc.
****************************************************************************** */

	int outdent_flag,
					sig_char_found,
					in_comment,  /* 1 = '/*' comment, 2 = '//' comment */
					curr_ind,
					start_col,
					start_line,
					cur_char_line,
					curr_end_char,
					prev_end_char,
					in_struct,
					mark_count = 0,
					junk,
					match_brace, go_match_brace,
					jx,
					Indent_Number,
					OldRefresh = Refresh;
	int ShowMessage = !Parse_Int( "/NM=", MParm_Str );

	char  orig_char ;

	struct tCProperties rCP;

	// Number of times to indent after a line that does not have ';{}'
	// at the end and flag for outdenting after a closing brace

	if ( Insert_Mode == False ) {
	/* If a new line is not being created, just go to the indent level that was
	 there before and get out. 10-03-90 TR */
		Cr;
		First_Word;
		goto Mac_Exit;
	}
	CGetProperties( rCP );
//	switch ( c_style )
	switch ( rCP.IndStyle ) {
		case 0 :
			Indent_Number = 1;
			Outdent_flag = False;
			break;

		case 1 :
		case 2 :
			Indent_Number = 0;
			Outdent_flag = False;
			break;

		case 3 :
			Indent_Number = 1;
			Outdent_flag = True;
			break;
	}

	/* If you want your end brace to line up with the first word of the
	 line that the opening brace is on, then set Match_Brace to TRUE */
	Match_Brace = rCP.Options & _c_opt_BraceAdj;
	Go_Match_Brace = FALSE;

	Messages = False;
	reg_exp_stat = true;
	refresh = false;
	{mark_pos; ++mark_count;}

	/*  If we are before or at the first character of the first word of the line
		then leave the level the same for the line  */
	Start_Col = C_Col;
	Start_Line = C_Line;
	First_Word;
	if ( ( C_Col >= Start_Col ) && !At_Eol ) {
		Set_Indent_Level;
		Cr;
		goto Mac_Exit;
	}
	orig_char = cur_char;
	/* If we are at a directive command (#include or #define), get out */
	if(  (orig_char == '#')  ) {
		{mark_pos; ++mark_count;}
		first_word;
		if( cur_char == "#") {
			{pop_mark; --mark_count;}
			set_indent_Level;
			goto_col(start_col);
			cr;
			goto mac_exit;
		}
		{goto_mark; --mark_count;}
	}
	goto_col(start_col);

	call Skip_C_Noise;

	// If the cursor is inside a comment, skip_c_noise leaves it at the opening
	// '/'.  We move one character right, to align with the '*', set the indent
	// level, do the carriage return, and insert a '* ' to form the line of
	// asterisks continuation.
	if ( In_Comment == 1 ) {
		Right;
		Set_Indent_Level;
		Right;
		while ( !At_Eol && ( XPos( Cur_Char, " \t\xFF", 1 ) != 0 ) ) {
			Right;
		}
		Start_Col = C_Col;
		{goto_mark; --mark_count;}
		Cr;

		if ( rCP.Options & _c_opt_AutoCmt ) {
			Text( "* " );
		}
		Set_Indent_Level;
		goto Mac_Exit;
	}

	/*  Here, we are at the top of the file, before any non-comment text.  We just
	 put everything in column 1 in that case.  */

	if(  (not(sig_char_found))  ) {
		goto_col(1);
		set_indent_level;
		{goto_mark; --mark_count;}
		cr;
		goto mac_exit;
	}

/*  Here we've found the nearest non-noise character.  There are four that
	specifically influence indentation decisions, so instead of carrying around
	a big string variable, we just save the index into a string of those four
	characters.  In addition, we find the first non-blank character on the line
	to determine its indentation.  Furthermore, we check if that first character
	is a '}' (probably as a result of the '} name;' at the end of a structure,
	union, or enum definition) so we can know how much to indent later.  */

	cur_char_line = c_line;
	if ( 1 == ( Curr_End_Char = Pos( Cur_Char, ";{}" ) ) ) {
//		if ( Search_Bwd('@{', 1) ) {
		if ( Find_Text( '{', 1, _Backward ) ) {
			++Curr_End_Char;
		}
	}
	jx = c_col;            /*  store the current column position  */
	first_word;
	Set_Indent_Level;
	// Special processing if we have a lone '{' on a line
	if ( ( C_Col == Jx ) && ( Curr_End_Char == 2 ) ) {
		if ( 3 == rCP.IndStyle ) {
			Curr_Ind = 0;
		}
		else {
			Curr_Ind = 1;
		}
		goto do_curs;
	}
	// Special processing when using indent style 2 and the first char is a '{'
	if ( ( Cur_Char == '{' ) && ( rCP.IndStyle == 2 ) ) {
		Curr_Ind = 1;
		goto Do_Curs;
	}
	Curr_Ind = 0;
	Start_Col = C_Col;
	In_Struct = ( Cur_Char == '}' );

	//  Now we get the line ending for the preceding line.
	call skip_c_noisex;  /* no need to check to see if we're in a comment */
	if ( Sig_Char_Found ) {
		Prev_End_Char = Pos( Cur_Char, ";{}" );
	}
	else {
		Prev_End_Char = 1;                  // treat like semicolon
	}
	// This code is executed when the nearest line-ending char was a semicolon.
	if ( Curr_End_Char == 1 ) {           // semicolon
		if ( Prev_End_Char == 0 ) {         // if continuation line then undent
			Curr_Ind -= Indent_Number;
		}
		if ( Outdent_Flag & In_Struct ) {
			--Curr_Ind;
		}
	}
	else {
		// This code is executed if the nearest line-ending char was a left brace.
		if ( Curr_End_Char == 2 ) {         // left brace
			++Curr_Ind;
			if ( Prev_End_Char == 0 ) {
				Curr_Ind -= Indent_Number;      // if continuation line then undent
			}
		}
		else {
			if ( Curr_End_Char == 3 ) {       // right brace
				if ( Outdent_Flag ) {
					--Curr_Ind;
				}
				else if ( Match_Brace ) {
					if ( Cur_Char_Line == Start_Line ) {
						Go_Match_Brace = TRUE;
					}
				}
			}
			else {

/*  Commas cause no indentation (to support enum declarations).  If the line-
	ending character was none of the above, the new line will be a continuation
	line, indented Indent_Number times.  However, if the preceding line-ending
	character was not ';{}', we assume that the current line was already a
	continuation line and no further indentation is necessary.  */

				if ( ( Curr_End_Char != 4 ) && ( Prev_End_Char != 0 ) ) {
					Curr_Ind += Indent_Number;
				}

			}
		}
	}

do_curs:
/*  Here we put the cursor at the correct column position, we set the indent
	level, return to the original position, and perform the carriage return.  */

	jx = 0;

	{goto_mark; --mark_count;}
	cr;
	if(  curr_ind > 0  ) {
		while(  jx < curr_ind  ) {
			++jx;
			indent;
		}
	} else {
		while(  jx > curr_ind  ) {
			--jx;
			undent;
		}
	}

	set_indent_level;

	if(  (go_match_brace) & (orig_char == '}')  ) {
		UP;
		First_Word;
		{mark_pos; ++mark_count;}
    Rm( "Perl^PerMtch " + MParm_Str );
		FIRST_WORD;
		set_indent_level;
		{goto_mark; --mark_count;}
		first_word;
		while(  (c_col > indent_level)  ) {
			back_space;
		}
		down;
		if ( ShowMessage ) {
			Make_message( "} undenting occurred." );
		}
	}
	goto Mac_Exit;


Skip_C_NoiseX:
	if ( ( C_Line == 1 ) && ( C_Col == 1 ) ) {
		Sig_Char_Found = FALSE;
		goto Exit_Skip_C;
	}
	Left;
	goto Skip_C_Noise1;

	/* This subroutine moves the cursor to the nearest preceding non-whitespace
	   character.  If there is no such character (the cursor is at the top of the
	   file), the variable sig_char_found will be FALSE; otherwise, it will be
	   true.  The macro also sets the variable in_comment appropriately.  */

Skip_C_Noise:
	Left;
	{mark_pos; ++mark_count;}

	// First check if we are inside a comment.  We look for the nearest of '/*'
	// or '*/'; if we find the former, we are inside a comment.  In this case, we
	// leave the cursor at the beginning of the comment to establish the correct
	// indentation margin and return.
	Jx = C_Line;

	// If we are using syntax highlighting, then is is much easier to know.
	if ( Keywords_Str != "" ) {

		int ic = 0, Tx = C_Col;

		if ( At_Eol || ( C_Line == 1 ) ) {
			ic = ( Line_Stat & 0x30 ) != 0;
		}
		else if ( C_Line > 1 ) {
			Up;
			ic = ( Line_Stat & 0x30 ) != 0;
			Down;
			First_Word;
			while ( !At_Eol && ( C_Col < Tx ) ) {
				if ( ic == 0 ) {
					Forward_Till( "/\"" );
					if ( C_Col < Tx ) {
						if ( Cur_Char == "\"" ) {
//Quote_Again:
//							Right;
//							Forward_Till( "\\\"" );
//							if ( ( Cur_Char == "\\" ) && !At_Eol ) {
//								Right;
//								goto Quote_Again;
//							}
							while ( True ) {
								Right;
								Forward_Till( "\\\"" );
								if ( ( Cur_Char == "\\" ) && !At_Eol ) {
									Right;
									continue;
								}
								break;
							}
						}
						else if ( Cur_Char == "/" ) {
							Right;
							if ( Cur_Char == "*" ) {
								ic = 1;
							}
						}
					}
				}
				else {
					Forward_Till( "*" );
					if ( C_Col < Tx ) {
						if ( Cur_Char == "*" ) {
							Right;
							if ( Cur_Char == "/" ) {
								ic = 0;
							}
						}
					}
				}
				Right;
			}
			Goto_Col( Tx );
		}
		if ( !ic ) {
			goto Comment_Done;
		}
	}


Comment_Again:
	if ( Find_Text( "/", 50, _Backward ) ) {
		if ( C_Col > 1 ) {
			Left;
			if ( Cur_Char == "/" ) {
				In_Comment = FALSE;
				if ( C_Line == Jx  ) {
					Left;
					{pop_mark; --mark_count;}
				}
				else {
					{goto_mark; --mark_count;}
				}
				goto Skip_C_Noise1;
			}
			Right;
		}
		Right;
		if ( Cur_Char == "/" ) {
			In_Comment = FALSE;
			if ( C_Line == Jx ) {
				Left;
				Left;
				{pop_mark; --mark_count;}
			}
			else {
				{goto_mark; --mark_count;}
			}
			goto Skip_C_Noise1;
		}
		if ( Cur_Char == "*" ) {
			{pop_mark; --mark_count;}
			In_Comment = 1;
			Left;
			goto Exit_Skip_C;
		}
		Left;
		Left;
		if ( Cur_Char != "*" ) {
			goto Comment_Again;
		}
	}

Comment_Done:
	{goto_mark; --mark_count;}
	In_Comment = FALSE;

	/*  The following code is a loop which skips over comments (including C++ '//'
		comments) and labels, leaving the cursor on the nearest preceding non-noise
		character, if any.  */

Skip_C_Noise1:
	if(  (Xpos('#',get_Line,1) > 0)  ) {
		{mark_pos; ++mark_count;}
		first_word;
		if(cur_char == "#" ) {
			{pop_mark; --mark_count;}
			// If at a compiler directive, get out
			sig_char_found = FALSE;
			goto exit_skip_c;
		}
		{goto_mark; --mark_count;}
	}
	if(  (c_line == 1) & (c_col == 1)  ) {
		sig_char_found = FALSE;
		goto exit_skip_c;
	}
	/* If there is a '//' on the line, we move the cursor before it and
	 * look some more.  */

	if(  (search_bwd('//', 1))  ) {
		if(  ((c_col == 1) & (c_line == 1))  ) {
			sig_char_found = FALSE;
			goto exit_skip_c;
		}
		left;
		goto skip_c_noise1;
	}



/*  Here we look for the nearest preceding nonblank character.  If it is a '/',
	we check if the preceding character was an asterisk; if so, we search for
	the corresponding '/*' and try again.  If the character before the '/' was
	not '*', we have found a significant character and are done.  */

	if(  (search_bwd('[~|9 ]', 1))  ) {
		if(  (cur_char == '/')  ) {
			left;
			if(  (cur_char == '*')  ) {
				junk = search_bwd('/@*', 0);
				left;
				goto skip_c_noise1;
			}
			right;
			sig_char_found = TRUE;
			goto exit_skip_c;
		}

/*  If the character we found was a colon, we have found a label.  We assume
	that the label was the first thing on the line and proceed to the end of
	the preceding line.  Any other character is significant.  */

		if(  (cur_char == ':')  ) {
			if(  (C_Line == 1)  ) {
				Eol;
				cr;
				Goto_Col(1);
				Indent;
				Set_Indent_Level;
				goto mac_exit;
			}
			up;
			eol;
			goto skip_c_noise1;
		}
		sig_char_found = TRUE;
		goto exit_skip_c;
	}

/*  If we failed to find a nonblank character on the current line, and the
	cursor is on line 1, we failed to find a significant character; otherwise,
	we back up a line and try again.  */

	if(  (c_line == 1)  ) {
		sig_char_found = FALSE;
		goto exit_skip_c;
	}
	up;
	eol;
	goto Skip_C_Noise1;

Exit_Skip_C:
	ret;


Mac_Exit:
	while ( mark_count > 0 )
	{
		pop_mark;
		--mark_count;
	}
	Refresh = OldRefresh;
	Messages = True;
}  // Per_Ind

void PerMtch( ) trans
// ****************************************************************************
//                                MULTI-EDIT MACRO
//
// Name: PerMtch
//
// Description: Match occurances of /*  */ or () and handles problems with
//              characters embedded in comments or quotes.
//   11/01/88 11:15am  Modified for greater speed.  Also, handles screen
//                     updates much better.
//
//   /HI=1    Highlight block
//   /RC=1    Restore cursor to original position
//   /LS=x    Limit scope of search to x number of lines.
//
//               (C) Copyright 1992 by American Cybernetics, Inc.
// ***************************************************************************/
{
	str str1[20],
			str2[20],
			search_str;                       // Strings to match

	int direction,                        // 1 = Search forward, 0 = backward
			b_count,                          // Brace count.  0 = match found
			s_res,                            // Result of last search
			t_row,
			t_col = c_col,                    // Store the current position
			t_line = c_line,
			jx,                               // General purpos
			f_line,
			f_col,                            // Found position

#ifndef Windows
			shift_stat = peek(0, 0x417),
#endif
			scope = parse_int("/LS=", mparm_str),
			cmac = parse_str("LS=", global_str("."
																+ get_extension(file_name))) == "CMAC",
			srf = refresh,
			search_flag = _RegExp,
			sre = reg_exp_stat,
			sic = ignore_case,
			in_stat = 0,
			start = 0
			;
	int ShowMessage = !Parse_Int( "/NM=", MParm_Str );
	int EnableAbort = !Parse_Int( "/NA=", MParm_Str );

	Refresh = False;                      // Turn screen display off

	Push_Undo;
	Mark_Pos;

 	if ( !XPos( Cur_Char, "(){}[]/*", 1 ) ) {	// If not match char, try to find one
		Goto_Col( 1 );
		Forward_Till( "{}'\"" );
		while ( XPos( Cur_Char, "'\"", 1 ) ) {
			if ( !C_Next_Quote( CMac ) ) {
				break;
			}
			Right;
			Forward_Till( "{}'\"" );
		}
		if ( At_Eol ) {
			Goto_Col( 1 );
			Forward_Till( "()'\"" );
			while ( XPos( Cur_Char, "'\"", 1 ) ) {
				if ( !C_Next_Quote( CMac ) ) {
					break;
				}
				Right;
				Forward_Till( "()'\"" );
			}
		}
		if ( At_Eol ) {
			Goto_Col( 1 );
			Forward_Till( "[]'\"" );
			while ( XPos( Cur_Char, "'\"", 1 ) ) {
				if ( !C_Next_Quote( CMac ) ) {
					break;
				}
				Right;
				Forward_Till( "[]'\"" );
			}
		}
	}
	S_Res = False;
 	if ( XPos( Cur_Char, "(){}[]/*", 1 ) ) {
		S_Res = True;
		Pop_Mark;
		Mark_Pos;
		if ( Cur_Char == "(" ) {						// If current char is a '(' then setup
			Str1 = "(";
			Str2 = ")";
			Direction = 1;
		}
		else if ( CUR_CHAR == ")" ) {				// If current char is a ')' then setup
			Str1 = ")";
			Str2 = "(";
			Direction = 0;
		}
		else if ( Cur_Char == "{" ) {				// If current char is a '{' then setup
			Str1 = "{";
			Str2 = "}";
			Direction = 1;
		}
		else if ( Cur_Char == "}" ) {				// If current char is a "}" then setup
			Str1 = "}";
			Str2 = "{";
			Direction = 0;
		}
   	else if ( Cur_Char == "[" ) {				// If current char is a "[" then setup
     	Str1 = "[";
     	Str2 = "]";
 			Direction = 1;
 		}
   	else if ( Cur_Char == "]" ) {				// If current char is a "]" then setup
     	Str1 = "]";
     	Str2 = "[";
 			Direction = 0;
 		}
		else if ( Cur_Char == "/" ) {
			Right;
			if ( Cur_Char == "*" ) {
				Str1 = "/*";
				Str2 = "*/";
				Direction = 1;
			}
			else {
				Left;
				Left;
				if ( Cur_Char == "*" ) {
					Str1 = "*/";
					Str2 = "/*";
					Direction = 0;
				}
				else {
					Right;
					S_Res = False;
				}
			}
		}
		else if ( Cur_Char == "*" ) {
			Right;
			if ( Cur_Char == "/" ) {
				Str1 = "*/";
				Str2 = "/*";
				Direction = 0;
				Left;
			}
			else {
				Left;
				Left;
				if ( Cur_Char == "/" ) {
					Str1 = "/*";
					Str2 = "*/";
					Direction = 1;
					T_Col = C_Col;
					Pop_Mark;
					Mark_Pos;
				}
				else {
					Right;
					S_Res = False;
				}
			}
		}
	}
	if ( S_Res == False ) {								// If nothing matched then
		Goto_Line( T_Line );								// Return to original position
		Goto_Col( T_Col );
		Goto_Mark;
		if ( ShowMessage ) {
			Make_Message( "Nothing to Match" );
			Return_Int = False;
		}
		goto Macro_Exit;										// Go exit
	}
	Search_Str = "(" + Make_Literal_X( Str1 ) + ")|(" + Make_Literal_X( Str2 ) +
			")|[\"'/]";

	B_Count = 1;													/* Brace count starts at 1 */
	S_Res = True;													// Init search result to true
																				/* Tell the user what we're matching */
	if ( ShowMessage ) {
		Make_Message( "matching \"" + Str1 + "\"..." );
	}

Match_Loop:     /* Main loop */

	if ( S_Res == 0 ) {           /* If the last search was a failure then exit */
		rm("meError^beeps /C=1");
		goto Error_Exit;
	}

	if ( B_Count == 0 ) {         /* If brace count is zero then success */
		goto Found_Exit;
	}

	// Execute search based on direction
	/* We are going to search not only for the match brace or paren, but
		 also for comments and double and single quotes */
	if ( Direction == 1 ) {
		if ( Scope > 0 ) {
			if ( C_Line > ( T_Line + Scope ) ) {
				goto Error_Exit;
			}
		}
		Right;
		while ( !At_Eol && XPos( Cur_Char, "\t\xFF", 1 ) ) {
			Right;
		}
	}
	else {
		if ( Scope > 0 ) {
			if ( C_Line < ( T_Line - Scope ) ) {
				goto Error_Exit;
			}
		}
		Left;
		while ( ( ( C_Line > 1 ) || ( C_Col > 1 ) ) && XPos( Cur_Char, "\t\xFF", 1 ) ) {
			Left;
		}
		Search_Flag |= _Backward;
	}
	S_Res = Find_Text( Search_Str, Scope, Search_Flag );
	// If the search result was a failure then exit
	if ( S_Res == 0 ) {
		goto Error_Exit;
	}
	if ( EnableAbort ) {
		// Allow aborting a long search
		if ( Check_Key ) {
			if ( ( Key1 == 27 ) && ( Key2 == 1 ) ) {
				goto Error_Exit;
			}
		}
	}
	// If we found the original string then up the count
	if ( Found_Str == Str1 ) {
		if ( ( C_In_Eol_Cmt( Start, CMac ) == 4 ) && !Direction ) {
			Goto_Col( Start );
		}
		else {
			++B_Count;
		}
		goto Match_Loop;
	}
	// If we found the matching string then decrement the count
	if ( Found_Str == Str2 ) {
		if ( ( C_In_Eol_Cmt( Start, CMac ) == 4 ) && !Direction ) {
			Goto_Col( Start );
		}
		else {
			--B_Count;
		}
		goto Match_Loop;
	}

	// If we found a single quote then match it
	if ( Found_Str == "'" ) {
		if ( Direction == 1 ) {
			C_Next_Quote( CMac );
		}
		else {
			C_Prev_Quote( CMac );
		}
		goto Match_Loop;
	}

	// If we found a double quote then match it
	if ( Found_Str == "\"" ) {
		if ( Direction == 1 ) {
			C_Next_Quote( 0 );
		}
		else {
			C_Prev_Quote( 0 );
		}
		goto Match_Loop;
	}

	// If we found a opening comment then match it
	if ( Found_Str == "/" ) {
		if ( Direction == 1 ) {
			Right;
			if ( Cur_Char == "*" ) {
				S_Res = Find_Text( "*/", Scope, 0 );
				Right;
			}
			else if ( Cur_Char == "/" ) {		// Handle possibility of C++ comments
				Eol;
			}
			else {
				Left;
			}
		}
		else {
			if ( C_Col > 1 ) {
				Left;
				// If we found a closing comment then match it
				if ( Cur_Char == "*" ) {
					S_Res = Find_Text( "/*", Scope, _Backward );
				}
				else if ( Cur_Char != "/" ) {
					Right;
				}
			}
			else if ( C_Line == 1 ) {
				S_Res = False;
			}
		}
		goto Match_Loop;
	}

Error_Exit:       /* We go here if no match was found */
	goto_mark;
	if ( ShowMessage ) {
		if ( Scope > 0 ) {
			Make_Message( "Match NOT found within " + Str( Scope ) + " lines." );
		}
		else {
			Make_Message( "Match NOT Found." );
		}
	}
	Return_Int = False;
	goto Macro_Exit;

Found_Exit:       /* We go here if a match was found */

	F_Line = C_Line;
	F_Col = C_Col;

	if ( C_Line > T_Line) {
		Jx = C_Line - T_Line;
	}
	else {
		Jx = T_Line - C_Line;
	}

	Goto_Mark;
	Mark_Pos;

	//***** Temp *****
	T_Col = C_Col;
	T_Line = C_Line;
	//****************
	int Highlight_Block = Parse_Int( "/HI=", MParm_Str );

#ifdef Windows
	if ( Jx < Win_CHeight )
#else
	if ( Jx < Screen_Length )
#endif
	{
		while ( Jx > 0 ) {
			--Jx;
			if ( F_Line > T_Line ) {
				Down;
			}
			else {
				Up;
			}
		}
	}
	else {
		Highlight_Block = False;
	}
	Goto_Line( F_Line );
	Goto_Col( F_Col );
	if ( ShowMessage ) {
		Make_Message( "Match Found." );
	}
	if ( Highlight_Block ) {
		Refresh = True;
		if ( F_Line > T_Line ) {
//			++F_Col;
			F_Col += Length( Found_Str );
		}
		else if ( T_Line > F_Line ) {
//			++T_Col;
			T_Col += Length( Found_Str );
		}
		else {
			if ( F_Col > T_Col ) {
//				++F_Col;
				F_Col += Length( Found_Str );
			}
			else {
//				++T_Col;
				T_Col += Length( Found_Str );
			}
		}
		if ( Parse_Int( "/RC=", MParm_Str ) ) {
			Goto_Mark;
		}
		else {
			Pop_Mark;
		}
		Set_Highlight( F_Line, F_Col, T_Line, T_Col );

#ifndef Windows
		while (shift_stat == peek(0, 0x417)) {
			if (check_key) {
				shift_stat = -1;
				push_key(key1, key2);
			}
		}
		Goto_Line( C_Line );
		Goto_Col( C_Col );
#endif

	}
	else {
		if ( Parse_Int( "/RC=", MParm_Str ) ) {
			Goto_Mark;
		}
		else {
			Pop_Mark;
		}
	}
	Return_Int = True;

Macro_Exit:
//	stream_block_mode = tstream_block;
	Refresh = Srf;
	Reg_Exp_Stat = SRe;
	Ignore_Case = SIc;
	Redraw;
	Pop_Undo;

}  // PerMtch

void PerlSetX( ) {
/* ******************************************************************************
																MULTI-EDIT MACRO

Name: PerlSetX

Description:	.C file name extension initialization code.  Sets up the .C
							specific global variable for template expansion.  see macro
							called "Template" in LANGUAGE.S for details of special characters
							found in the global string "!C.Tmplt0".

							 (C) Copyright 1992 by American Cybernetics, Inc.
**************************************************************************** */

#ifdef Windows

	struct tCProperties rCP;

	CGetProperties( rCP );

	if ( rCP.Options & _c_opt_ParenHi ) {
    Key_To_Window( Ascii( ")" ), "Perl^Perl_CLOSE_PAREN" );  // ")" key
	}
	if ( rCP.Options & ( _c_opt_BraceHi | _c_opt_BraceAdj ) ) {
    Key_To_Window( Ascii( "}" ), "Perl^Perl_CLOSE_BRACE" );  // "}" key
	}

#else
	if ( !Global_Int( "@DA_AB_MATCH" ) ) {
    key_to_window(<)>, "Perl^Perl_CLOSE_PAREN");
    key_to_window(0x1B7D, "Perl^Perl_CLOSE_BRACE");  // "}" key
	}
#endif
}  // PerlSetX

void Perl_CLOSE_PAREN( ) {
/*-----------------09-16-91 00:00am-----------------
 * Highlights to matching open paren when a closing
 * paren is entered.
 *--------------------------------------------------*/

	int SavRefresh = Refresh;
	int TSearch_Highlight;

  Push_Undo;
	Refresh = False;
  Text( ")" );
	if ( ( GetStatusAtCursor( ) & 0x0F ) == 0 ) {
  	Left;
    Rm( "Perl^PerMtch " + MParm_Str + "/RC=1/HI=1/LS=20" );
		TSearch_Highlight = Search_Highlight;
		Right;
		Search_Highlight = TSearch_Highlight;
	}
	Refresh = SavRefresh;
  Pop_Undo;

}  // Perl_CLOSE_PAREN

void Perl_CLOSE_BRACE( ) {
/*-----------------09-16-91 00:00am-----------------
 * Sets the indenting correctly when a closing brace
 * is entered.
 *--------------------------------------------------*/

	int Jx = C_Col;
	int TRefresh = Refresh;
	int TSearch_Highlight;

	struct tCProperties rCP;

	CGetProperties( rCP );
	Push_Undo;
	Refresh = False;
	Text( "}" );
	if ( ( GetStatusAtCursor( ) & 0x0F ) == 0 ) {
		First_Word;
		if ( Jx == C_Col ) {
			Mark_Pos;
      Rm( "Perl^PerMtch " + MParm_Str + "/HI=" + Str( rCP.Options & _c_opt_BraceHi ) );
			TSearch_Highlight = Search_Highlight;
			if ( rCP.Options & _c_opt_BraceAdj ) {
				First_Word;
				Jx = C_Col;
				Goto_Mark;
				Del_Char;
				if(at_eol)
					Put_Line(Shorten_Str(Get_Line));
				Goto_Col( Jx );
				Text( "}" );
			}
			else {
				Goto_Mark;
			}
			Search_Highlight = TSearch_Highlight;
		}
		else {
			Goto_Col( Jx );
			Right;
		}
	}
	Refresh = TRefresh;
	Pop_Undo;

}  // Perl_CLOSE_BRACE