/*
 * Commandes nroff
 * (c)1991 par Denis GOUNELLE
 */

#include "aroff.h"
#include "pile.h"

extern unsigned char *TestString(), *TestNum() ;
extern unsigned char Arg[], OutputBuf[], TabChar, *GetArg(), TitleSeq[] ;
extern long ArgLen, OutputLen, AdjustMode, PageNumber, InputMode, LineLen,
	    Indent, TitleLen, Flg, TmpIndent, PageOffset, PageLen, LineSpacing,
	    TabLen, TmpCenter, LineNumber, NumInterv, NumSpace, NumIndent,
	    TmpNoNum, OldInputMode, OutputLine, EmptyToWrite, FindTrp(),
	    NewPageNumber, CtrlLen ;
extern struct InputFile *CurrentInputFile, *NewFile() ;
extern struct Macro *CurrentMacro ;
extern struct TeteListe TMac ;
extern struct Node *FindVar() ;

static unsigned char tmp[LGMAXSTR+1], TrTab[LGMAXSTR+1] = "" ;

static char *TableEsc[] =
{
  "\033[22m" ,  /* b0 */
  "\033[1m"  ,  /* b1 */
  "\033[23m" ,  /* i0 */
  "\033[3m"  ,  /* i1 */
  "\033[24m" ,  /* u0 */
  "\033[4m"  ,  /* u1 */
  "\033[0m"     /* n  */
} ;

/***********************************************************************/

void do_br()
{
  if ( Flg & F_NOBRK ) return ;

  if ( OutputLen )
  {
    if ( AdjustMode != AM_BOTH ) AdjustLine() ;
    WriteLine() ;
  }
}

/***********************************************************************/

void do_tm()
{
  if ( ArgLen > 0 )
  {
    write( 2 , Arg , ArgLen ) ;
    write( 2 , "\n" , 1 ) ;
  }
}

/***********************************************************************/

void do_ab()
{
  do_tm() ;
  Abort( 1 ) ;
}

/***********************************************************************/

void do_ad()
{
  long oldmode ;
  unsigned char c ;

  c = Arg[0] ;
  oldmode = AdjustMode ;

  switch ( c )
  {
    case 'c'  : AdjustMode = AM_CENTER ; break ;
    case 'l'  : AdjustMode = AM_LEFT   ; break ;
    case 'r'  : AdjustMode = AM_RIGHT  ; break ;
    case 'b'  : AdjustMode = AM_BOTH   ; break ;
    case '\0' : AdjustMode = Pop( TE_ADJMOD , 0 ) ;
		if ( AdjustMode == -1 ) AdjustMode = AM_BOTH ;
		break ;
    default   : Fatal( ERR_SYNTAX ) ;
  }

  Push( TE_ADJMOD , oldmode ) ;
}

/***********************************************************************/

void do_am()
{
  struct Macro *m ;

  if ( InputMode & IM_EXMACRO ) Fatal( ERR_SYNTAX ) ;

  if ( ! ArgLen ) return ;
  m = (struct Macro *)FindVar( &TMac , Arg ) ;
  if ( ! m )
  {
    SetMac( Arg ) ;
    m = CurrentMacro ;
  }

  m->m_NextL = NULL ;
  CurrentMacro = m ;
  OldInputMode = InputMode ;
  BCLR( InputMode , IM_FILLING ) ;
  BSET( InputMode , IM_RDMACRO ) ;
}

/***********************************************************************/

void do_as()
{
  register long k ;
  register unsigned char *var, *val ;

  if ( ! ArgLen ) return ;

  /* isole le nom de la chaine */

  var = Arg ;
  for ( k = 0 ; (Arg[k] != '\0') && (! isspace( Arg[k] )) ; k++ ) ;
  if ( Arg[k] == '\0' ) return ;
  Arg[k] = '\0' ;

  /* recupere la valeur */

  for ( k++ ; isspace( Arg[k] ) ; k++ ) ;
  if ( Arg[k] == '\0' ) return ;
  if ( Arg[k] == '"' ) k++ ;
  val = &Arg[k] ;
  if ( *val == '\0' ) return ;

  /* fabrique la nouvelle valeur */

  GetStr( var , tmp ) ;
  strcat( tmp , val ) ;
  SetStr( var , tmp ) ;
}

/***********************************************************************/

void do_bp()
{
  do_br() ;

  if ( OutputLine > 1 ) EmptyToWrite += PageLen - OutputLine + 1 ;
  if ( isdigit( Arg[0] ) ) NewPageNumber = atoi( Arg ) ;
  if (! (Flg & F_SORTIE)) ChangePageNumber( NewPageNumber ) ;
}

/***********************************************************************/

void do_ce()
{
  do_br() ;
  TmpCenter = ( isdigit( Arg[0] ) ) ? atoi( Arg ) : 1 ;
}

/***********************************************************************/

void do_de()
{
  if ( InputMode & IM_EXMACRO ) Fatal( ERR_SYNTAX ) ;

  if ( ! ArgLen ) return ;
  SetMac( Arg ) ;
  OldInputMode = InputMode ;
  BCLR( InputMode , IM_FILLING ) ;
  BSET( InputMode , IM_RDMACRO ) ;
}

/***********************************************************************/

void do_ds()
{
  register long k ;
  register unsigned char *var, *val ;

  if ( ! ArgLen ) return ;

  /* isole le nom de la chaine */

  var = Arg ;
  for ( k = 0 ; (Arg[k] != '\0') && (! isspace( Arg[k] )) ; k++ ) ;
  if ( Arg[k] == '\0' ) return ;
  Arg[k] = '\0' ;

  /* recupere la valeur */

  for ( k++ ; isspace( Arg[k] ) ; k++ ) ;
  if ( Arg[k] == '\0' ) return ;
  if ( Arg[k] == '"' ) k++ ;
  val = &Arg[k] ;
  if ( *val == '\0' ) return ;

  SetStr( var , val ) ;
}

/***********************************************************************/

void do_el()
{
  if (! (Flg & F_WASIF)) Fatal( ERR_IFELSE ) ;
  if (! ArgLen) Fatal( ERR_SYNTAX ) ;
  if ( Flg & F_WASFALSE )
  {
    sprintf( tmp , "%s\n" , Arg ) ;
    NewString( tmp ) ;
  }
}

/***********************************************************************/

void do_ex()
{
  Abort( 1 ) ;
}

/***********************************************************************/

void do_fi()
{
  do_br() ;
  BSET( InputMode , IM_FILLING ) ;
}

/***********************************************************************/

void do_fl( str )
unsigned char *str ;
{
  register long k,l ;
  register unsigned char *p, *q ;

  k = strlen( str ) ;
  if ( k < 1 ) Fatal( ERR_WRITE ) ;

  for ( q = str ; *q ; q++ )
  {
    if ( *q == SC_FIXSPC ) *q = ' ' ;
    for ( l = 0 ; TrTab[l] ; l += 2 ) /* do translation */
      if ( *q == TrTab[l] )
      {
	*q = TrTab[l+1] ;
	break ;
      }
  }

  write( 1 , str , k ) ;
}

/***********************************************************************/

void do_fs()
{
  register long k ;
  register unsigned char *p, *q ;

  if ( ! ArgLen ) return ;

  for ( p = Arg ; *p ; p++ )
  {
    switch ( *p )
    {
      case 'b' : k = 0 ; break ;
      case 'i' : k = 2 ; break ;
      case 'u' : k = 4 ; break ;
      case 'n' : k = 6 ; break ;
      default  : Fatal( ERR_SYNTAX ) ; break ;
    }

    if ( k != 6 )
    {
      p++ ;
      if ( *p == '1' ) k++ ;
      else if ( *p != '0' ) Fatal( ERR_SYNTAX ) ;
    }

    for ( q = TableEsc[k] ; *q ; q++ )
    {
      CtrlLen++ ;
      PutChar( *q ) ;
    }
  }

  BSET( Flg , F_CONTINUE ) ;
}

/***********************************************************************/

void do_if()
{
  register unsigned char *p ;

  if (! ArgLen) Fatal( ERR_SYNTAX ) ;

  p = Arg ;
  BSET( Flg , F_WASIF ) ;
  BCLR( Flg , (F_IFNOT|F_WASFALSE) ) ;

  if ( *p == '!' )
  {
    BSET( Flg , F_IFNOT ) ;
    p++ ;
    while ( isspace(*p) ) p++ ;
  }

  switch ( *p )
  {
    case 'e'  : if ( PageNumber & 1 ) BSET( Flg , F_WASFALSE ) ; p++ ; break ; 
    case 'o'  : if (! (PageNumber & 1)) BSET( Flg , F_WASFALSE ) ; p++ ; break ;
    case 'n'  : p++ ; break ;
    case 't'  : BSET( Flg , F_WASFALSE ) ; p++ ; break ;
    case '\'' : p = TestString( p ) ; break ;
    default   : if (! isdigit(*p)) Fatal( ERR_SYNTAX ) ;
                p = TestNum( p ) ; break ;
                break ;
  }

  if ( Flg & F_IFNOT ) 
    if ( Flg & F_WASFALSE )
      BCLR( Flg , F_WASFALSE ) ;
    else
      BSET( Flg , F_WASFALSE ) ;

  while ( isspace(*p) ) p++ ;
  if (! *p) Fatal( ERR_SYNTAX ) ;
  sprintf( tmp , "%s\n\\\n" , p ) ;
  if (! (Flg & F_WASFALSE)) NewString( tmp ) ;
}

/***********************************************************************/

void do_in()
{
  long oldval ;

  do_br() ;
  oldval = Indent ;
  if (! ChangeValue( &Indent )) Indent = Pop( TE_INDENT , 0 ) ;
  if ( Indent < 0 ) Indent = DEF_INDENT ;
  Push( TE_INDENT , oldval ) ;
}

/***********************************************************************/

void do_ll()
{
  long oldval ;

  oldval = LineLen ;
  if (! ChangeValue( &LineLen )) LineLen = Pop( TE_LINLEN , 0 ) ;
  if ( LineLen < 0 ) LineLen = DEF_LINLEN ;
  Push( TE_LINLEN , oldval ) ;
}

/***********************************************************************/

void do_ls()
{
  long oldval ;

  oldval = LineSpacing ;
  if (! (LineSpacing = atoi( Arg ))) LineSpacing = Pop( TE_LINSPC , 0 ) ;
  if ( LineSpacing < 1 ) LineSpacing = DEF_LINSPC ;
  Push( TE_LINSPC , oldval ) ;
}

/***********************************************************************/

void do_lt()
{
  long oldval ;

  oldval = TitleLen ;
  if (! ChangeValue( &TitleLen )) TitleLen = Pop( TE_TITLEN , 0 ) ;
  if ( TitleLen < 0 ) TitleLen = DEF_TITLEN ;
  Push( TE_TITLEN , oldval ) ;
}

/***********************************************************************/

void do_na()
{
  Push( TE_ADJMOD , AdjustMode ) ;
  AdjustMode = AM_LEFT ;
}

/***********************************************************************/

void do_ne()
{
  register long k, l ;

  if ( ! ArgLen ) return ;
  k = OutputLine + (( isdigit( Arg[0] ) ) ? atoi( Arg ) : 1) - 1 ;

  if ( k > PageLen )
  {
_do_it:
    *Arg = '\0' ;
    ArgLen = 0 ;
    do_bp() ;
    return ;
  }

  for ( l = OutputLine ; l <= k ; l++ )
    if ( FindTrp( l ) ) goto _do_it ;
}

/***********************************************************************/

void do_nf()
{
  do_br() ;
  BCLR( InputMode , IM_FILLING ) ;
}

/***********************************************************************/

void do_nm()
{
  unsigned char *p ;

  if (! ArgLen)
  {
    BCLR( Flg , F_NUMBER ) ;
    return ;
  }

  BSET( Flg , F_NUMBER ) ;
  ChangeValue( &LineNumber ) ;
  for ( p = Arg ; (*p != '\0') && (! isspace( *p )) ; p++ ) ;
  p = GetArg( p , &NumInterv ) ;
  if (! NumInterv) Fatal( ERR_SYNTAX ) ;
  p = GetArg( p , &NumSpace ) ;
  GetArg( p , &NumIndent ) ;
}

/***********************************************************************/

void do_nn()
{
  TmpNoNum = ( isdigit( Arg[0] ) ) ? atoi( Arg ) : 1 ;
}

/***********************************************************************/

void do_nr()
{
  register long k, val, inc ;
  register unsigned char *var ;

  if ( ! ArgLen ) return ;

  /* isole le nom du registre */

  var = Arg ;
  for ( k = 0 ; (Arg[k] != '\0') && (! isspace( Arg[k] )) ; k++ ) ;
  if ( Arg[k] == '\0' ) return ;
  Arg[k] = '\0' ;

  /* recupere la valeur initiale */

  for ( k++ ; isspace( Arg[k] ) ; k++ ) ;
  val = atoi( &Arg[k] ) ;

  /* recupere l'increment */
  while ( isdigit( Arg[k] ) ) k++ ;
  while ( isspace( Arg[k] ) ) k++ ;
  inc = atoi( &Arg[k] ) ;

  SetReg( var , val , inc ) ;
}

/***********************************************************************/

void do_pl()
{
  if (! ChangeValue( &PageLen )) PageLen = DEF_PAGLEN ;
  UpdateTrp() ;
}

/***********************************************************************/

void do_pm()
{
  register struct Macro *m ;
  register struct MLine *l ;

  m = (struct Macro *) (( ArgLen ) ? FindVar( &TMac , Arg ) : NULL) ;

  if ( m )
    for ( l = (struct MLine *)m->m_Def.tl_Premier ;
	  l ;
	  l = (struct MLine *)l->ml_Node.el_Suivant )
    {
      write( 1 , l->ml_Text , l->ml_Len ) ;
      write( 1 , "\n" , 1 ) ;
    }
  else for ( m = (struct Macro *)TMac.tl_Premier ;
	     m ;
	     m = (struct Macro *)m->m_Node.el_Suivant )
  {
    printf( "Macro %s : \n" , m->m_Name ) ;
    fflush( stdout ) ;
    for ( l = (struct MLine *)m->m_Def.tl_Premier ;
	  l ;
	  l = (struct MLine *)l->ml_Node.el_Suivant )
    {
      write( 1 , l->ml_Text , l->ml_Len ) ;
      write( 1 , "\n" , 1 ) ;
    }
  }
}

/***********************************************************************/

void do_pn()
{
  long oldval ;

  oldval = PageNumber ;
  if (! ChangeValue( &oldval )) return ;
  if ( oldval > 1 ) NewPageNumber = oldval ;
}

/***********************************************************************/

void do_po()
{
  long oldval ;

  oldval = PageOffset ;
  if (! ChangeValue( &PageOffset )) PageOffset = Pop( TE_PAGOFS , 0 ) ;
  if ( PageOffset < 0 ) PageOffset = DEF_PAGOFS ;
  Push( TE_PAGOFS , oldval ) ;
}

/***********************************************************************/

void do_rm()
{
  if ( ArgLen ) RemMac( Arg ) ;
}

/***********************************************************************/

void do_rr()
{
  if ( ArgLen ) RemReg( Arg ) ;
}

/***********************************************************************/

void do_rs()
{
  if ( ArgLen ) RemStr( Arg ) ;
}

/***********************************************************************/

void do_rt()
{
  long lig, k ;

  if (! ArgLen) return ;
  lig = 0 ;
  if (! ChangeValue( &lig )) return ;

  RemTrp( lig ) ;
}

/***********************************************************************/

void do_so()
{
  if ( ! ArgLen ) Fatal( ERR_SYNTAX ) ;
  Push( TE_INFILE , CurrentInputFile ) ;

  CurrentInputFile = NewFile( Arg ) ;
  if ( CurrentInputFile ) SetStr( "fn" , CurrentInputFile->if_Name ) ;
}

/***********************************************************************/

void do_sp()
{
  do_br() ;
  EmptyToWrite += ( isdigit( Arg[0] ) ) ? atoi( Arg ) : 1 ;
}

/***********************************************************************/

void do_ta()
{
  long oldval ;

  oldval = TabLen ;
  if (! ChangeValue( &TabLen )) TabLen = oldval ;
}

/***********************************************************************/

void do_tc()
{
  TabChar = ( Arg[0] == '\0' ) ? DEF_TABCHR : Arg[0] ;
}

/***********************************************************************/

void do_ti()
{
  do_br() ;
  TmpIndent = myatoi() ;
  BCLR( Flg , F_NOTI ) ;
}

/***********************************************************************/

void do_tl()
{
  long i ;
  register long k, l ;
  unsigned char *centre, *droit ;
  register unsigned char delim, *gauche, *p ;

  if ( ! ArgLen ) return ;

  /* recupere les trois morceaux */

  delim = Arg[0] ;

  gauche = &Arg[1] ;
  while ( (*gauche != delim) && (*gauche != '\0') ) gauche++ ;
  if ( *gauche == '\0' ) Fatal( ERR_SYNTAX ) ;
  *gauche = '\0' ;
  gauche++ ;

  centre = gauche ;
  while ( (*gauche != delim) && (*gauche != '\0') ) gauche++ ;
  if ( *gauche == '\0' ) Fatal( ERR_SYNTAX ) ;
  *gauche = '\0' ;
  gauche++ ;

  droit = gauche ;
  while ( (*gauche != delim) && (*gauche != '\0') ) gauche++ ;
  if ( *gauche == '\0' ) Fatal( ERR_SYNTAX ) ;
  *gauche = '\0' ;

  gauche = &Arg[1] ;

  /* calcule l'intervalle et pond le resultat */

  p = tmp ;
  i = TitleLen - strlen( centre ) ;
  k = i >> 1 ;
  for ( l = 0 ; l < PageOffset ; l++ , p++ ) *p = ' ' ;
  if ( k > 0 )
  {
    strcpy( p , TableEsc[6] ) ; /* repasse en normal */
    while ( *p ) p++ ;
    for ( l = 0 ; TitleSeq[l] ; l++ )
    {
      switch( TitleSeq[l] )
      {
	case 'b' : strcpy( p , TableEsc[1] ) ; break ;
	case 'i' : strcpy( p , TableEsc[3] ) ; break ;
	case 'u' : strcpy( p , TableEsc[5] ) ; break ;
	default  : Fatal( ERR_SYNTAX ) ;
      }
      while ( *p ) p++ ;
    }
    strcpy( p , gauche ) ;
    l = strlen( gauche ) ;
    p += l ;
    if ( i & 1 ) l-- ;
    for ( l = k - l ; l > 0 ; l-- , p++ ) *p = ' ' ;
    strcpy( p , centre ) ;
    p += strlen( centre ) ;
    for ( l = k - strlen( droit ) ; l > 0 ; l-- , p++ ) *p = ' ' ;
    strcpy( p , droit ) ;
    p += strlen( droit ) ;
  }

  strcpy( p , TableEsc[6] ) ; /* repasse en normal */
  strcat( p , "\n" ) ;
  EmptyToWrite += LineSpacing - 1 ;
  LigneSuiv( tmp ) ;
}

/***********************************************************************/

void do_tr()
{
  if (! ArgLen) return ;
  strcpy( TrTab , Arg ) ;
  if ( ArgLen & 1 ) strcat( TrTab , " " ) ;
}

/***********************************************************************/

void do_ts()
{
  if ( ! ArgLen ) return ;
  strcpy( TitleSeq , Arg ) ;
}

/***********************************************************************/

void do_wh()
{
  long lig, k ;
  unsigned char *p ;

  if (! ArgLen) return ;
  lig = 0 ;
  if (! ChangeValue( &lig )) return ;

  for ( p = Arg ; (*p) && (! isspace( *p )) ; p++ ) ;
  while ( isspace( *p ) ) p++ ;
  k = strlen( p ) ;
  if ( (k != 1) && (k != 2) ) Fatal( ERR_SYNTAX ) ;
  SetTrp( lig , p ) ;
}

