/*===========================================================================
  Copyright (C) 1986-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++++++++
.TYPE		Module
.IDENTIFICATION txdisplay.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Display ``TeX-like'' text
.ENVIRONMENT	TermWindows

.COMMENTS	This module contains routines to output a ``TeX-like'' text
		on a window. A complete macro substitution is performed
		(cf tex.c module).

\begin{TeX}

The text to process follows some of the 
\LaTeX\footnote{Leslie Lamport, Addison-Wesley Publishing Company} conventions:
it includes both the text and ``macros'' that specify
processing options.
The available macros are listed in the table.

\begin{table}[htbp]
$$\begin{tabular}{|lp{30em}|} \hline
 	Name	&	Explanation \\  		\hline
{\tt\b b} 	&	Issue a single backslash (\b)	\\
{\tt\b$x$} 			&	Issue symbol $x$, with possibilities
					\{ \} \% \$ \& \_ \^{ } \# space \\
\hline \hline
\multicolumn{2}{|c|}{{\em Blanks and Line Position}} \\	\hline \hline 
{\tt\b\b} 			&	Break the current line		\\
{\tt\b quad}	{\tt\b qquad}	&	Issue 2 or 4 blanks	\\
{\tt \~{ }}			&	Issue a single non-breakable blank \\
{\tt\b Hspace\{$n$\}} 		&	Issue $n$ non-breakable blanks	\\
{\tt\b tab}	 		&	Align to next ``tab''		\\
{\tt\b SkipLine} 		&	Issue a single blank line	\\
{\tt\b indent}			& 	Indentation	\\
{\tt\b hfill}			& 	Horizontal Stretchable space	\\
{\tt\b vfill}			& 	Vertical Stretchable space \\	
{\tt\b tabright\{{\em text}\}	& 	Align rightmost letter of {\em text} to
					next ``tab''		\\
%
\hline \hline
\multicolumn{2}{|c|}{{\em Line Justifications}} \\	\hline \hline 
{\tt\b centerline\{{\em text}\}}	&	Issue a line with {\em text} 
						centered.
					(see also {\tt center} environment)\\
{\tt\b leftline\{{\em text}\}}	&	Issue a line with {\em text} 
						adjusted to the left 
					(see also {\tt left} environment)\\
{\tt\b rightline\{{\em text}\}}	&	Issue a line with {\em text} 
						adjusted to the right 
					(see also {\tt right} environment)\\
\hline \hline
\multicolumn{2}{|c|}{{\em Video Attributes}} \\	\hline \hline 
{\tt\b VB} or {\tt\b bf}	&	use {\bf Boldface} attribute	\\
{\tt\b Vu} or {\tt\b sl} 	&	use {\sl Underscore} attribute	\\
{\tt\b Vr} or {\tt\b em} or{ \tt\b it} 	& use {\em Reverse} attribute	\\
{\tt\b Vb} 			&	use {\tt Blink} attribute	\\
{\tt\b fbox\{{\em text}\}} 	&	use \fbox{Bold+Reverse+Blink}
						attribute	\\	
%
\hline \hline
\multicolumn{2}{|c|}{{\em Environments}} \\	\hline \hline 
{\tt\{verbatim\}} 		&	Display text as it is	\\
{\tt\{center\}} 		&	Centering text 		\\
{\tt\{left\}} \quad{\tt\{right\}} &	Left-aligned / Right-adjusted text, 
					also called raggedright / raggedleft\\
{\tt\{indent\}} 		&	Push left margin 	\\
{\tt\{quote\}}		&	Push left and right margins	\\
{\tt\{itemize\}}		&	Itemized  list; {\tt\b item}
					starts each item		\\
{\tt\{enumerate\}}		&	Start an enumerated list;
					{\tt\b item} starts each item	\\
{\tt \{alphaenumerate\}}	&	Enumerated list using 
						lowercase letters\\
{\tt \{Alphaenumerate\}}	&	Enumerated list using 
						uppercase letters\\
{\tt \{more\}}\{{\em name}\}	&	Text which can be retrieved, 
					but not displayed \\
{\tt \{table\}\{{\em pos}\}\{{\em w}\}}	& 
		Table environment. 
		See explanations in the text.	\\
\hline \hline
\multicolumn{2}{|c|}{{\em Environment-specific macros}} \\	\hline \hline 
%
{\tt\b item} 			&	Specifies a new element in the 
				 itemize / enumerate list\\
{\tt\b crule\{$c_1$--$c_2$\}} {\tt\b Rule} 	&Issue a horizontal line over 
					one columns $c_1$ to $c_2$ and whole table	\\
{\tt\b len\{{\em text}\}}	&	Computes length of {\em text} \\
{\tt\b multicolumn\{$n$\}\{{\em j}\}\{{\em text}\}} 
			&	define a column spreading over $n$
				columns with {\em j} justification.\\
{\tt\b columns\{{\em pos}\}\{{\em w}\}}	& redefine columns in 
		Table environment\\
\hline \hline
\multicolumn{2}{|c|}{{\em Miscellaneous}} \\	\hline \hline 
%
{\tt\b bell} 			&	Ring the terminal bell	\\
{\tt\b input\{{\em filename}\}}  & Get text to process from {\em filename} \\
{\tt\b def\b{\em name\#}\dots\{{\em equivalence}\}}
				& Macro definition	\\
{\tt\b today}\quad {\tt\b time}\quad {\tt\b now}
				& Date, time, date+time editions\\
{\tt\b EOF}			& End of text indicator	(stops when found) \\
{\tt\b iftrue} {\tt\b iffalse}
{\tt\b else} {\tt\b fi}	&	Conditional tests \\
{\tt\b CheckLines\{$n$\}}	&	Terminate if less than 	$n$ lines 
						remain on the current window \\
{\tt\b FormField\{{\em name}\}\{{\em type}\}\{{\em pic}\}\{{\em text}\}}&
				Define a named field
				which can be retrieved in the calling program
				\via {\em GetMarkedFields}\\ 
\hline\end{tabular}$$
\caption{\label{macro:tex}TermDisplay macros}\end{table}

\medskip
{\bf Table Environment} 

\label{env:table}
The 2 parameters of the {\tt\b begin\{table\}} or 
{\tt\b columns} macros specify:
\begin{enumerate}
\item The {\em pos} parameter, similar to the \LaTeX:
	possibilities of \quad l \quad r \quad c \quad p \quad for left, 
	right, centered or justified text,
	$|$ for a vertical line separating columns,
	and blanks before / after the $|$ .
\item The {\em w} parameter specifies the {\em width} of
	each column, as number of characters separated
	by commas or blanks; if the last column width is
	not specified, it is assumed to use the {\em remaining space}.
\end{enumerate}

Examples:
\begin{itemize}
\item 	{\tt\b begin\{table\}\{|r |p|\}\{5,50\}} will create a table with 2 columns:
	a right-adjusted column (with a blank at the left of the
	second $|$) of 5 characters, and a justified column
	with 50 characters; the complete width of the table is 59 characters
	(don't forget the space required by the vertical lines and blanks!)
\item 	{\tt\b begin\{table\}\{rcp\}\{5,10\}} will create a table with 3 columns:
	a right-adjusted column of 5 characters, a centered column of 10 
	characters, and a justified column of 65 characters if the
	width of the window is 80 characters.
\end{itemize}

\medskip
{\bf more environment} 
\label{env:Ccode}

The text between {\tt\b begin\{more\}\{Ccode\}} and {\tt\b end\{more\}}
contains C instructions (cf {\bf cc} module) which can be retrieved with
the {\em tx\_more{Ccode}} function, then compiled (see {\em cc\_compile})
and executed. 
The text between {\tt\b begin\{more\}\{Ccode\}} and {\tt\b end\{more\}}
will never be displayed on a window.

\medskip

The program uses a {\em tex\_action} routine; the characters passed to
this routine have the following meaning:
$$\begin{tabular}{|ll||ll|} \hline
\multicolumn{2}{|c||}{\em Single character actions} &
\multicolumn{2}{|c|}{\em Two character actions} \\
\hline
\ 	&single stretchable blank	&
	&	\\
\b r	&new line	&
	&	\\
\b n	&new paragraph		&
	&	\\
0	&begin / end environments	&
	F	&Field definitions\\
\{	&open a new environment		&
	f	&fill horizontal / vertical\\
\}	&return to previous environment	&
	J	&justification specifications\\
/	&issue a blank line		&
	M	&Move margins operations\\
\&	&start next column of a table	&
	m	&multicolumn definitions\\
t	&start at next `tab'		&
	V	&Video attributes specifications\\
b	&ring terminal bell		&
	&	\\
r	&rule over one column	& 
	&	\\
R	&rule all over table	&
	&	\\
\hline
\end{tabular}$$


\end{TeX}

.VERSION 1.0	24-Sep-1986:	Creation
.VERSION 1.1	30-Jan-1987:	Redefinition of internal functions as static
.VERSION 1.2	01-Jun-1987:	Added \ul macro (underline)
.VERSION 2.0	02-Jul-1987:	Version '2' of TermWindows.
				Removed LastRule.
				Added CentreColumns, LeftColumns, RightColumns.
.VERSION 2.1	28-Sep-1987:	For Forms, added Macros
				\Field{name}{text}
				\Hspace{len}
.VERSION 3.0	29-Apr-1988:	Version '3', with complete macro processing.
.VERSION 3.1	08-Dec-1988:	\tab action does not indent if the
				cursor is at a line beginning.
			Added environment {Ccode}, which accumulates the
			text into the Ccode buffer, and which can be
			retrieved via tx_ccode.
.VERSION 3.2	31-Jan-1989:	\columns{}{} to redefine columns of a table
.VERSION 3.3	01-Mar-1989:	Replaced Ccode by {more}{Ccode}.
			Added tx_symbol() to translate a symbol.
.VERSION 3.31	14-Mar-1989:	Removed bug in tx_action,
			at tx_item call, and tx_item
			(Syndrome: item tag disappeared at top of page)
.VERSION 3.32	26-May-1989:	Added tx_def to define a symbol.
.VERSION 3.4	29-Jun-1989:	Redesigned table editions.
.VERSION 3.5	24-Jul-1989:	Removed bug in tx_rule / nltab
.VERSION 3.51	16-Aug-1989:	Added close_table as a separate routine.
.VERSION 3.6 	19-Mar-1990:	Removed bug in tx_action.
.VERSION 3.7 	21-Nov-1990:	Added obeylines / obeyspaces + 
				begin{group}/end{group}
.VERSION 3.8 	04-Dec-1990: Removed bug in tabright, alignment was not correct
				when linesize not a multiple of MARGIN..
				Also allow negative Hspace
.VERSION 3.9 	10-Jun-1991: Modified tx_more to allow several `more'
			environments

 090901         last modif
----------------------------------------------------------------------------*/

#define	DEBUG		0	/* For debugging only		*/

#define	PM_LEVEL 	LEVEL_TX

#include <midas_def.h>

#include <stdlib.h>



#include <twset.h>
#include <tex.h>
#include <atex.h>
#include <buffer.h>
#include <str.h>

#define HSIZE	300		/* Size of h-table	*/


extern int tv_bell();

extern int tw_fill(), tw_attr(), tw_uattr(), tw_r(), tw_goto(), tw_rule();
extern int tw_st(), tw_uflag(), tw_chars(), tw_copw(), tw_flags();
extern int tw_uw(), tw_write(), tw_il(), tw_clear(), tw_dc();
extern int tw_nl(), tw_upw(), tw_uc(), tw_mvc(), tw_copy();
extern int tw_mrule();

extern int pm_enter(), pm_iexit(), pm_tr2(), pm_ed_i();

extern int eh_ed_as(), eh_put1(), eh_put();

extern int tex_exec(), tex_unit(), tex_getparm(), tex_mexec();
extern int tex_input(), tex_load(), tex_list();

extern int tx_jr(), tx_jc(), tx_justify(); 


static 	char item_ed[3];
static	char zero = EOS;

typedef struct {
	short int col_lim[2];
	short int line_lim[2];		/* [0] = Top next & / [1] = Next \\ */
	unsigned char smart;		/* Smart (justify) attribute	*/
	unsigned char attr;		/* Attribute for this depth	*/
	unsigned char cols;		/* Number of columns this depth	*/
	unsigned char col_no;		/* Current column number	*/
	unsigned char col_i;		/* Increment column number	*/
	unsigned char flags;	
#define ForceJustification	0x01
#define NoJustification		0x02
#define JustificationDone	0x04
#define AnchoredPosition	0x08
#define InField			0x10
	char  tex_mode;
	char  env;	
	short count;			/* For enumerate		*/
   }	DEPTH;

static DEPTH *pd, *pd0;	/* Pointer to current environment */
static DEPTH column;

#define _JUSTIFY_	(_LEFT_+_RIGHT_)
#define _LEFT_BAR_	0x10
#define _RIGHT_BAR_	0x20
#define _LEFT_BLANK_	0x40
#define _RIGHT_BLANK_	0x80

#define _MARKER1_	0x8000		/* 2 markers are used to allow 	*/
static 	short 	marked;

#define Lines()		(ws->dim[0])
#define Columns()	(ws->dim[1])
#define Position(i,j)	((i)*(ws->dim[1]) + j)
#define CurrentLine()	(ws->pos / ws->dim[1])
#define CurrentColumn()	(ws->pos % ws->dim[1])
#define BottomReached()	(ws->pos >= ws->marker[1])


	/* Static variables used for communication between routines */

static TeX	htex = {	/* The TeX header	*/
	NULL_PTR(H_TABLE), NULL_FCT(int), NULL_FCT(int), NULL_PTR(char),
	0,0,0};

static BUFFER	*depth_buf = NULL_PTR(BUFFER);

static BUFFER	line_buf = BUF_Init(char, 80);
		/* This buffer contains the `blank' line */

static BUFFER	field_names = BUF_Init(char, 128);
		/* This buffer contains the Marked Field Names */

static BUFFER	field_def = BUF_Init(short int, 48);
		/* This buffer contains the Column Ranges of each field */

static BUFFER 	More = BUF_Init(char, 128);
		/* This buffer contains the C Code in the Ccode env. */

static BUFFER 	aux = BUF_Init(char, 24);
		/* This buffer contains the temporary output	*/

static BUFFER 	oMore = BUF_Init(int, 4);
		/* This buffer contains the offsets in More env. */

static BUFFER	*inaux = NULL_PTR(BUFFER);	/* When output redirected */

static short 	MARGIN;	/* Margin Size, computed as 1/10 of window width */
static char 	WindowIsActive 	= 0;
static char 	blank_lines	= 0;
static char 	rule_lines	= 0;
static char 	do_init_line	= 0;
static char 	last_issued_char = EOS;
static char	table_depth = 0;

#define MAX_MARKED	6
static int 	vfill_no = 0;
static int 	hfill_no = 0;
static short	vfill[MAX_MARKED];
static short	hfill[MAX_MARKED];
static char 	blanks[32];

static int	(*external_action)() = NULL_FCT(int);
				/* User-defined function for math symbols */

static WINDOW	*ws = NULL_WINDOW;		/* Used Window		 */

static ACHAR 	current_blank = ' ';	/* Blank attr of begin of line */

static int	txopt = 1;		/* Centering Option	*/

int tx_out(), tx_action();

	MONITOR(TXDISPLAY);


/*==========================================================================
 *			tx_set
 *==========================================================================*/
static int tx_set()
/*+++
.PURPOSE Positions in the current environment.
.RETURNS OK
.REMARKS Not traced
---*/
{
  pd0 = BUF_ItemPosition(depth_buf, DEPTH), pd = pd0;
  if (pd0->cols)	pd += (1 + pd->col_no);

  return(OK);
}

/*==========================================================================
 *			init_line
 *==========================================================================*/
static int init_line()
/*+++
.PURPOSE Write Rules
.RETURNS OK
.REMARKS 
---*/
{
	int	i, j;

  i = CurrentLine();
  if (CurrentColumn() > 0)	i++;
  i = Position(i, 0);
  if (BottomReached())	FINISH;

  for (j = 0; j < line_buf.used; j++)
  {
  	if (line_buf.buf[j] == 1)	ws->pos = i + j,
  					tw_rule(ws, _DOWN_, ws->dim[0]);
  }

  FIN:
  ws->pos = i;
  do_init_line = 0;	/* Performed... */

  return(OK);
}
  
/*==========================================================================
 *			tx_tex
 *==========================================================================*/
TeX *tx_tex()
/*+++
.PURPOSE Retrieve the TeX structure (macro table, etc); initilize if not done.
.RETURNS Pointer to the TeX structure used by TermDisplay.
.REMARKS The macros H-table is created if necessary
---*/
{
	MID_STATIC char definitions[] = "\
\\def\\indent{\03MI\04}\
\\def\\vfill{\03fv\04}\
\\def\\hfill{\03fh\04}\
\\def\\Hspace#1{\\action{Mh}}\
\\def\\CheckLines#1{\\action{MV}}\
\\def\\CheckCols#1{\\action{MH}}\
\\def\\quad{\\ \\ }\
\\def\\qquad{\\ \\ \\ \\ }\
\\def\\SkipLine{\03/\04}\
\\def\\bell{\03b\04}\
\\def\\VB{\03VB\04}\
\\def\\Vr{\03Vr\04}\
\\def\\VR{\03VR\04}\
\\def\\Vu{\03Vu\04}\
\\def\\Vn{\03Vn\04}\
\\def\\Vb{\03Vb\04}\
\\def\\Jl{\03Jl\04}\
\\def\\Jr{\03Jr\04}\
\\def\\Jc{\03Jc\04}\
\\def\\Jf{\03Jf\04}\
\\def\\Jj{\03Jj\04}\
\\def\\bf{\\VB}\
\\def\\em{\\VR}\
\\def\\rm{\\Vn}\
\\def\\it{\\Vr}\
\\def\\sl{\\Vu}\
\\def\\fbox#1{{\\action{VrVBVb}#1}}\
\\def\\item{\\action{Mi}}\
\\def\\crule#1{\\action{r}}\
\\def\\Rule{\\action{R}}\
\\def\\tab{\\action{t}}";
	MID_STATIC char def_env[] = "\
\\def\\tabright#1{\\action{Jt}#1\\action{J0}}\
\\def\\centerline#1{\\\\{\\Jc\\Jf#1}\\\\}\
\\def\\leftline#1{\\\\{\\Jl\\Jf#1}\\\\}\
\\def\\rightline#1{\\\\{\\Jr\\Jf#1}\\\\}\
\\defenv\\left{\\0\\action{JlJf}}{\\0}\
\\defenv\\right{\\0\\action{JrJf}}{\\0}\
\\defenv\\center{\\0\\action{JcJf}}{\\0}\
\\defenv\\quote{\\0\\action{MlMr}}{\\0}\
\\defenv\\itemize{\\0\\action{Ml}}{\\0}\
\\defenv\\indent{\\0\\action{Ml}}{\\0}\
\\defenv\\enumerate{\\0\\action{Ml}}{\\0}\
\\defenv\\alphaenumerate{\\0\\action{Ml}}{\\0}\
\\defenv\\Alphaenumerate{\\0\\action{Ml}}{\\0}\
\\defenv\\group{\\0}{\\0}\
\\defenv\\more#1{\\0}{\\0}\
\\defenv\\table#1#2{\\0}{\\0}";
	MID_STATIC char def_ff[] = "\
\\def\\columns#1#2{\\action{Ft}}\
\\def\\multicolumn#1#2#3{\\action{m#2}#3\\action{m0}}\
\\def\\FormField#4{\\action{Fs}#4\\action{Fe}}\
\\def\\Field#2{\\FormField{#1}{}{}{#2}}\
";

  htex.output = tx_out, htex.action = tx_action;

  if_not(htex.macros)
  {	htex.macros = h_create (HSIZE);
 	TeX_Execute(&htex, definitions, sizeof(definitions)-1);
  	TeX_Execute(&htex, def_env, sizeof(def_env)-1);
  	TeX_Execute(&htex, def_ff , sizeof(def_ff )-1);
  }

  return(&htex);
}

/*==========================================================================
 *			tx_init
 *==========================================================================*/
static int tx_init(str, clear_option)
/*+++
.PURPOSE Initialisation: TeX, buffers, etc
.RETURNS OK  / NOK 
.REMARKS Important actions as \03action\04
---*/
	char 	*str;	/* IN: String to display	*/
	int	clear_option;	/* IN: Option to clear Window before display */
{
	int status, i;
	int old_offset, old_used;

  ENTER("tx_init");

  status = NOK;

  if_not(depth_buf)	/* Not yet initialized */
  {  	oscfill(blanks, sizeof(blanks), ' ');
  	if_not(depth_buf = BUF_Open(DEPTH, 24, 24))
		FINISH;
  	if_not(htex.macros)	tx_tex();	/* Initialise TeX macros */
  }

  	/* Do all initialisations */

  MARGIN 	= (Columns()+4)/10;
  vfill_no 	= 0;
  hfill_no 	= 0;
  inaux     	= NULL_PTR(BUFFER);
  last_issued_char = ' ';
  BUF_Clear(&field_def);
  BUF_Clear(&field_names);
  BUF_Clear(&More);
  BUF_Clear(&aux);
  BUF_Clear(&oMore);
  if (Fields(ws))	BUF_Clear(Fields(ws));

	/* Be sure that window is not Active. It is useless to output
	 * data on the screen immediately --- moreover, justifications
	 * etc must be performed before presenting the nice result ... */

  WindowIsActive = (ws->flags & Echo);
  DeactiveWindow(ws);

  if (clear_option)	ClearWindow(ws);
  blank_lines	= (CurrentColumn() == 0 ? 1 : 0);

  tw_uflag(ws, 1);		/* Window completely modified */


	/* Initialise: set flags, and if the text is starting (non-null str)
	 * initialize depth to zero, default justification, etc ...	*/

  if (str)		/* A new string to display	*/
  {	BUF_Clear(depth_buf);
	column.col_lim[0] = 0, column.col_lim[1] = Columns();
	column.line_lim[0] = 0, column.line_lim[1] = 0;
	column.smart = 0;
	column.attr = 0;
	column.cols = 0;
	column.col_no = 0;
	column.col_i  = 1;
	column.env    = 0;
	column.count  = 0;
	BUF_StackItem(depth_buf, DEPTH, &column);
	BUF_Clear(&line_buf); 
	table_depth = 0;
	rule_lines  = 0;
  }

  		/* Reset All Line Limits */

  tx_set();
  pd0->line_lim[1] = CurrentLine();
  pd0->line_lim[0] = pd0->line_lim[1];
  old_used   = depth_buf->used;
  old_offset = depth_buf->offset;
  for ( pd = pd0; pd; pd = BUF_UnstackItem(depth_buf, DEPTH))
  {
	for (i = pd->cols; i-- >= 0; pd++)
  	{	pd->line_lim[0] = pd0->line_lim[0];
  		pd->line_lim[1] = pd0->line_lim[1];
	}
  }
  depth_buf->used   = old_used;
  depth_buf->offset = old_offset ;

  tx_set();
  SetAttr(ws, pd0->attr);
  current_blank = ws->attr;

	/* In the case of a continuation in the Tab environment,
	 * initialize the first line. This is done with init_line	*/

  if (table_depth)		init_line();

  FIN:
  EXIT(status);
}

/*==========================================================================
 *			new_col
 *==========================================================================*/
static int new_col()
/*+++
.PURPOSE Create a new column
.RETURNS OK
.REMARKS 
---*/
{
	unsigned char j;

  j = column.smart&7;
  if ( (j == _RIGHT_) || (j == _CENTER_))
  	column.flags |= ForceJustification;
  BUF_AppendItem(depth_buf, DEPTH, &column);
  tx_set();
  pd0->cols += 1;
  column.smart = 0;
  column.flags = 0;
  
  return(OK);
}

/*==========================================================================
 *			Mark
 *==========================================================================*/
static int Mark(pos, len)
/*+++
.PURPOSE Mark characters from pos, length len
.RETURNS OK / NOK
.REMARKS 
---*/
	int pos;	/* IN: Position for marker */
	int len;	/* IN: Number of characters to mark	*/
{
	ACHAR 	*pa;

  if (marked == 0)	
  {	pa = Aij(ws, pos/Columns(),  pos%Columns());
	*pa |= _MARKER1_;
  }
  marked += len;
  
  return((pos + len < ws->marker[1] ? OK : NOK));
}

/*==========================================================================
 *			tx_smart
 *==========================================================================*/
static int tx_smart()
/*+++
.PURPOSE Justify the text in column, accordings to pd->smart.
.RETURNS OK 
.REMARKS No justification if left adjustement, or line is empty.
	The flag NoJustification or JustificationDone are taken into account.
---*/
{
	register ACHAR *pa;
	int	i, len, j, justify;

  if (pd->flags & NoJustification)	FINISH;
  if (pd->flags & JustificationDone)
  {	pd->flags &= ~JustificationDone;
  	FINISH;
  }
  if (blank_lines)			FINISH;
  if (htex.mode == _TeX_VERBATIM_)	FINISH;
  if (htex.mode &  _TeX_OBEY_)		FINISH;

  switch( justify = (pd->smart & 7) )
  {  default: justify = _JUSTIFY_;
  	break;
     case _LEFT_ : case _RIGHT_ : case _CENTER_: 
     	break;
  }

  i  = CurrentLine();
  pa = Aij(ws, i, pd->col_lim[0]);
  len = pd->col_lim[1] - pd->col_lim[0];

  if ((current_blank != ws->attr_init) && (current_blank == ws->attr) &&
  	(justify != _JUSTIFY_))
  {	for (j = len; --j >=0; pa++) 
  		if (*pa == ws->attr_init)	*pa = current_blank;
	pa = Aij(ws, i, pd->col_lim[0]);
  }
  else	current_blank = ws->attr_init;	
  
  switch( justify )
  {	case _LEFT_ : 	/* tx_jl(pa, len, current_blank); */
			break;
	case _RIGHT_ : 	tx_jr(pa, len, current_blank);
			break;
	case _CENTER_: 	tx_jc(pa, len, current_blank);
			break;
	case _JUSTIFY_:
			tx_justify(pa, len, current_blank);
			break;
  }
  current_blank = ws->attr;

  ws->pos = Position(i, pd->col_lim[1]);	/* Correct position */
  if (CurrentColumn() == 0)
  	blank_lines  = 1;
  else	blank_lines  = 0;
  
  FIN:
  return(OK);
}
   
/*==========================================================================
 *			tx_blank
 *==========================================================================*/
static int tx_blank()
/*+++
.PURPOSE Output a stretchable blank on a window: 
	don't output blanks on margins, don't repeat spaces.
.RETURNS --- OK if blank not needed or correctly output 
	--- NOK if there is no more room to issue the character.
.REMARKS The blank uses the current attribute --- but Blinking or
		Bold blanks are made normal !
---*/
{
	register int 	j;

  tx_set();
  j = CurrentColumn();
  if ((j <= pd->col_lim[0]) && (!(pd->flags & AnchoredPosition)))
				FINISH;
  if (j >= pd->col_lim[1])	FINISH;
  if (last_issued_char == ' ')	FINISH;		/* Don't issue 2 blanks	*/

  last_issued_char = ' ', j++;

  if (j == pd->col_lim[1])			/* Must justify, no blank */
  	tx_smart();
  else						/* Insert blank		  */
  {	Write(ws, &last_issued_char, 1);
  	rule_lines = 0;
	if (pd->flags & InField)
	  	Mark(ws->pos - 1, 1);
  }

  FIN:
  return(OK);
}

/*==========================================================================
 *			tx_hfil
 *==========================================================================*/
static int tx_hfil()
/*+++
.PURPOSE Insert blanks according to hfill_no.
.RETURNS OK
.REMARKS Not traced
---*/
{
	int j, right_lim, remaining_blanks;
	ACHAR *pa;
	
  pa = Aij(ws, CurrentLine(), 0);	/* Line beginning */
  remaining_blanks =  pd->col_lim[1] - CurrentColumn();

  for (right_lim = pd->col_lim[1]; hfill_no > 0; right_lim=hfill[--hfill_no]+j)
  {	j = hfill[hfill_no - 1];
	tx_jr(pa + j, right_lim - j, ws->attr_init);
	j = remaining_blanks / hfill_no;
	remaining_blanks -= j;
  }
  pd->flags |= NoJustification;		/* Already justified */

  return(OK);
}

/*==========================================================================
 *			tx_nl
 *==========================================================================*/
static int tx_nl()
/*+++
.PURPOSE Issue a new line.
.RETURNS OK
.REMARKS Newline may imply some skips...
---*/
{
	register int i;

  tx_set();
  pd->flags &= ~AnchoredPosition;

  if (BottomReached())		FINISH;

  if (hfill_no)	tx_hfil();		/* Horizontal Fill */
  if (pd->flags & ForceJustification)	tx_smart();

  i = CurrentLine();
  if (i < pd->line_lim[0])	i = pd->line_lim[0];
  pd->line_lim[1] = i;
  i = pd->line_lim[1] + 1;
  ws->pos = Position(i, 0);		/* New position */

  blank_lines += 1;
  pd->flags &= ~(NoJustification | JustificationDone);
  last_issued_char = ' ';

	/* In the Tab Environment, the new line is initialized
	 * when the position is at column 0. */

  if (do_init_line)	init_line();
  
  FIN:
  return(OK);
}

/*==========================================================================
 *			tx_vfil
 *==========================================================================*/
static int tx_vfil()
/*+++
.PURPOSE Insert lines according to vfill_no.
.RETURNS OK
.REMARKS Not traced
---*/
{
	int old_pos, i, remaining_lines;
	
  if(blank_lines == 0)	tx_nl() ;
  
  old_pos = ws->pos;
  i = CurrentLine();
  remaining_lines = Lines() - i;
  if (remaining_lines <= 0)	FINISH;

  for ( ; vfill_no > 0; vfill_no--)
  {	tw_goto(ws, vfill[vfill_no - 1], 0);
	i = remaining_lines / vfill_no;
	tw_il(ws, i);
	old_pos += i*ws->dim[1];
	remaining_lines -= i;
  }
  ws->pos = old_pos;

  FIN:
  return(OK);
}

/*==========================================================================
 *			tabright
 *==========================================================================*/
static int tabright(text, len)
/*+++
.PURPOSE Justify the text at right of next `tab'
.RETURNS Written Length
.REMARKS 
---*/
	char	*text;	/* IN : text to issue 	*/
	int	len;	/* IN : Len of text	*/
{
	int	i, j, nb;
	
  if (inaux == &aux)	inaux = NULL_PTR(BUFFER);

  i = CurrentLine(), j = CurrentColumn();

  if (j < pd->col_lim[0])	j = pd->col_lim[0];
  if ((j+len) > pd->col_lim[1])	j = pd->col_lim[0], i++;
  
  nb = (j+len-pd->col_lim[0])%MARGIN;
  if (nb) nb = (MARGIN - nb);
  j += nb;
  if ((j+len) > pd->col_lim[1]) {	/* Try to remove blanks */
	j = pd->col_lim[1] - len;
	if (j < pd->col_lim[0])	j = pd->col_lim[0];
  }
  ws->pos = Position(i, j);
  pd->flags |= JustificationDone;

  if (len > 0)	i = tx_out(text, len);
  else		i = 0;
  return(i);
}

/*==========================================================================
 *			nltab
 *==========================================================================*/
static int nltab()
/*+++
.PURPOSE Issue a new line in Table Environment
.RETURNS OK
.REMARKS Newline may imply some skips...
---*/
{
	int 	i, j;

  i = CurrentLine();
  pd0->line_lim[0] = MAX(i, pd0->line_lim[1]);

  pd0->col_no = 0, pd0->col_i = 1;
  pd0->line_lim[1] = pd0->line_lim[0];
  ws->pos = Position(pd0->line_lim[0], 0);
  for (j = pd0->cols, pd = pd0+1; --j >= 0; pd++)
	pd->line_lim[0] = pd0->line_lim[0],
	pd->line_lim[1] = pd0->line_lim[1];

  if (do_init_line)	init_line();
  blank_lines = 1;
  last_issued_char = ' ';

  return(OK);
}

/*==========================================================================
 *			clear_down
 *==========================================================================*/
static int clear_down(pd)
/*+++
.PURPOSE Clear the bottom of the table
.RETURNS OK
.REMARKS 
---*/
	DEPTH	*pd;		/* IN: Column concerned	*/
{
	ACHAR	*pa;
	int	i, j, len, old_pos;

  old_pos = ws->pos;
  len = pd->col_lim[1] - pd->col_lim[0]; 
  
  i = CurrentLine();
  if (CurrentColumn() > pd->col_lim[0])	i++;
  i = MAX(i , pd->line_lim[1]);

  j = i-1;	/* Where to start tx_mrule */
  if (j < 0)	j = 0;
  ws->pos = Position(j, pd->col_lim[0]);

  for (; i < ws->dim[0]; i++)
  {
	pa = Aij(ws, i, pd->col_lim[0]);
	for (j = len; --j >= 0; pa++)	*pa = ws->attr_init; 
  }

  tw_mrule(ws, len);

  ws->pos = old_pos;
  
  return(OK);
}
  
/*==========================================================================
 *			open_depth
 *==========================================================================*/
static int open_depth()
/*+++
.PURPOSE Do operations for opening a depth
.RETURNS OK 
.REMARKS 
---*/
{

  BUF_StackItem(depth_buf, DEPTH, pd);	/* Copy this depth */
  tx_set();

  pd->env = 0;
  pd->tex_mode = htex.mode;
  pd->flags &= ~ForceJustification;

  return(OK);
}

/*==========================================================================
 *			close_depth
 *==========================================================================*/
static int close_depth()
/*+++
.PURPOSE Do operations for closing a depth
.RETURNS OK 
.REMARKS 
---*/
{
	unsigned char old_attr;
	short	old_lim[2];
	
  old_attr = pd->attr;
  old_lim[0] = pd0->line_lim[1];
  old_lim[1] =  pd->line_lim[1];
  
  if (depth_buf->offset <= sizeof(int))
  	ERROR("Too many }");
  else	{
  	if (pd->flags & ForceJustification)	tx_smart();
  	BUF_Unstack(depth_buf);
	tx_set();
	htex.mode = pd->tex_mode;
	if (old_attr != pd->attr)	SetAttr(ws, pd->attr);
  	/* pd->line_lim[0] = old_lim[0], */
  	pd0->line_lim[1] = MAX(pd0->line_lim[1], old_lim[0]);
  	 pd->line_lim[1] = MAX( pd->line_lim[1], old_lim[1]);
  }
  
  return(OK);
}

/*==========================================================================
 *			full_width
 *==========================================================================*/
static int full_width(pd, width)
/*+++
.PURPOSE Compute the full width of a column
.RETURNS The width.
.REMARKS Adds the bars
---*/
	DEPTH	*pd;		/* IN: Column concerned	*/
	short	width[2];	/* OUT: Column Limits (right limit excluded)*/
{

  width[0] = pd->col_lim[0];	width[1] = pd->col_lim[1];
  
  if (pd->smart & _LEFT_BLANK_)		width[0]--;
  if (pd->smart & _LEFT_BAR_)		width[0]--;
  if (pd->smart & _RIGHT_BLANK_)	width[1]++;
  if (pd->smart & _RIGHT_BAR_)		width[1]++;
  
  return(width[1] - width[0]);
}
  
/*==========================================================================
 *			tr_line
 *==========================================================================*/
static int tr_line(pd, old, new)
/*+++
.PURPOSE Modify the line buffer.
.RETURNS Number of modifications.
.REMARKS Adds the bars
---*/
	DEPTH	*pd;		/* IN: Column concerned	*/
	int	old;		/* IN: Values to change	*/
	int	new;		/* IN: Replacement	*/
{
	char	*p;
	int	i, count;

  count = 0;
  for (p = line_buf.buf + pd->col_lim[0], i = pd->col_lim[1] - pd->col_lim[0];
  	--i >= 0; p++)
  {
	if (*p == old)	count++, *p = new;
  }

  return(count);
}
  
/*==========================================================================
 *			tx_tab0
 *==========================================================================*/
static int tx_tab0(opt)
/*+++
.PURPOSE Write / Wipe Vertical Rules (until End of Window)
.RETURNS OK 
.REMARKS Starting at current line...
---*/
	int	opt;	/* IN: Option 1 to add, 0 to remove vertical bars */
{
	DEPTH	*pc;
	int 	ic, i, j, old_pos;
	short	lim[2];

  if (line_buf.used == 0)
  {	ic = Columns();
  	BUF_AllocateItems(&line_buf, char, ic);
	oscfill(line_buf.buf, ic, 0);		/* Clear Vertical Rules */
  }

  if (opt == 0)  
  {	tr_line(pd0, 1, 0);	/* Suppress */
  	return(OK);
  }

  old_pos = ws->pos;
  i = old_pos / Columns();	/* Current Line */

  for (ic = pd0->cols, pc = pd0+1; --ic >= 0; pc++)
  {	full_width(pc, lim);
  	if (pc->smart & _LEFT_BAR_)	
  		j = lim[0],
  		ws->pos = Position(i, j),
  		line_buf.buf[j] = 1,
  		tw_rule(ws, _DOWN_, ws->dim[0]);
	if (pc->smart & _RIGHT_BAR_)	
  		j = lim[1]-1,
  		ws->pos = Position(i, j),
  		line_buf.buf[j] = 1,
  		tw_rule(ws, _DOWN_, ws->dim[0]);
  }
  ws->pos = old_pos;
  
  return(OK);
}
  
/*==========================================================================
 *			tx_tab1
 *==========================================================================*/
static int tx_tab1()
/*+++
.PURPOSE Interpret the first field of a tabular environment, made of
	| l r c p{} (column definitions)
.RETURNS Number of defined columns
.REMARKS 
---*/
{
	char 	*p;
	unsigned char j;

  pd0->cols = 0, pd0->smart = 0;
  column = *pd0;

  for (p = htex.ap; *p ; p++)
  {	switch(*p)
	{ case '|':	if (column.smart&7)
				column.smart |= _RIGHT_BAR_, new_col();
			else	column.smart |= _LEFT_BAR_;
			continue;
	  case ' ': 	column.smart |= (column.smart&7 ? 
	  			_RIGHT_BLANK_ : _LEFT_BLANK_);
	  default:	continue;
	  case '{':  	p += 1 + tex_unit(p+1, 1024);	/* Length arbitrary */
	  					continue;
	  case 'l':	j = _LEFT_;	goto NEW_COL;
	  case 'r':	j = _RIGHT_;	goto NEW_COL;
	  case 'c':	j = _CENTER_;	goto NEW_COL;
	  case 'p':	j = _JUSTIFY_;
	  NEW_COL:	if (column.smart&7)
				new_col();
			column.smart |= j;
			break;
	}
  }
  if (column.smart&7)	new_col();

  return ((int)pd0->cols);
}

/*==========================================================================
 *			tx_tab2
 *==========================================================================*/
static int tx_tab2()
/*+++
.PURPOSE Interpret the second field of a tabular environment, 
	indicating the column width.
.RETURNS OK / NOK (table cannot fit)
.REMARKS The last column width is computed, or the table is centered on
	the window.
---*/
{
	char 	*p;
	int 	i, left_position, status;
	DEPTH 	*pc;

  status = OK;

  pd0->col_no = 0;

  left_position  = pd0->col_lim[0];

  for (p = htex.ap, pc = pd0+1; pd0->col_no < pd0->cols; 
  	pc++, (pd0->col_no)++)
  {	i = 0;
	while (isTeX(*p,_TeX_SPACE_))	p++;
	while (isTeX(*p,_TeX_DIGIT_))
		i = i*10 + (*(p++) - '0');
	if (pc->smart & _LEFT_BAR_)	left_position++;
	if (pc->smart & _LEFT_BLANK_)	left_position++;
	pc->col_lim[0] = left_position;
	if (i == 0)
	{	i = pd0->col_lim[1] - left_position;
		if (pc->smart & _RIGHT_BAR_)	i--;
		if (pc->smart & _RIGHT_BLANK_)	i--;
	}
	left_position += i;
	if (left_position > pd0->col_lim[1])  	/* Too large text ... */
		status = NOK, left_position = pd0->col_lim[1];
	pc->col_lim[1] = left_position;

	if (pc->smart & _RIGHT_BAR_)	left_position++;
	if (pc->smart & _RIGHT_BLANK_)	left_position++;

	if (pc->col_lim[1] <= pc->col_lim[0]) 	/* Too large text ... */
 		status = NOK, pc->col_lim[0] = pc->col_lim[1] - 1;

	if (isTeX(*p,_TeX_PUNCT_))	p++;
  }
  pd0->col_no = 0;

	/* If spaces remain, center table	*/

  i = pd0->col_lim[1] - left_position;
  if (i < 0)	status = NOK;

  if_not(status)
  	ERR_ED_STRING("The table cannot fit on window: ", htex.ap);

  else						/* Remaining blanks */
  {	pd0->col_lim[1] = left_position;
  	left_position = ( txopt & 1 ? i/2 : 0);
	for (pc = pd0, i = pd0->cols; i-- >= 0; pc++)
		pc->col_lim[0] += left_position, 
		pc->col_lim[1] += left_position;
  }

  return(status);
}

/*==========================================================================
 *			open_env
 *==========================================================================*/
static int open_table()
/*+++
.PURPOSE Do operations for opening a table
.RETURNS OK 
.REMARKS 
---*/
{
  pd->env = 't';
  tex_getparm(1);	tx_tab1();
  tex_getparm(2);	tx_tab2();
  tx_tab0(1);
  table_depth++;
  return(OK);
}

/*==========================================================================*/
static int open_more()
/*+++
.PURPOSE Do operations for opening a {more}{...} environment
.RETURNS OK 
.REMARKS 
---*/
{
	int	i, l;
	char	*p;
	
  pd->env = 'm';
  tex_getparm(1);	/* More name */
  l = strlen(htex.ap);

  i = More.used;
  BUF_AppendItem(&oMore, int, &(More.used));
  BUF_AppendItems(&More, char, htex.ap, l+1);	/* Copy Name */
  p = More.buf + i;		/* Name address */
  strred(p);		/* Suppress blanks in name */
  More.used = i + strlen(p) + 1;

  return(OK);
}

/*==========================================================================*/
static int open_env()
/*+++
.PURPOSE Do operations for opening a new environment
.RETURNS OK / NOK (not possible)
.REMARKS 
---*/
{
	int 	i;
	char 	env;
	int	min_lines;
	

  i = CurrentLine();	/* Current position 	*/
  env = *(htex.ap+1);	/* Environment to open	*/

	/* Some lines at least must exist before starting */

  if (env == 'm')	min_lines = -1;
  else if (env == 't')	min_lines = (table_depth ? 1:3);
  else			min_lines = 2;
  
  if (i >= Lines() - min_lines)
  	return(NOK);

  if(blank_lines == 0)		tx_nl();

  open_depth();		/* Copy this depth */
  pd->env = env;
  pd->count = 0;
  pd->line_lim[0] = CurrentLine();	pd->line_lim[1] = pd->line_lim[0];
  
  if (env == 'm')	/* more */
  	inaux = &More,
  	open_more();

  if (env == 't')		/* Tabular Environment */
  	open_table(); 

  return(OK);
}

/*==========================================================================
 *			close_env
 *==========================================================================*/
static int close_table()
/*+++
.PURPOSE Do operations for closing a table
.RETURNS OK 
.REMARKS 
---*/
{
  do_init_line = 0;
  clear_down(pd0);	/* Clear the bottom of Columns */
  tx_tab0(0);
  table_depth--;

  return(OK);
}

/*==========================================================================*/
static int close_env()
/*+++
.PURPOSE Do operations for closing an environment
.RETURNS OK 
.REMARKS 
---*/
{
	char *p;

  p = htex.ap + 1;

  if (pd->env != *p)
	ERR_ED_STRING("Mismatching begin/end env.: ", p);

  if (pd0->env == 'm')	/* More */
  	inaux = NULL_PTR(BUFFER),
  	BUF_AppendItem(&More, char, &zero);

  if (pd0->env == 't')	/* Ending Table Env */
	close_table();

  if(blank_lines == 0)	tx_nl() ;

  return(close_depth());
}

/*==========================================================================
 *			tx_item
 *==========================================================================*/
static int tx_item()
/*+++
.PURPOSE Write an `item'
.RETURNS OK / NOK (line full)
.REMARKS Item issued in Bold
---*/
{
	int i, old_attr;


  if(blank_lines == 0)	tx_nl() ;
  if (BottomReached())	return(NOK);
  
  pd->count += 1;	/* Item number */
  pd->col_lim[0] -= sizeof(item_ed);
  if (pd->col_lim[0] < 0)	pd->col_lim[0] = 0;
  ws->pos = Position(CurrentLine(), pd->col_lim[0]);
  item_ed[0] = pd->count, item_ed[1] = '>', item_ed[2] = ' ';

  switch(pd->env)
  { case 'e':	item_ed[0] += '0';	break;
    case 'a':	item_ed[0] += 'a'-1;	break;
    case 'A':	item_ed[0] += 'A'-1;	break;
    default:	item_ed[0] = '=';
  }

  old_attr = SetAttr(ws, _BOLD_);
  i = tx_out(item_ed, 2);
  SetAttr(ws, old_attr);
  if (i == 0)
  	pd->count -= 1, 
	i = NOK;
  else 	tx_out(item_ed+2, sizeof(item_ed)-2),
  	pd->col_lim[0] += sizeof(item_ed),
  	i = OK;

  return(i);
}

/*==========================================================================
 *			multicol
 *==========================================================================*/
static int multicol(str)
/*+++
.PURPOSE Interprets the `multicolumn' environment
.RETURNS Length of string taken into account
.REMARKS 
---*/
	char 	*str;	/* IN: multicolumn specification (starting with `m') */
{
	int 	nc, status;
	DEPTH	*pm;
	char	*p;

  for (p = str; *++p == '\0'; ) ;

  if (*p == '0')	/* Close Multicol Env */
  {
  	do_init_line = tr_line(pd, 2, 1);
  	close_depth();
	return(1);
  }
  
  status = depth_buf->offset;
  open_depth();				/* Create a new depth */
  pm = pd;
  pd0 = (DEPTH *)(depth_buf->buf + status);	/* In case of expansion */
  pd  = pd0 + 1 + pd0->col_no;
  pd->flags |= JustificationDone;		/* Don't justify old columns */
  
  tex_getparm(1);	/* Number of columns */
  nc = atoi(htex.ap);	
  pm->flags |= ForceJustification;
  pm->smart = 0;
  status = OK;
  
  if ((int)(pd0->col_no + nc) > (int)pd0->cols)
  	status = NOK, nc = pd0->cols - pd0->col_no;
  
  if (nc < 1)
  	status = NOK, nc = 1;
  
  pd0->col_i = nc;	/* Increment for next column */

  pm->col_lim[0] = pd->col_lim[0];
  pm->col_lim[1] = (pd + nc - 1)->col_lim[1];


			/* Modify the current line sketch */

  if (tr_line(pm, 1, 2))	clear_down(pm);

  	/* Chose smart attr */
  	
  ChoseSmart:
  switch(*(p++))
  { case '|': case ' ': 
  	if_not(iscntrl(*p))	goto ChoseSmart;	/* If |c| */
    default : pm->smart = _JUSTIFY_, pm->flags &= ~ForceJustification;
    	break;
    case 'l': pm->smart = _LEFT_;	break;
    case 'r': pm->smart = _RIGHT_;	break;
    case 'c': pm->smart = _CENTER_;	break;
  }
  while ( (*p == '|') || (*p == ' '))	p++;
  --p;

			/* Position at correct position	*/

  /* ws->pos = Position(i, pm->col_lim[0]); */

  if_not(status)	ERR_ED_STRING("Bad \\multicolumn ", htex.ap);

  return(p-str);
}

/*==========================================================================
 *			tx_crule
 *==========================================================================*/
static int tx_crule()
/*+++
.PURPOSE Draw a horizontal rule some columns (\crule).
.RETURNS OK / NOK (Bad column)
.REMARKS Argument normally contains 2 parameters separated by a dash.
---*/
{
	int	i, len, status;
	short	lim[2], alim[2];
	char	*p;
	DEPTH	*pd;

  status = NOK;		/* Default status */
  pd0->col_no = 0;
  i = CurrentLine();
  if (rule_lines)	i--, ws->pos -= ws->dim[1];

  if (BottomReached())	FINISH;
  
  len = 0;
  tex_getparm(1);

  p = htex.ap;

  alim[0] = pd0->col_no + 1;	/* Column Number */
  alim[1] = alim[0];
  
  if (*p)	/* There are parameters */
  {
  	p += strloc(p, '-');
  	if (*p == '-')	alim[0] = atoi(htex.ap), 
			alim[1] = atoi(++p);	/* Two Parameters */
	else		alim[1] = alim[0] + atoi(htex.ap);
  }  						
  if ( 	(alim[0] < 1)		||
  	(alim[1] < alim[0]) 	||
  	(alim[1] > (int)pd0->cols))
		{ ERR_ED_STRING("Bad \\crule ", htex.ap); FINISH; }

  full_width(pd0 + alim[0], lim);	/* lim[0] = Left  */

  if (alim[0] > 1)	/* Check if preceding column has a Right Bar */
  {	pd = pd0 + (alim[0] - 1);
	if (pd->smart & _RIGHT_BAR_)	lim[0] -= 1;
  }

  full_width(pd0 + alim[1], alim);	/* alim[1] = Right */

  len = alim[1] - lim[0];

  i = CurrentLine(); 
  if (len > 0)	ws->pos = Position(i, lim[0]),
		tw_rule(ws, _RIGHT_, len);
		
  ++i;		ws->pos = Position(i, 0);
  nltab();	rule_lines = 1;
  status = OK;

  FIN:
  return(status);
}

/*==========================================================================
 *			tx_rule
 *==========================================================================*/
static int tx_rule()
/*+++
.PURPOSE Draw a horizontal rule over the complete window / table.
.RETURNS OK / NOK (Window full)
.REMARKS 
---*/
{
	int	i;
	
  if (BottomReached())	return(NOK);
  if (blank_lines == 0)	tx_nl();
  if (do_init_line)	init_line();

  pd0->col_no = 0;
  i = CurrentLine();

	/* Now replace blanks by horizontal bars */
	
  ws->pos = Position(i, pd0->col_lim[0]);
  tw_rule(ws, _RIGHT_, pd0->col_lim[1] - pd0->col_lim[0]) ; 

  ++i;		ws->pos = Position(i, 0);
  nltab();	rule_lines = 1;

  return(OK);
}

/*==========================================================================
 *			tx_mark
 *==========================================================================*/
static int tx_mark()
/*+++
.PURPOSE Mark the starting point of a field.
.RETURNS OK / NOK (Window full)
.REMARKS 
---*/
{
	int 	i, l;
	
	/* Save Field name, Field Type, Field Picture	*/

  for (i = 1; i <= 3; i++)
  {	tex_getparm(i);		/* Get Param	*/
	l = strlen(htex.ap);	/* Param Length	*/
	BUF_AppendItems(&field_names, char, htex.ap, l+1);
  }
  
  
  	/* Save current column position	*/
  
  BUF_AppendItems(&field_def, short int, &pd->col_lim[0], 2);
  
  pd->flags |= InField;		/* Next output will be flagged */
  marked  = 0;			/* Count of marked bytes	*/

  return(OK);
}

/*==========================================================================
 *			get_marked
 *==========================================================================*/
static int get_marked(st)
/*+++
.PURPOSE Retrieve marked fields.
.RETURNS Number of fields
.REMARKS If a field lies over several lines, complete lines are assumed.
---*/
	int	st;	/* IN: Completion Status of Field	*/
{
	char 	*fname;		/* Field Name 		*/
	TWFIELD	*fdef;		/* Field Definition	*/
	short 	*f, *fe;	/* Current Column	*/
	ACHAR	*pa;
	short 	i, j;
	char	**stored_address;
	BUFFER	*b;
	MID_STATIC	TWFIELD twfield0 = {(char *)0};

	/* Issue Error Message if Bad Status */

  if (st == NOK)
  {	ERROR("The Form doesn't fit on the Window...");
  	return(0);
  }

	/* Allocate first the Field Buffer connected with window */

  if_not(Fields(ws))
  {	Fields(ws) = BUF_Open(char, field_names.used + 
  		sizeof(TWFIELD)*(1 + field_def.used / (3*sizeof(short int))), 
  		8*sizeof(TWFIELD));
  }
  b = Fields(ws);
  BUF_Clear(b);

	/* ... and retrieve fields successively	*/

  f  = (short int *)(field_def.buf);		/* First column range */
  fe = (short int *)(field_def.buf + field_def.used);

  for (; f < fe; f += 3)
  {	fdef = BUF_AllocateItem(b, TWFIELD);

	fdef->dim[1] = f[1] - f[0];	/* Field Width */
	fdef->dim[1] = MIN(fdef->dim[1], f[2]);

	i = 0, j = f[0], pa = Aij(ws, 0, j);
	
	while (!(*pa & _MARKER1_))	/* Find Start of Marked Text */
	{	if (++j >= f[1])
		{	j = f[0], i++, pa = Aij(ws, i, j);
			if (i >= Lines())	break;	/* Bottom of Window */
		}
		else	pa++;
	}
	if (i >= Lines())
	{	b->used -= sizeof(TWFIELD);
		break;	/* Bottom of Window */
	}
	*pa &= ~_MARKER1_;

	fdef->home[0] = i, fdef->home[1] = j;
	fdef->dim[0] = (f[2] + fdef->dim[1] -1)/fdef->dim[1];
  }
  
  i = b->used / sizeof(TWFIELD);		/* Number of Fields */

  BUF_AppendItem(b, TWFIELD, &twfield0);	/* End indicator */


  	/* Copy now the Names into the Buffer associated to Window */
	
  b->offset = b->used;				/* Mark beginning of names */
  BUF_AppendItems(b, char, field_names.buf, field_names.used);
  
  	/* Indicate positions of names	*/
	
  fdef = (TWFIELD *)(b->buf);
  fname= b->buf + b->offset;		/* First name address */
  
  for (j = i; --j >= 0; fdef++)
  {	for (stored_address = &(fdef->name); 
             stored_address <= (&fdef->pic); stored_address++)
  		*stored_address = fname, 
  		fname += 1 + strlen(fname);
  }

  return(i);
}

/*==========================================================================
 *			tx_finish
 *==========================================================================*/
static int tx_finish(st)
/*+++
.PURPOSE Terminate Display: actions of \vfill's, non-terminated tables,
		retrieve marked fields.
.RETURNS st  
.REMARKS 
---*/
	int	st;	/* IN: Status as OK / NOK / EOF	*/
{
  ENTER("tx_finish");

  if (hfill_no)	tx_hfil();

	/* Insert supplementary blank lines	*/

  if(vfill_no > 0) 
  	tx_vfil(), vfill_no = 0;

	/* Get the Marked Lines */

  if (field_def.used)	get_marked(st);
  
  if ( (st == OK) && (depth_buf->offset > sizeof(int)))
	ERROR("Missing } or \\end");
	
  if (WindowIsActive)	ActiveWindow(ws);

  EXIT(st);
}

/*==========================================================================
 *			tx_option
 *==========================================================================*/
int tx_option(opt)
/*+++
.PURPOSE Change options.
.RETURNS Old options.
.REMARKS 
---*/
	int	opt;	/* IN: New option 0 = Don't Center	*/
{
	int	old_opt;
  
  old_opt = txopt;
  txopt = opt;
  return(old_opt);
}

/*==========================================================================
 *			tx_symbol
 *==========================================================================*/
char *tx_symbol(str)
/*+++
.PURPOSE Retrieve the translation of a definition
.RETURNS Pointer to the translated definition
.REMARKS 
---*/
	char 	*str;	/* IN: String to translate	*/
{
  return(tex_symbol(str));
}

int tx_def(symbol, synonym)
/*+++
.PURPOSE Set a symbol in the TeX definitions.
.RETURNS 1
.REMARKS 
---*/
	char 	*symbol;	/* IN: Symbol to define	*/
	char 	*synonym;	/* IN: Equivalence	*/
{
	char	*def, *p;
	
  tx_tex();			/* Install the TeX structure */

  def = MEM_GET(char, 12  + strlen(symbol) + strlen(synonym));
  
  p = def + strcopy(def, "\\def");
  if (*symbol != '\\')	*(p++) = '\\';
  p += strcopy(p, symbol);
  p += strcopy(p, "{");
  p += strcopy(p, synonym);
  p += strcopy(p, "}");
  
#if DEBUG
  LOG_ED_STRING("Added: ", def);
#endif
  status = TeX_Execute(&htex, def, strlen(def));
  MEM_FREE(def);
  
  return(1);
}

/*==========================================================================
 *			tx_display
 *==========================================================================*/
int tx_display(w, str, len, clear_option)
/*+++
.PURPOSE Display a text on the window.
.RETURNS --- OK (no remaining text)
	--- NOK (text did not fit on the window)
	--- EOF: the \EOF macro was fond in the text.
.REMARKS 
---*/
	WINDOW	*w;	/* IN: Window to echo the processed text */
	char	*str;	/* IN: text to display, or NULL (continuation )*/
	int	len;	/* IN: Length of text	*/
	int	clear_option;	/* IN: Option 1 to clear window \
					before display	*/
{
	register int 	status;

  ENTER("tx_display");

#if DEBUG
  TRACE_ED_STR2("String to display:", str, len);
#endif

  ws = w;
  tx_init(str, clear_option);


	/* Execute TeX. The routines tx_out collects the words
	 * for output, while tx_action is triggered for each action.
	 */

  status = TeX_Execute(&htex, str, len);

	/* Terminate: vfill adjustments, non-terminated tables,
	 * and retrieve marked fields
	 */

  tx_finish(status);

  EXIT(status);

}

/*==========================================================================
 *			tx_mdisplay
 *==========================================================================*/
int tx_mdisplay(w, str, nstrings, clear_option)
/*+++
.PURPOSE Display a text on the window made of several pieces of text.
	The pieces are provided by the array of strings str made of
	(2*nstrings) pointers, specifying nstrings times (start, end).
	(a pointer to the end of a string is the byte just following
	the last byte of the string, i.e. the address of the null character
	for a standard string)
.RETURNS --- OK (no remaining text)
	--- NOK (text did not fit on the window)
	--- EOF: the \EOF macro was fond in the text.
.REMARKS 
---*/
	WINDOW	*w;	/* IN: Window to echo the processed text */
	char	**str;	/* IN: Array of strings to display, \
				as n* (start, end) */
	int	nstrings; /* IN: Number of strings 	*/
	int	clear_option;	/* IN: Option 1 to clear window \
					before display	*/
{
	register int 	status;

  ENTER("tx_mdisplay");

#if DEBUG
  TRACE_ED_I("Number of string to display:", nstrings);
#endif

  ws = w;
  tx_init(str, clear_option);


	/* Execute TeX. The routines tx_out collects the words
	 * for output, while tx_action is triggered for each action.
	 */

  status = TeX_ExecuteArray(&htex, str, nstrings);

	/* Terminate: vfill adjustments, non-terminated tables,
	 * and retrieve marked fields
	 */

  tx_finish(status);

  EXIT(status);

}

/*==========================================================================
 *			tx_fdisplay
 *==========================================================================*/
int tx_fdisplay(w, fid, len, clear_option)
/*+++
.PURPOSE Display a file extract on the window.
.RETURNS --- OK (no remaining text)
	--- NOK (text did not fit on the window)
	--- EOF the \EOF macro was found in the text.
.REMARKS 
---*/
	WINDOW	*w;	/* IN: Window to echo the processed text	*/
	int	fid;	/* IN: File number (opened by fi_open)	*/
	int	len;	/* IN: Maximal number of bytes to read	*/
	int	clear_option;	/* IN: Option 1 to clear window \
					before display	*/
{
	register int 	status;

  ENTER("tx_fdisplay");

  ws = w;
  tx_init(blanks, clear_option);	/* Non-null char pointer */

	/* Load text to process */

  TeX_Load(&htex, fid, len);

	/* Execute TeX. The routines tx_out collects the words
	 * for output, while tx_action is triggered for each action.
	 */

  status = TeX_Continue(&htex);

	/* Terminate: vfill adjustments, non-terminated tables,
	 * and retrieve marked fields
	 */

  tx_finish(status);

  EXIT(status);

}

/*==========================================================================
 *			tx_file
 *==========================================================================*/
int tx_file(w, fname, clear_option)
/*+++
.PURPOSE Display a complete file extract on the window.
.RETURNS --- OK (no remaining text)
	--- NOK (text did not fit on the window)
	--- EOF the \EOF macro was found in the text.
.REMARKS 
---*/
	WINDOW	*w;	/* IN: Window to echo the processed text	*/
	char	*fname;	/* IN: File name 	*/
	int	clear_option;	/* IN: Option 1 to clear window \
					before display	*/
{
	register int 	status;

  ENTER("tx_file");

  ws = w;
  tx_init(fname, clear_option);	/* Non-null char pointer */

	/* Load text to process */

  TeX_Include(&htex, fname);

	/* Execute TeX. The routines tx_out collects the words
	 * for output, while tx_action is triggered for each action.
	 */

  status = TeX_Continue(&htex);

	/* Terminate: vfill adjustments, non-terminated tables,
	 * and retrieve marked fields
	 */

  tx_finish(status);

  EXIT(status);

}

/*==========================================================================
 *			tx_fields
 *==========================================================================*/
TWFIELD *tx_fields(w)
/*+++
.PURPOSE Retrieve the marked fields in a window.
.RETURNS Address of first TWFIELD / null pointer if no field was defined.
.REMARKS The last valid field is followed by a null name, i.e. (char *)0.
---*/
	WINDOW *w;	/* IN: Window concerned	*/
{
	TWFIELD *f;
	BUFFER  *b;

  ENTER("*tx_fields");

  f = NULL_PTR(TWFIELD);
  
  b = Fields(w);
  if (b)	
  	if (b->used)	f = (TWFIELD *)b->buf;
  
  EXIT_PTR(TWFIELD, f);
}

/*==========================================================================
 *			tx_more
 *==========================================================================*/
char *tx_more(name)
/*+++
.PURPOSE Retrieve the `more' (between \begin{more}{name} and \end{more})
.RETURNS Address of code / NULL if none
.REMARKS When `name' is NULL, return the NEXT more value as the
	two strings concatenated.
---*/
	char	*name;	/* IN: `more' name to find */
{
	char	*p;
	int	i, *pi;
	
  ENTER("*tx_more");

  if (!name)  {		/* Get the Next one */
	if (oMore.offset >= oMore.used) {
		oMore.offset = 0;
		EXIT_PTR(char, 0);	/* Indicates End */
	}
	pi = (int *)(oMore.buf + oMore.offset);
	oMore.offset += sizeof(int);
	p = More.buf + *pi;
	EXIT_PTR(char, p);
  }
  for (i=SET_Items(&oMore, int), pi=SET_FirstItem(&oMore, int); --i >= 0; pi++)
  {
	p = More.buf + *pi;
	if (strcomp(name, p) == 0)	break;		/* Topic Found */
  }
  if (i < 0)	p = NULL_PTR(char);
  else		p += 1 + strlen(name);

  
  EXIT_PTR(char, p);
}

/*==========================================================================
 *			tx_math
 *==========================================================================*/
int tx_math(f)
/*+++
.PURPOSE Define an action routine in case of mathematical symbols
	($ ^ _)
.RETURNS OK 
.REMARKS Use NULL_FCT to take standard action (ignore)
---*/
	int (*f)();	/* IN: Function with arguments (string, length) \
			  to call when $ ^ _ are encountered */
{
  external_action = f;
  
  return(OK);
}

/*==========================================================================
 *			tx_out
 *==========================================================================*/
int tx_out(str, len)
/*+++
.PURPOSE This is the output routine.
.RETURNS Number of bytes processed.
.REMARKS 
---*/
	char 	*str;		/* text to output	*/
	int	len;		/* Length of text	*/
{
	int 	output_bytes, j, m;

/*  ENTER("tx_out"); */

#if DEBUG
	TRACE_ED_STR2("Output=>",str,len);
#endif

  if (inaux)
  {	BUF_AppendItems(inaux, char, str, len);
  	j = inaux->used;	/* Save */
  	BUF_AppendItem(inaux, char, &zero);
  	inaux->used = j;	/* Restore */
  	return(len);
  }

  output_bytes = 0;
  if (len < 0)		FINISH;
  tx_set();

  j = CurrentColumn();
  
  if (j < pd->col_lim[0]) {		/* Position at Left Margin 	*/
	if (pd->flags & AnchoredPosition) ;
	else	ws->pos += pd->col_lim[0] - j,
  		j = pd->col_lim[0];
  }
  else	pd->flags &= ~AnchoredPosition;

  if (BottomReached())	FINISH;

  if ((htex.mode != _TeX_VERBATIM_) && (j + len > pd->col_lim[1]))
  				/* Doesn't fit on the line ... */
  {	tx_smart();
  	if (blank_lines == 0)	tx_nl();
	if (BottomReached())	FINISH;
	j = pd->col_lim[0];
	ws->pos += j;
  }
  m = ws->pos;				/* Position for marker */
  output_bytes = Write(ws, str, len);

  if (output_bytes > 0)
  {	last_issued_char = *(str+output_bytes-1);
	blank_lines = 0;	rule_lines = 0;
	if ((j + len) == Columns()) 	/* On a new line... */
		blank_lines++, last_issued_char = ' ';
	if (pd->flags & InField)
		Mark(m, output_bytes);
  }
 
  FIN:
 /* EXIT(output_bytes); */
  return(output_bytes); 
}

/*==========================================================================
 *			tx_action
 *==========================================================================*/
int tx_action(str, len)
/*+++
.PURPOSE This is the action routine.
.RETURNS Number of bytes processed.
.REMARKS 
---*/
	char 	*str;		/* text to output	*/
	int	len;		/* Length of text	*/
{
	char	*p, *pe;
	int	i, j;
	unsigned char old_attr;
	MID_STATIC  char fill_error[] = "Too many \\vfill's";
	MID_STATIC  char no_math_action[] = "`$' ignored";
	MID_STATIC  char bad_action[] = "`$' action stopped";

  /* ENTER("tx_action"); */

#if DEBUG
	TRACE_ED_STR2("Action: ",str,len);
#endif

  p = str, pe = str+len;
  tx_set();

  for (; p < pe; p++) switch(*p)
  { case '0':		/* begin / end Environment */
	if (*htex.ap == _TeX_BEGIN_)	/* New env. */
  		if_not(open_env())	FINISH;
		else;
	else	if (*htex.ap == _TeX_END_)	/* Close env. */
  		if_not(close_env())	FINISH;
		else;
	else;
	break;

    case '{' :		/* New Depth */
	if (BottomReached())	FINISH;
	open_depth();
	break;

    case '}':		/* Old depth	*/
	close_depth();
	break;

    case '\0': case '\\': /* NUL's may be found after macro substitutions */
    	continue;

    case '$': case '^': case '_':	/* Mathematical symbols */
	if (external_action)
	{	if_not(i = (*external_action)(p, pe-p))
			FINISH;
		p += i-1;
	}
	else	no_math_action[1] = *p, WARNING(no_math_action);
    	break;

    case ' ':		/* Stretchable blank */
  	if(inaux)	
  	{	if ((inaux->used) && (inaux->buf[inaux->used-1] != ' '))
  			tx_out(blanks, 1);
  	}
  	else		tx_blank();	/* Always possible   */
	break;

    case '\r':		/* New Line	*/
    case_newline:
  	if (inaux)	{ tx_out("\n", 1); break; }
	if (blank_lines == 0)	tx_nl() ;
	if (pd->env == 't')	tx_set(), nltab();
	break;

    case '\n':		/* New paragraph */
  	if(inaux)	{ tx_out("\n", 1); break; }
	while (blank_lines <= 1)
	{	if (BottomReached())	break;
		tx_nl() ;
	}
	break;

    case '/':		/* Skip Line */
  	if(inaux)	{ tx_out("\n", 1); break; }
	if(blank_lines == 0)	tx_nl() ;
	tx_nl() ;
	break;

    case '&': 		/* Tab in Tabular, or `tab' outside	*/
  	if(inaux)	{ tx_out("&", 1); break; }
    	if (pd->env == 't')
    	{
		if (CurrentColumn() > pd->col_lim[0])
			tx_smart(),
			pd->line_lim[1] += 1;
		pd0->line_lim[1] = MAX(pd0->line_lim[1], pd->line_lim[1]);

    		if (hfill_no)	tx_hfil();	/* Horizontal Fill */
		j = pd0->col_no;		/* save in case bad & */
    		pd0->col_no += pd0->col_i;	/* Increment column */
    		pd0->col_i  = 1;
    		if (pd0->col_no >= pd0->cols) {
		    WARNING("Too many &'s"); 
		    pd0->col_no  = j;		/* Restore ...	*/
		    goto case_newline; 
		}
		tx_set();
		ws->pos = Position(pd0->line_lim[0], pd->col_lim[0]);
		break;
    	}
    case 't':	/* Put a Tab */
  	if(inaux)	{ tx_out("\t", 1); break; }
  	tx_blank();			/* At least one blank 	*/
	tabright(blanks, 0);		/* Go to next tab	*/
    	break;

    case 'b':
    	Bell();	/* Ring Bell	*/
    	break;

    case 'f':			/* Fill vertical or horizontal */
  	if(inaux)	break; 
	fill_error[10] = *++p;
    	if (*p == 'v')	/* Vertical Fill	*/
	{	if (vfill_no >= MAX_MARKED)
		{	ERROR(fill_error);
			continue;
		}
		if(blank_lines == 0)	tx_nl() ;
		vfill[vfill_no++] = CurrentLine();
	}
	else			/* Horizontal Fill	*/
	{	if (hfill_no >= MAX_MARKED)
		{	ERROR(fill_error);
			continue;
		}
		hfill[hfill_no++] = CurrentColumn();
	}
	continue;

    case 'F':			/* Mark Field  or Redefine Columns */
  	if(inaux)	break;
	if (BottomReached())	FINISH;
	switch(*++p)
    	{ case 's':	tx_mark();	/* Start */
    		break;
    	  case 'e':			/* End Field */
		if (marked == 0)	/* Null Field: At least one byte! */
			tx_out(blanks, 1);
		BUF_AppendItem(&field_def, short, &marked);	/* Length */
		pd->flags &= ~InField;
    		break;
	  case 't':			/* Redefine Columns in a Table */
		close_table();
  		close_depth(), 	open_depth();
		j = ws->pos;
  		if ((rule_lines) && (ws->pos >= ws->dim[1]))
  			ws->pos -= ws->dim[1];
		open_table();
		tx_set();
		if(rule_lines)	tx_rule();
		else		nltab();
		ws->pos = j;
    		break;
	}
	break;

    case 'J':	/* Justification specifications */
	if (BottomReached())	FINISH;
    	switch(*++p)
    	{ case 'l': pd->smart = _LEFT_;		break;
    	  case 'r': pd->smart = _RIGHT_;	break;
    	  case 'c': pd->smart = _CENTER_;	break;
    	  case 'j': pd->smart = _JUSTIFY_;	break;
	  case 'f': pd->flags |= ForceJustification;	break;
	  case 't': inaux = &aux; BUF_Clear(inaux);	break; /* Justfy Tab */
	  case '0': /* End Tab   */
		if (tabright(aux.buf, aux.used) != aux.used)
			{ --p; FINISH; }
		inaux = NULL_PTR(BUFFER);
	  	break;
    	  default : tx_smart();		break;
    	}
    	break;

    case 'M':	/* Move Margins */
  	if(inaux)	break;
    	switch(*++p)
    	{ case 'l': pd->col_lim[0] += MARGIN;		break;
    	  case 'r': pd->col_lim[1] -= MARGIN;		break;
    	  case 'L': pd->col_lim[0] -= MARGIN;		break;
    	  case 'R': pd->col_lim[1] += MARGIN;		break;
	  case 'i': 		/* Item */
		if_not(tx_item())	{ p--; FINISH;}
		break;
	  case 'I': 		/* indent */
		tx_out(blanks, MARGIN);
		continue;
	  case 'h': 		/* hspace */
		tex_getparm(1);	i = atoi(htex.ap); j = CurrentColumn();
		if (j < pd->col_lim[0])	j = pd->col_lim[0];
		if (i < 0) {	/* Left Move */
			j += i;	
			if (j < 0)	j = 0;
			i = CurrentLine();
			ws->pos = Position(i,j);
			pd->flags |= AnchoredPosition;
			continue;
		}
	  	for ( ; i > 0; i--)
	  	{	if_not(tx_out(blanks, 1))	FINISH;
			j = MIN(pd->col_lim[1] - CurrentColumn(), i-1);
			if (j > 0)
			{	j = MIN(j, sizeof(blanks));
				tx_out(blanks, j);
				i -= j;
			}
	  	}
		continue;
	  case 'V': 		/* Test remaining lines */
		tex_getparm(1);
	  	i = atoi(htex.ap);
	  	if (i >= Lines())	continue;
		if ((ws->pos + i*Columns()) <= ws->marker[1])
					continue;
		p = str;	/* Stop here, tx_action will return 0 */
		FINISH;	
	  case 'H': 		/* Test remaining cols */
		tex_getparm(1);
	  	i = atoi(htex.ap);
		if ((CurrentColumn()+i)	> pd->col_lim[1])
				tx_nl();
		continue;
    	}
	if (pd->col_lim[0] < 0)
		pd->col_lim[0] = 0, ERROR("Negative Left Margin");
	if (pd->col_lim[1] > Columns())
		pd->col_lim[1] = Columns(), ERROR("Bad Right Margin");
    	continue;

    case 'm':	/* Multicolumn */
  	if(inaux)	break;
	if (BottomReached())	FINISH;
	p += multicol(p);
    	break;

    case 'r':
  	if(inaux)	break;
    	tx_crule();	tx_set();
    	continue;
    case 'R':
  	if(inaux)	break;
    	tx_rule();	tx_set();
    	continue;

    case 'V':	/* Video Attributes */
  	if(inaux)	break;
	old_attr = pd->attr;	/* Current Attribute */
    	switch(*++p)
    	{ case 'B': i = old_attr | _BOLD_;	break;
    	  case 'b': i = old_attr | _BLINK_;	break;
    	  case 'u': i = old_attr | _UNDERSCORE_;break;
    	  case 'r': i = old_attr | _REVERSE_;	break;
    	  case 'R': i = old_attr ^ _REVERSE_;	break;
    	  case 'n': i = 0;			break;
	  default : i = old_attr;		break;
    	}
	j = CurrentColumn();
	pd->attr = i;
	if (old_attr != pd->attr)	SetAttr(ws, pd->attr);
	if (j <= pd->col_lim[0])	current_blank = ws->attr;
    	break;

    default:
	bad_action[1] = *p, WARNING(bad_action);
	p = str - 1;		/* Means EOF */
	FINISH;
  }
 
  FIN:
  /* EXIT(p-str); */
  return(p-str);
}

/*==========================================================================
 *			tx_list
 *==========================================================================*/
int tx_list()
/*+++
.PURPOSE List, in the Log File, the list of macros
.RETURNS Number of non-uused symbols
.REMARKS For debugging purposes.
---*/
{
	int n;

  ENTER("+tx_list");

  n = tex_list(&htex);

  EXIT(n);
}
