/*
 * Programme principal
 * (c)1991 par Denis GOUNELLE
 *
 * Usage : aroff [-wstack] [-l] file
 * L'option -w permet d'augmenter la taille de la pile interne (256 par defaut)
 * L'option -l provoque le chargement du fichier en mémoire avant traitement
 * Si "file" est "-", l'entree standard est utilisee
 */

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

static char PourVersion[] = "$VER: ARoff 1.12 (29.11.91)" ;

char *ARoff_Version = "ARoff v1.12 (c)1991 by Denis GOUNELLE" ,
     *ARoff_Usage   = "Usage : aroff [-wstack] [-l] file" ;

unsigned char cmd[LGMAXSTR+1] ,
	      Arg[LGMAXSTR+1] ,
	      TitleSeq[LGMAXSTR+1] ,
	      TabChar = DEF_TABCHR ,
	      OutputBuf[LGMAXBUF] ; /* tampon de sortie */

long OutputLen ,		/* nb de car. dans OutputBuf[] */
     ArgLen ,			/* nb de car. dans Arg[] */
     Flg ,			/* divers indicateurs */
     *Pile	 = NULL ,
     TaillePile  = DEF_PILE ,
     OutputLine  = 1 ,		/* no ligne dans la page courante */
     InputMode	 = IM_FILLING , /* indicateur pour lecture */
     AdjustMode  = AM_BOTH ,	/* type d'ajustement */
     PageNumber  = 0 ,
     NewPageNumber = DEF_PAGNUM ,
     LineLen	 = DEF_LINLEN ,
     LineSpacing = DEF_LINSPC ,
     TitleLen	 = DEF_TITLEN ,
     PageLen	 = DEF_PAGLEN ,
     PageOffset  = DEF_PAGOFS ,
     Indent	 = DEF_INDENT ,
     TabLen	 = DEF_TABLEN ,
     LineNumber  = DEF_LINNUM ,
     NumInterv	 = 1 ,
     NumSpace	 = 1 ,
     NumIndent	 = 0 ,
     TotalIndent = 0 ,
     TmpIndent	 = 0 ,
     TmpCenter	 = 0 ,
     TmpNoNum	 = 0 ,
     EmptyToWrite= 0 ,
     OldInputMode ,
     CtrlLen ,			/* lg ajoutée par ".fs"             */
     TmpLineLen ;		/* lg que doit avoir ligne courante */

struct TeteListe TReg, TStr, TMac, TTrp ;
struct InputFile *CurrentInputFile = NULL ;
struct Contexte *LastContext = NULL ;
struct String *CurrentString = NULL ;
struct Macro *CurrentMacro = NULL ;

extern struct InputFile *NewFile() ;

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

void Traite()
{
  register long c, d ;
  unsigned char e, tmp[LGMAXSTR+1], aux[LGMAXSTR+1] ;

  Flg &= F_LOADED ; /* raz tous les bits sauf F_LOADED */

  OutputLen = CtrlLen = 0 ;
  BSET( Flg , (F_WASNL|F_NOTI) ) ;

  for (;;)
  {
    BCLR( Flg , F_BREAKED ) ;
    TotalIndent  = Indent + TmpIndent ;
    TmpLineLen	 = LineLen + CtrlLen - TotalIndent ;

#ifdef AZTEC_C
    if ( SetSignal( 0L , 0L ) & SIGBREAKF_CTRL_C )
    {
      fprintf( stderr , "\n***BREAK\n" ) ;
      Termine() ;
    }
#endif

    /*
     * Boucle de lecture :
     *	  - on interprete \x
     *	  - on detecte et traite ".xx" en debut de ligne
     *	  - quand on sort, on doit traiter les "OutputLen" caracteres
     *	    places dans "OutputBuf[]"
     */

    while ( (c = GetChar()) != EOF )
    {
      if ( (c == '\n') && (! (InputMode & IM_EXMACRO)) )
      {
	CurrentInputFile->if_Line++ ;
	SetReg( "il" , CurrentInputFile->if_Line , 0 ) ;
      }

      if ( Flg & F_WASBS )
      {
	switch ( c )
	{
	  case 't'  : c = '\t'      ; break ;
	  case ' '  : c = SC_FIXSPC ; break ;
	  case '!'  : if (! OutputLen) BSET( InputMode , IM_TRANSP ) ;
		      for ( c = 0 ; c < TotalIndent ; c++ )
			PutChar( (char)' ' ) ;
	  case '\n' : c = SC_IGNORE ;
		      break ;
	  case 'n'  : e = '\0' ;
	  case '*'  : d = GetChar() ;
		      if ( (c == 'n') && ((d == '+') || (d == '-')) )
		      {
			e = d ;
			d = GetChar() ;
		      }

		      if ( d != '(' )
		      {
			if ( d == EOF ) Fatal( ERR_SYNTAX ) ;
			tmp[0] = d ;
			tmp[1] = '\0' ;
		      }
		      else if (! GetName( tmp )) Fatal( ERR_SYNTAX ) ;

		      if ( c == 'n' )
		      {
			if ( e != '\0' ) IncReg( tmp , e ) ;
			GetReg( tmp , aux ) ;
			for ( d = 0 ; aux[d] ; d++ ) PutChar( aux[d] ) ;
			c = SC_EXPAND ;
		      }
		      else
		      {
			GetStr( tmp , aux ) ;
			NewString( aux ) ;
			c = SC_IGNORE ;
		      }

		      break ;
	  case '"'  : BSET( Flg , F_SKIPEOL ) ; break ;
	  case '$'  : if ( ! CurrentMacro ) Fatal( ERR_SYNTAX ) ;
		      c = GetChar() ;
		      if ( (c == EOF) || (c < '1') || (c > '9') )
			Fatal( ERR_SYNTAX ) ;
		      c -= '1' ;
		      if ( c < CurrentMacro->m_NbArg )
			NewString( CurrentMacro->m_Arg[c] ) ;
		      c = SC_IGNORE ;
		      break ;
	  default   : break ;
	}
	BCLR( Flg , F_WASBS ) ;
      }
      else if ( (c == '\\') && (! (InputMode & (IM_TRANSP|IM_STRING))) )
      {
	BSET( Flg , F_WASBS ) ;
	continue ;
      }

      if ( c == SC_EXPAND )
      {
	BCLR( Flg , (F_WASNL|F_NEWPAR) ) ;
	if ( OutputLen > TmpLineLen ) break ;
	continue ;
      }

      if ( c == SC_IGNORE ) continue ;
      if ( (c == ' ') && (InputMode & IM_RDARGS) && (! ArgLen) ) continue ;

      if ( InputMode & IM_TRANSP )
      {
	if ( PutChar( (unsigned char)c ) ) break ;
	if ( c == '\n' )
	{
	  BCLR( InputMode , IM_TRANSP ) ;
	  OutputBuf[OutputLen] = '\0' ;
	  OutputLen = 0 ;
	  LigneSuiv( OutputBuf ) ;
	}
	continue ;
      }

      if ( Flg & F_SKIPEOL )
      {
	if ( c != '\n' ) continue ;
	BCLR( Flg , F_SKIPEOL ) ;
	BSET( Flg , F_WASNL ) ;
	if (! (InputMode & IM_RDARGS)) continue ;
      }

      if ( (Flg & F_WASNL) && ((c == '.') || (c == '\'')) )
	if ( InputMode & IM_RDMACRO )
	{
	  if ( (c = GetChar()) == EOF ) Fatal( ERR_SYNTAX ) ;
	  if ( c == '.' )
	  {
	    BSET( Flg , F_SKIPEOL ) ;
	    EnleveQueue( &(CurrentMacro->m_Def) ) ;
	    InputMode = OldInputMode ;
	    CurrentMacro = NULL ;
	    continue ;
	  }
	  BCLR( Flg , F_WASNL ) ;
	  PutChar( '.' ) ;
	  PutChar( (unsigned char)c ) ;
	  continue ;
	}
	else
	{
	  if (! GetName( cmd )) Fatal( ERR_SYNTAX ) ;
	  if ( c == '\'' ) BSET( Flg , F_NOBRK ) ;
	  BSET( InputMode , IM_RDARGS ) ;
	  BCLR( Flg , F_WASNL ) ;
	  ArgLen = 0 ;
	  continue ;
	}

      if ( c == '\n' )
      {
	if ( Flg & F_WASNL )
	{
	  if ( (! OutputLen) && (! (InputMode & IM_RDMACRO)) ) PutChar( ' ' ) ;
	  else BSET( Flg , F_NEWPAR|F_BREAKED ) ;
	  break ;
	}
	BSET( Flg , F_WASNL ) ;

	if ( InputMode & IM_RDARGS )
	{
	  BCLR( InputMode , IM_RDARGS ) ;
	  if ( OutputLen ) SauveContexte( 0 ) ;
	  BCLR( Flg , F_CONTINUE ) ;
	  ExecCmd( cmd ) ;
	  if ( Flg & F_CONTINUE ) continue ;
	  break ;
	}

	if ( (InputMode & IM_FILLING) && (! TmpCenter) )
	{
	  if ( OutputLen ) c = ' ' ;
	  else continue ;
	}
	else
	{
	  BSET( Flg , F_BREAKED ) ;
	  break ;
	}
      }
      else BCLR( Flg , F_WASNL ) ;

      if ( c == '\t' )
      {
	c =  TabLen * (1 + (OutputLen / TabLen)) ;
	while ( OutputLen < c ) if ( PutChar( TabChar ) ) break ;
	SauveContexte( 0 ) ;
	if ( OutputLen > TmpLineLen ) break ;
	continue ;
      }

      if ( InputMode & IM_RDARGS )
      {
	PutChar( (unsigned char)c ) ;
	continue ;
      }

      if ( isspace( c ) &&
	   (! OutputLen) &&
	   (! (Flg & F_WASNL)) &&
	   (! (InputMode & IM_RDMACRO)) ) continue ;

      PutChar( (unsigned char)c ) ;
      if ( isspace( c ) ) SauveContexte( 0 ) ;
      BCLR( Flg , F_NEWPAR ) ;
      if ( OutputLen >= TmpLineLen ) break ;
    }

    if ( c == EOF ) BSET( Flg , F_BREAKED ) ;

    if ( OutputLen && (! (Flg & F_MACREQ)) )
    {
      AdjustLine() ;
      if ( Flg & F_NEWPAR ) EmptyToWrite++ ;
      WriteLine() ;
      CtrlLen = 0 ;
      if ( TmpNoNum ) TmpNoNum-- ;
      if ( TmpCenter ) TmpCenter-- ;
      FlushStack( TE_CONTEXT ) ;
      BSET( Flg , F_SORTIE ) ;
      if ( LastContext ) free( LastContext ) ;
      LastContext = NULL ;
    }

    if ( InputMode & IM_RDMACRO ) PutChar( '\n' ) ;
    BCLR( Flg , F_MACREQ ) ;
    BSET( Flg , F_NOTI ) ;

    if ( c == EOF )
    {
      CloseFile( CurrentInputFile ) ;
      CurrentInputFile = (struct InputFile *) Pop( TE_INFILE , 0 ) ;
      if ( (CurrentInputFile == NULL) ||
	   (CurrentInputFile == (struct InputFile *)-1) ) do_bp() ;
      else
      {
	c = 0 ;
	SetStr( "fn" , CurrentInputFile->if_Name ) ;
      }
    }

    while ( EmptyToWrite > 0 )
    {
      EmptyToWrite-- ;
      LigneSuiv( "\n" ) ;
      BSET( Flg , F_SORTIE ) ;
    }

    if ( c == EOF ) break ;
  }

}

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

main( argc , argv )
int argc ;
char *argv[] ;

{
  char *nom ;
  long heure, k ;
  struct tm *ladate ;

  time ( &heure ) ;
  ladate = localtime( &heure ) ;

/* examine les arguments */

  Flg = 0 ;
  nom = NULL ;

  for ( k = 1 ; k < argc ; k++ )
    if ( argv[k][0] == '-' )
      switch( argv[k][1] )
      {
	case 'w'  : if (! isdigit( argv[k][2] )) Fatal( ERR_ARGS ) ;
		    TaillePile = atoi( &(argv[k][2]) ) ;
		    break ;
	case 'l'  : if ( argv[k][2] != '\0' ) Fatal( ERR_ARGS ) ;
		    BSET( Flg , F_LOADED ) ;
		    break ;
	case '\0' : if ( nom != NULL ) Fatal( ERR_ARGS ) ;
		    nom = argv[k] ;
		    break ;
	default  : Fatal( ERR_ARGS ) ;
		   break ;
      }
    else
    {
      if ( nom != NULL ) Fatal( ERR_ARGS ) ;
      nom = argv[k] ;
    }

  if ( nom == NULL ) Fatal( ERR_ARGS ) ;

/* cree la pile */

  Pile = (long *)myalloc( (TaillePile << 3) , 0 ) ;

/* initialise les registres */

  InitListe( &TReg ) ;
  InitListe( &TStr ) ;
  InitListe( &TMac ) ;
  InitListe( &TTrp ) ;

  SetReg( "dw" , ladate->tm_wday + 1 , 0 ) ;
  SetReg( "dy" , ladate->tm_mday , 0 ) ;
  SetReg( "mo" , ladate->tm_mon + 1 , 0 ) ;
  SetReg( "yr" , ladate->tm_year , 0 ) ;
  SetReg( "hr" , ladate->tm_hour , 0 ) ;
  SetReg( "mn" , ladate->tm_min , 0 ) ;
  SetReg( "sc" , ladate->tm_sec , 0 ) ;
  SetReg( "ol" , LineNumber , 0 ) ;

  ChangePageNumber( NewPageNumber ) ;

  *TitleSeq = '\0' ;

/* traite le fichier indique */

  CurrentInputFile = NewFile( nom ) ;
  if ( CurrentInputFile )
  {
    SetStr( "fn" , CurrentInputFile->if_Name ) ;
    SetReg( "il" , 1 , 0 ) ;
    Traite() ;
  }

  Termine() ;
}
