// $Header: /MeShare/Src/C.S 17    4/17/96 16:47 Dan $

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

Name:		C

Description:  This macro file contains language support macros for C.

C_IND         - Smart indent for C
CMTCH         - Construct/brace matching for C
BUILDCMT      - Builds a shell of a comment block for C
CSETX         - Sets up the C specific global variable for template expansion
C_CLOSE_PAREN - Highlights to matching open paren when closing paren is entered.
C_CLOSE_BRACE - Sets the indenting correctly when a closing brace is entered.
C_COMMENT     - Handles formatting of EOL comments while entering.

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

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

#ifdef _Debug_
  #include MSGLOG.SH
#endif

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

#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 _c_opt_AutoEolCmt     0x0020    // DLS, Added define

#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_ContInd						1004

#define id_ParenHiCB					1100
#define id_BraceHiCB					1101
#define id_BraceAdjCB					1102
#define id_EolCmtAdjCB				1103
#define id_AutoCmtCB					1104
#define id_AutoEolCmtCB       1105      // DLS, Added define

// Comment character structure
structure tCProperties {
	str Language;
	int IndStyle;
	int Options;
	int EolCol;
	int ContInd;
}

void _CShowIndStyle( int Dlg = Parse_Int( "/DATAHANDLE=", MParm_Str ) )
/******************************************************************************
															 Multi-Edit Macro
															 20-Oct-95  15:47

	Function: Show the a sample of the indent selected indent style.  Only to be
						called by the CSetProperties macro. ( Internal Function )

	Entry   : int Dlg			- The data handle for the Properties dialog
	Exit    : None

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

		str TStr1[ 10 ];
		str TStr2[ 10 ];
		str TStr3[ 10 ];
		str TStr4[ 10 ];

		int Style;

    DlgSetFields( Dlg );
		Style = DlgGetInt( Dlg, id_IndentStyle );
		switch ( Style ) {
			case 1 :
				TStr1 = "if ( ) {";
				TStr2 = "  ++i;";
				TStr3 = "}";
				TStr4 = "";
				break;

			case 2 :
				TStr1 = "if ( )";
				TStr2 = "{";
				TStr3 = "  ++i;";
				TStr4 = "}";
				break;

			case 3 :
				TStr1 = "if ( )";
				TStr2 = "{ ++i;";
				TStr3 = "}";
				TStr4 = "";
				break;

			case 4 :
				TStr1 = "if ( )";
				TStr2 = "  {";
				TStr3 = "  ++i;";
				TStr4 = "  }";
				break;
		}
  	DlgSetStr( Dlg, id_StyleTxt1, TStr1 );
  	DlgUpdateCtrl( Dlg, id_StyleTxt1, 0 );

  	DlgSetStr( Dlg, id_StyleTxt2, TStr2 );
  	DlgUpdateCtrl( Dlg, id_StyleTxt2, 0 );

  	DlgSetStr( Dlg, id_StyleTxt3, TStr3 );
  	DlgUpdateCtrl( Dlg, id_StyleTxt3, 0 );

  	DlgSetStr( Dlg, id_StyleTxt4, TStr4 );
  	DlgUpdateCtrl( Dlg, id_StyleTxt4, 0 );
	}

}  // CShowIndStyle

void CSetProperties(
			str GStr 		= Parse_Str( "/GSTR=", MParm_Str ),
			str ParmStr = 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 (/GSTR=)
						str ParmStr		- Misc parameters
							/L=str				- Language name to show on title bar

	Exit    : None

							 Copyright (C) 1995 by American Cybernetics, Inc.
********************************************************************( ldh )***/
{
	str Properties = Global_Str( GStr );
	str Language	 = Parse_Str( "/L=", ParmStr );

	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 ContInd	 = Parse_Int( _c_prop_ContInd, Properties );
	int Dlg;
	int X;
	int Y;

	if ( Svl( Language ) == 0 ) {
		Language = "C";
	}

	DlgCreate( Dlg );

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

	DlgAddCtrl( Dlg, dlg_BlackFrame, "",
			dlg_PosOffset + 11, 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 + 14, dlg_PosOffset + 0,
			6, 0,
			id_EolCol, 0, "/MIN=0/MAX=" + Str( Max_Line_Length ) );
	DlgSetInt( Dlg, id_EolCol, EolCol );

	DlgAddCtrl( Dlg, dlg_Static, "&Cont Indents:",
			1, dlg_PosOffset + 1,
			0, 0,
			-1, 0, "" );
	DlgAddCtrl( Dlg, dlg_Integer, "",
			dlg_PosOffset + 14, dlg_PosOffset + 0,
			6, 0,
			id_ContInd, 0, "/MIN=0/MAX=10" );
	DlgSetInt( Dlg, id_ContInd, ContInd );

	DlgAddCtrl( Dlg, dlg_GroupBox, "Options",
			1, dlg_PosOffset + 1,
			40, 7,
			-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 );

  // 4/3/96 2:06PM DLS: Add new control
  DlgAddCtrl( Dlg, dlg_CheckBox, "Au&to Insert Eol Comment",
			dlg_PosOffset + 0, dlg_PosOffset + 1,
			0, 0,
      id_AutoEolCmtCB, 0, "" );
  DlgSetInt( Dlg, id_AutoEolCmtCB, ( Options & _c_opt_AutoEolCmt ) != 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, Language + " Properties Setup",
			"", "/INITMAC=_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;
		}
    if ( DlgGetInt( Dlg, id_AutoEolCmtCB ) != 0 ) {
      Options |= _c_opt_AutoEolCmt;
		}
		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 ) +
				_c_prop_ContInd + Str( DlgGetInt( Dlg, id_ContInd ) );
		Set_Global_Str( GStr, Properties );
	}
	DlgKill( Dlg );

}  // CSetProperties

int CGetProperties( struct tCProperties rCP )
/******************************************************************************
															 Multi-Edit Macro
															 20-Oct-95  12:36

	Function: Get the specific language properties for the current file.
	Entry   : struct rCP		- A structure to fill with the properties.
	Exit    : int
							True		- Properties fill from Db
							False		- Properties set to internal defaults

							 Copyright (C) 1995 by American Cybernetics, Inc.
********************************************************************( ldh )***/
{
	int Result = False;

	str Language;
	str Properties;
	str LanguageRec[ Max_Line_Length ];

	if ( LangGetRecord( Language, LanguageRec ) == _NoError ) {
		Properties = Parse_Str( "\x7F" + _db_lang_Properties + "=", LanguageRec );
	}
	if ( Svl( Properties ) != 0 ) {
		rCP.Language = Language;
		rCP.IndStyle = Parse_Int( _c_prop_IndentStyle, Properties );
		rCP.Options  = Parse_Int( _c_prop_Options, Properties );
		rCP.EolCol   = Parse_Int( _c_prop_EolCol, Properties );
		rCP.ContInd	 = Parse_Int( _c_Prop_ContInd, Properties );
		Result = True;
	}
	else {
		rCP.IndStyle = 0;
		rCP.EolCol   = 41;
		rCP.ContInd	 = 1;
		rCP.Options  = _c_opt_ParenHi | _c_opt_BraceAdj | _c_opt_EolCmtAdj;
	}
	return ( Result );

}  // CGetProperties

void CIndentTmp(
      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 );
	if ( rCP.IndStyle == 2 ) {
		Mark_Pos;
		First_Word;
		if ( Cur_Char == '{' ) {
      Word_Right;
			++g_TmpIndentCnt;
			Set_Global_Int( tmp_SavIndent + Str( g_TmpIndentCnt ), g_Original_Col );
			if ( C_Col > g_Original_Col ) {
				g_Original_Col = C_Col;
			}
		}
    Goto_Mark;
	}
	TmpExpand( Set, Name + Str( rCP.IndStyle ) );

}  // CIndentTmp

void C_Ind( )
/******************************************************************************
																MULTI-EDIT MACRO

	Name: C_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 DelAutoCmt = False;
	int DoAutoEolCmt;
	int ShowMessage = !Parse_Int( "/NM=", MParm_Str );
  int AutoCmtStar;

	char  orig_char ;

	struct tCProperties rCP;

	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 );
	// Number of times to indent after a line that does not have ';{}' at the end
	// and flag for outdenting after a closing brace
	Indent_Number = rCP.ContInd;
	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;
	}
	// Setup auto end of line comment flag
  if ( XPos( "/AEC=", MParm_Str, 1 ) ) {
    DoAutoEolCmt = Parse_Int( "/AEC=", MParm_Str );
  }
  else {
		DoAutoEolCmt = ( rCP.Options & _c_opt_AutoEolCmt );
  }

	/* 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 */
  if ( XPos( "/ABC=", MParm_Str, 1 ) ) {
    Match_Brace = Parse_Int( "/ABC=", MParm_Str );
  }
  else {
    Match_Brace = rCP.Options & _c_opt_BraceAdj;
  }
	Go_Match_Brace = FALSE;

	Messages = False;
	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;

  // -------- Begin Change 3/27/96 10:26PM (from 01-06-94 11:32pm) DLS
	// If in_comment is 2, we are in a single line (double slash) comment.
	//  skip_c_noise leaves the cursor at the opening	'/'.
	//
  if ( DoAutoEolCmt && ( In_Comment == 2 ) ) {
    // Remember cursor position for later
    {mark_pos; ++mark_count;}
		// -------------------------------------------------------------------
    // Move three spaces right.
		//
    Right;
    Right;
    Right;
    if ( !At_Eol )  { // we want to insert the slashes below the last
      {goto_mark; --mark_count;}   // Back to the 1st slash
      Set_Indent_Level;            // This is the new indent level
      {goto_mark; --mark_count; }  // Back to where enter was pressed
			Cr;
			Text( "// " );
			goto Mac_Exit;
		}
		// -------------------------------------------------------------------
    // MOVE ONE TO THE LEFT, and see if we are still at EOL.
		//
		Left;
		if ( At_Eol ) {
      // -----------------------------------------------------------------
      // OK. We are at the end of a line which has nothing except the double
      // slashes (i.e. not even a space after them).  This means that the
      // user typed //<CR>.  We will insert a dashed-line separator on the
      // current line, insert a <CR>, and put new double-slashes on the
      // next line.
      //
      {goto_mark; --mark_count;}
      Set_Indent_Level;
      {goto_mark; --mark_count;}
      Text( ' ' );
      // -----------------------------------------------------------------
      // If you don't like dashed-line, comment out the while loop below!
      //
      while ( C_Col < 75 ) {
        Text( '-' );
      }
      Cr;
      Text( "// " );
      goto Mac_Exit;
		}
		// -------------------------------------------------------------------
    // We got a CR on a line with only slashes and one space.
    // This means that the user wants to delete them IF they were inserted
    // automatically.  If so, we should act as if CR was pressed on the
		// line above (i.e. delete this line entirely).
		// We will do all that, and also set the indent level
		//
		In_Comment = FALSE;
		// -------------------------------------------------------------------
		// Determine if these were inserted automatically, by checking for slashes
    // immediately above these.  If there are, assume automatic slashes
    //
    Goto_Mark;                          // Get back to the 1st slash
    Mark_Pos;
    if ( c_line != 1 ) {
      Up;
      if ( Cur_Char == '/' ) {
        Right;
        if ( Cur_Char == '/' ) {
          DelAutoCmt = True;            // Flag says 'we did it'
          Down;
          Del_Line;
          Up;
          Eol;
        }
      }
    }
    // -------------------------------------------------------------------
    // If we didn't delete some auto-slashes, then insert some!
    //
    if ( !DelAutoCmt ) {
      {goto_mark; --mark_count;}   // Back to the 1st slash
      Set_Indent_Level;            // This is the new indent level
      {goto_mark; --mark_count; }  // Back to where enter was pressed
			Cr;
			Text( "// " );
			goto Mac_Exit;
    }
    // -------------------------------------------------------------------
    // We deleted a line which only had automatically inserted slashes, and
    // the cursor is sitting at EOL of the line above it.  Perform indenting
    // as if we never saw the EOL comment!
    //
    {pop_mark; --mark_count;}     // Ignore the position of the 1st slash
    Pop_Mark;                     // pop the marker set on entry
    Mark_Pos;                     // replace it with the current position
		call Skip_C_NoiseX;  // no need to check to see if we're in a comment
	}
	// -------- End Change DLS.
	/*-------------------------------------------------------------------------*\
	| 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 ( XPos( "/ACS=", MParm_Str, 1 ) ) {
      AutoCmtStar = Parse_Int( "/ACS=", MParm_Str );
    }
    else {
      AutoCmtStar = rCP.Options & _c_opt_AutoCmt;
    }
    if ( AutoCmtStar ) {
			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 ( !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 ( 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
	}
	if ( Curr_End_Char == 1 ) {           // semicolon
		// Nearest line-ending char was a semicolon.
		if ( Prev_End_Char == 0 ) {         // if continuation line then undent
			Curr_Ind -= Indent_Number;
		}
		if ( Outdent_Flag & In_Struct ) {
			--Curr_Ind;
		}
	}
	else {
		if ( Curr_End_Char == 2 ) {         // left brace
			// Nearest line-ending char was a 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;}
		LangDoMatch( "C", "", 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:
  if ( DoAutoEolCmt ) {
		// If we are doing 'automatic eol comment insertion', and
		// there is a '//' on this line, set in_comment to 2!
		if ( ( GetStatusAtCursor( ) & 0xC ) != 0 ) {
			Goto_Col( 1 );
    	while ( Find_Text( "//", 1, 0 ) ) {
				if ( GetStatusAtCursor( ) & 0x10 ) {  // In quotes, skip
					continue;
				}
				break;
			}
      In_Comment = 2;
			Sig_Char_Found = FALSE;
      goto Exit_Skip_C;
    }
  }
	// Check if we are in a comment before we move
	In_Comment = GetStatusAtCursor( ) & 0x03;
	Left;
	{mark_pos; ++mark_count;}

	Jx = C_Line;
	if ( GetStatusAtCursor( ) & 0x03 ) {
		if ( LangDoMatch( "C", "*/", "/NM=1/IC=0" ) == 0 ) {
			if ( In_Comment ) {
				{pop_mark; --mark_count;}
				In_Comment = 1;
				Sig_Char_Found = FALSE;
				goto Exit_Skip_C;
			}
			In_Comment = 0;
			if ( C_Line == Jx  ) {
				Left;
				{pop_mark; --mark_count;}
			}
			else {
				{goto_mark; --mark_count;}
			}
			goto Skip_C_Noise1;
		}
	}
	{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 ( ( GetStatusAtCursor( ) & 0x0C ) != 0 ) {
		Goto_Col( 1 );
		while ( Find_Text( "//", 1, 0 ) ) {
			if ( GetStatusAtCursor( ) & 0x10 ) {  // In quotes, skip
				continue;
			}
			break;
		}
		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 ( Find_Text( "[^\t ]", 1, _RegExp | _Backward ) ) {
		if ( Cur_Char == '/' ) {
			Left;
			if ( Cur_Char == '*' ) {
//				Junk = Find_Text( "/*", 0, _Backward );
				Junk = LangDoMatch( "C", "*/", "/NM=1/IC=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;

}  // C_Ind

void CSetX( )
/******************************************************************************
															 Multi-Edit Macro
															 05-Apr-96  11:23

  Function: Sets the C/C++ style language construct matching globals.
  Entry   : None
  Exit    : None
	Author  : Dan Hughes

							 Copyright (C) 1996 by American Cybernetics, Inc.
********************************************************************( ldh )***/
{
	struct tCProperties rCP;

	CGetProperties( rCP );

	if ( rCP.Options & _c_opt_ParenHi ) {
		Key_To_Window( Ascii( ")" ), "LANGUAGE^LangCloseParen /LP=C" );	 // ")" key
	}
	if ( rCP.Options & ( _c_opt_BraceHi | _c_opt_BraceAdj ) ) {
		Key_To_Window( Ascii( "}" ), "C^C_Close_Brace" );  // "}" key
	}
	if ( Length( Global_Str( "!CMatchExtra" ) ) == 0 ) {
		// Set word begin/end characters
		Set_Global_Str( "!CMatchExtra",
      	"B=" + "\xFF\t " +
      	"E=" + "\xFF\t " );
		// Set begin construct patterns
		Set_Global_Str( "!CMatchBegPat",
				"(" +                          // ( matching
						"B= ( " +
						"M=" +
						"E= ) " +
						"X=[()]" +
				"[" +                          // [ matching
						"B= [ " +
						"M=" +
						"E= ] " +
						"X=[\\[\\]]" +
				"{" +                         // { matching
						"B= { " +
						"M=" +
						"E= } " +
						"X=[{}]" +
				"/*" +                         // /* matching
						"B= /* " +
						"M=" +
						"E= */ " +
						"X=(/\\*)|(\\*/)" +
				"#IFDEF#IFNDEF#ELSE" +       // #ifdef, #ifndef, #else matching
						"B= #IFDEF #IFNDEF " +
						"M= #ELSE " +
						"E= #ENDIF " +
						"X=\\#(IFDEF)|(IFNDEF)|(ELSE)|(ENDIF)" +
				"" );
		// Set end construct patterns
		Set_Global_Str( "!CMatchEndPat",
				")" +                          // ) matching
						"B= ) " +
						"E= ( " +
						"X=[()]" +
				"]" +                          // ] matching
						"B= ] " +
						"E= [ " +
						"X=[\\[\\]]" +
				"}" +                          // } matching
						"B= } " +
						"E= { " +
						"X=[{}]" +
				"*/" +                         // */ matching
						"B= */ " +
						"E= /* " +
						"X=(/\\*)|(\\*/)" +
				"#ENDIF" +                     // #endif matching
						"B= #ENDIF " +
						"E= #IFDEF #IFNDEF " +
						"X=\\#(IFDEF)|(IFNDEF)|(ENDIF)" +
				"" );
	}
}  // _CSetX

void _CGetMatchPat( )
/******************************************************************************
															 Multi-Edit Macro
															 05-Apr-96  10:48

  Function: Called by LangDoMatch to locate and return a special pattern to
 						match for C/C++ style languages.

  Entry   : None

  Exit    : Return_Str  - Pattern to match or "" if a special pattern not found
            g_LangInCmt - True when pattern starts in a comment

	Author	: Dan Hughes

							 Copyright (C) 1996 by American Cybernetics, Inc.
********************************************************************( ldh )***/
{
	str Special = "{}()[]/*";
	str TStr;

  int StartInCmt;

	if ( !XPos( Cur_Char, Special + '#', 1 ) ) {
		// If not a special character find one
		Goto_Col( 1 );
		while ( True ) {
			Forward_Till( "{}" );
			if ( XPos( Cur_Char, "{}", 1 ) ) {
				if ( GetStatusAtCursor( ) ) {
					Right;
					continue;
				}
			}
			break;
		}
		if ( At_Eol ) {
			Goto_Col( 1 );
			while ( True ) {
				Forward_Till( "()" );
				if ( XPos( Cur_Char, "()", 1 ) ) {
					if ( GetStatusAtCursor( ) ) {
						Right;
						continue;
					}
				}
				break;
			}
		}
	}
	StartInCmt = GetStatusAtCursor( );
	if ( XPos( Cur_Char, Special, 1 ) ) {
		Mark_Pos;
		TStr = Cur_Char;
		switch ( Cur_Char ) {
			case '/' :
				Right;
				if ( Cur_Char == '*' ) {
					StartInCmt = 1;
					TStr += Cur_Char;
				}
				else {
					Left;
					Left;
					if ( Cur_Char == '*' ) {
						StartInCmt = 1;
						TStr = Cur_Char + TStr ;
						Right;
						Pop_Mark;
						Mark_Pos;
						Left;
					}
					else {
						TStr = '';
					}
				}
				break;

			case '*' :
				Right;
				if ( Cur_Char == '/' ) {
					StartInCmt = 1;
					TStr += Cur_Char;
					Pop_Mark;
					Mark_Pos;
					Left;
				}
				else {
					Left;
					Left;
					if ( Cur_Char == '/' ) {
						StartInCmt = 1;
						TStr = Cur_Char + TStr ;
						Pop_Mark;
						Mark_Pos;
					}
					else {
						TStr = '';
					}
				}
				break;
		}
		TStr = "\x7F" + TStr + "\x7F";
	}
  g_LangInCmt = StartInCmt;
  Return_Str = TStr;

}  // _CGetMatchPat

void CMtch( )
/******************************************************************************
															 Multi-Edit Macro
															 04-Apr-96  09:55

  Function: Find matching language constructs for C/C++ style languages.

	Entry   : /HI=1    		- Highlight block
						/RC=1    		-	Restore cursor to original position
						/LS=x    		-	Limit scope of search to x number of lines
						/NM=1				- Display no messages
						/NA=1				- No check for abort

	Exit    : Return_Int
						 -1					- Search aborted
							0         - Match found
							1         - No match found

	Author	: Dan Hughes

							 Copyright (C) 1996 by American Cybernetics, Inc.
********************************************************************( ldh )***/
{
  Return_Int = LangDoMatch( "C", "", MParm_Str );

}  //  CMtch

void C_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( ) & 0x1F ) == 0 ) {
		// Not in a comment or a quote
		First_Word;
		if ( Jx == C_Col ) {
			Mark_Pos;
			LangDoMatch( "C", "", "/NM=1/HI=0" + MParm_Str );
			if ( rCP.Options & _c_opt_BraceAdj ) {
        // ------- 3/29/96 2:34PM-- (from 01-06-94 11:03pm DLS) --------------
        // This code (useful for for c_style=0) added so that if the open brace
        // appears on a line OTHER than the one that begins the statement, we
        // will find the REAL first line, by matching the parenthesis!
        if ( Find_Text( ')', 1, _Backward ) ) {
					LangDoMatch( "C", "", "/NM=1" );
        }
				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;
			}
		}
		else {
			Goto_Col( Jx );
			Right;
		}
		if ( rCP.Options & _c_opt_BraceHi ) {
			Left;
			LangDoMatch( "C", "", "/RC=1" + MParm_Str + "/HI=1" );
			TSearch_Highlight = Search_Highlight;
			Right;
			Search_Highlight = TSearch_Highlight;
		}
	}
	Refresh = TRefresh;
	Pop_Undo;

}  // C_Close_Brace

void C_BuildCmt( )
/* ******************************************************************************
																MULTI-EDIT MACRO

Name:	C_BUILDCMT

Description:	Builds a C comment block. (called by Language^Template)

							 (C) Copyright 1992 by American Cybernetics, Inc.
****************************************************************************** */
{
  int ic,    /*  the indent column  */
      jx,    /*  general purpose  */
      jk,    /*  general purpose  */
      ti     /*  temp insert mode  */
      ;
  int SavRefresh = Refresh;

  str TStr;   /*  general purpose  */
	str TimeStr;
	str sTime;

	push_undo;

  Refresh = False;
  ti = insert_mode;
	insert_mode = true;
	mark_pos;
	while ((get_line == "") && !at_eof) {
		down;
		first_word;
	}
	if (at_eof) {
		goto_mark;
		text("  */");
		left;
		left;
		left;
		goto exit;
	}
	set_indent_level;
	ic = c_col;

	if (ic == 1) {
		if (find_text("(", 1, 0)) {
			mark_pos;
			if (find_text(")", 0, 0)) {
				right;
				if (cur_char != ";") {
					goto_mark;
					goto build_header;
				}
			}
			goto_mark;
		}
	}

	Goto_Mark;
	Goto_Col( ic );

/* If you don't like the fancy comment construct with the time and date, simply
remove the comments off the following 5 lines, and a very simple comment will
be created.
	Text("  */");
	Left;
	Left;
	Left;
	Goto Exit;
*/
	jx = 0;
	while(  (c_col < 79) & (jx < 50)  ) {
		text('-');
		++jx;
	}
	jk = ic + (jx / 2) - 8;
	if(  jk < (ic + 2)  ) {
		jk = ic + 2;
	}
	goto_col( jk );
	Insert_Mode = False;

  GetProfileString( "intl", "sTime", ":", sTime, 2 );
	// Setup Current time string
  TimeStr  = Str_Del( Time, 6, 3);
  if ( XPos( sTime, TimeStr, 1 ) == 2 ) {
    TimeStr = Str_Del( Time, 5, 3 );
	}
	Text( Date + " " + TimeStr );
	Insert_Mode = True;
	Eol;

	rm('C_IND');
	Mark_Pos;
	rm('C_IND');

	jk = 0;
	while(  (jk < jx)  ) {
		text('-');
		++jk;
	}
  Text( "*/" );
	Goto_Mark;

  goto Exit;


Build_Header:
  First_Word;
  TStr = Get_Word( "(" );
  Goto_Mark;
	Goto_Col( 1 );
  Set_Indent_Level;
  Text( "/************************************************" );
//  Rm( "Cr" );
  Cr;
  Text( " *" );
  Jk = 24 - ( ( Svl( TStr ) + 8 ) / 2 );
  if ( Jk < 3 ) {
    Jk = 3;
	}
  Goto_Col( Jk );
  Text( "--- " + TStr + " ---" );
  Cr;
  Text( " *" );
  Cr;
  Text( " ***********************************************/" );
  Up;
  Eol;
  Indent;

Exit:
  Insert_Mode = Ti;
  Refresh = SavRefresh;
  Pop_Undo;

}  // C_BuildCmt

void CCmt( )
/******************************************************************************
															 Multi-Edit Macro
															 20-Oct-95  20:11

	Function: Auto adjust the end of line comments to the end of line column when
						enabled.  Called from the template macro.

	Entry   : /C=str				- The comment characters.
	Exit    : None

							 Copyright (C) 1995 by American Cybernetics, Inc.
********************************************************************( ldh )***/
{
	str TStr = Parse_Str( "/C=", MParm_Str ) + " ";

	int SavRefresh 		= Refresh;
	int SavInsertMode = Insert_Mode;
	int EndFunc				= False;
	int SavCol 				= 0;

	struct tCProperties rCP;

	CGetProperties( rCP );

	Push_Undo;
	Refresh = False;
	Insert_Mode = True;

	if ( rCP.Options & _c_opt_EolCmtAdj ) {
		Mark_Pos;
		First_Word;
		SavCol = C_Col;
		Goto_Mark;
		if ( C_Col != SavCol ) {
			while ( Pos( Cur_Char, " \t\xFF" ) && ( C_Col != 1 ) ) {
				Left;
			}
			if ( C_Col != 1 ) {
				Right;
			}
			if ( ( C_Col == 1 ) && ( Cur_Char == "}" ) ) {
				Right;
				if ( Length( rCP.Language ) != 0 ) {
					Return_Str = "";
					Rm( Copy( rCP.Language, 1, 8 ) + "^" + Copy( rCP.Language, 1, 3 ) +
							"FindFunc" );
					Error_Level = 0;
					if ( Length( Return_Str ) != 0 ) {
						TStr += Return_Str;
						EndFunc = True;
					}
				}
			}
		}
		if ( C_Col < ( rCP.EolCol - 1 ) && !EndFunc ) {
			Goto_Col( rCP.EolCol );
		}
		else {
			TStr = "  " + TStr;
		}
		if ( XPos( "/*", TStr, 1 ) ) {
			TStr += " */";
			SavCol = 3;
		}
		else {
			SavCol = 0;
		}
	}
	Text( TStr );
	Goto_Col( C_Col - SavCol );
	Insert_Mode = SavInsertMode;
	Refresh = SavRefresh;
	Pop_Undo;

}  // CCmt
