/*
 * Diverses fonctions de base
 * (c)1991 par Denis GOUNELLE
 */

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

static unsigned char tmp[1+LGMAXSTR<<1], aux[LGMAXSTR+1] ;

extern struct Contexte *LastContext ;
extern struct String *CurrentString ;
extern unsigned char Arg[], OutputBuf[] ;
extern char *ARoff_Version, *ARoff_Usage ;
extern struct InputFile *CurrentInputFile ;
extern struct Macro *CurrentMacro, *FindVar() ;
extern struct TeteListe TReg, TStr, TMac, TTrp ;
extern long ArgLen, OutputLen, TotalIndent, LineSpacing, LineNumber,
	    NumInterv, NumSpace, NumIndent, PageOffset, Flg, TmpNoNum,
	    InputMode, TmpIndent, OutputLine, PageLen, PageNumber,
	    EmptyToWrite, NewPageNumber ;

extern void do_ab(), do_ad(), do_am(), do_as(), do_bp(), do_br(), do_ce(),
	    do_de(), do_ds(), do_el(), do_ex(), do_fi(), do_fs(), do_fl(),
	    do_if(), do_in(), do_ll(), do_ls(), do_lt(), do_na(), do_ne(),
	    do_nf(), do_nm(), do_nn(), do_nr(), do_pl(), do_pm(), do_pn(),
	    do_po(), do_rm(), do_rr(), do_rs(), do_rt(), do_so(), do_sp(),
	    do_ta(), do_tc(), do_ti(), do_tl(), do_tm(), do_tr(), do_ts(),
	    do_wh() ;

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

void Termine()
{
  register long k ;
  register struct Macro *m ;

  CloseFile( CurrentInputFile ) ;

  VideListe( &TReg ) ;
  VideListe( &TStr ) ;
  VideListe( &TTrp ) ;

  for ( m = (struct Macro *)TMac.tl_Premier ;
	m ;
	m = (struct Macro *)m->m_Node.el_Suivant )
  {
    VideListe( &(m->m_Def) ) ;
    for ( k = 0 ; m->m_Arg[k] ; k++ ) free( m->m_Arg[k] ) ;
  }
  VideListe( &TMac ) ;

  exit( 0 ) ;
}

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

void Abort( indic )
long indic ;

{
  if ( (indic) && (OutputLen) )
  {
    OutputBuf[OutputLen] = '\0' ;
    OutputLen = '\0' ;
    LigneSuiv( OutputBuf ) ;
  }
  Termine() ;
}

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

void FatalError( code , line , file )
long code, line ;
char *file ;

{
  if ( code == ERR_ARGS )
    fprintf( stderr , "%s\n%s\n" , ARoff_Version , ARoff_Usage ) ;
  else
  {
    fprintf( stderr , "Error %d in file %s at line %d\n" , code , file , line ) ;
    if ( CurrentInputFile )
      fprintf( stderr , "current file: %s, line: %d\n" ,
	       CurrentInputFile->if_Name , CurrentInputFile->if_Line ) ;
  }

  Abort( 0 ) ;
}

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

char *myalloc( len , indic )
register long len ;
long indic ;

{
  register long *p ;

  if (! len) Fatal( ERR_INTERNAL ) ;

  if ( len & 1 ) len++ ; /* forces len to next even value  */
  p = (long *)malloc( len ) ;
  if ( ! p ) Fatal( ERR_MEMORY ) ;
  if ( indic ) bzero( p , len ) ;
  return( (char *)p ) ;
}

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

void SetMacArgs()
{
  register long k ;
  register struct Macro *m ;
  register unsigned char *p, c ;

  m = CurrentMacro ;
  m->m_NbArg = k = 0 ;

  while ( Arg[k] )
  {
    p = m->m_Arg[m->m_NbArg] = (unsigned char *)myalloc( ArgLen - k + 1 , 0 ) ;
    while ( (c = Arg[k]) && (! isspace( c )) )
    {
      if ( c == SC_FIXSPC ) c = ' ' ;
      *p = c ;
      k++ ;
      p++ ;
    }
    *p = '\0' ;
    while ( isspace( Arg[k] ) ) k++ ;
    m->m_NbArg++ ;
  }

  SetReg( ".$" , m->m_NbArg , 0 ) ;
}

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

struct Command CmdTable[] =
{
  "ab",do_ab,
  "ad",do_ad,
  "am",do_am,
  "as",do_as,
  "bp",do_bp,
  "br",do_br,
  "ce",do_ce,
  "de",do_de,
  "ds",do_ds,
  "el",do_el,
  "ex",do_ex,
  "fi",do_fi,
/*"fl",do_fl, inaccessible pour l'utilisateur */
  "fs",do_fs,
  "if",do_if,
  "in",do_in,
  "ll",do_ll,
  "ls",do_ls,
  "lt",do_lt,
  "na",do_na,
  "ne",do_ne,
  "nf",do_nf,
  "nm",do_nm,
  "nn",do_nn,
  "nr",do_nr,
  "pl",do_pl,
  "pm",do_pm,
  "pn",do_pn,
  "po",do_po,
  "rm",do_rm,
  "rr",do_rr,
  "rs",do_rs,
  "rt",do_rt,
  "so",do_so,
  "sp",do_sp,
  "ta",do_ta,
  "tc",do_tc,
  "ti",do_ti,
  "tl",do_tl,
  "tm",do_tm,
  "tr",do_tr,
  "ts",do_ts,
  "wh",do_wh,
  ""  ,NULL,
} ;

void ExecCmd( cmd )
char *cmd ;

{
  register struct Macro *m ;
  register struct Command *p ;

  Arg[ArgLen] = '\0' ;

  /* est-ce une requete ? */

  for ( p = CmdTable ; p->c_func ; p++ )
    if (! strcmp( p->c_name , cmd ))
    {
      (*p->c_func)() ;
      goto _end ;
    }

  /* est-ce une macro ? */

  m = FindVar( &TMac , cmd ) ;
  if ( m )
  {
    if ( InputMode & IM_EXMACRO ) Push( TE_MACRO , CurrentMacro ) ;
    if ( InputMode & IM_STRING ) Push( TE_STRING , CurrentString ) ;
    Push( TE_INMODE , InputMode ) ;
    BCLR( InputMode , (IM_STRING|IM_EXMACRO) ) ;

    m->m_NextC = 0 ;
    m->m_NextL = (struct MLine *)m->m_Def.tl_Premier ;
    CurrentMacro = m ;
    SetMacArgs() ;
    BSET( InputMode , IM_EXMACRO ) ;
  }

_end:

  if ( (Flg & F_WASIF) && (strcmp( cmd , "if")) ) BCLR( Flg , F_WASIF ) ;
  BCLR( Flg , F_NOBRK ) ;
  BSET( Flg , (F_MACREQ|F_WASNL) ) ;
}

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

long GetName( dst )
char *dst ;

{
  long c ;

  if ( (c = GetChar()) == EOF ) return( 0 ) ;
  dst[0] = c ;
  if ( (c = GetChar()) == EOF ) return( 0 ) ;
  dst[1] = c ;
  dst[2] = '\0' ;
  return( 1 ) ;
}

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

long myatoi()

{
  long k ;
  unsigned char *p ;

  k = strtol( Arg , &p , 10 ) ;
  if ( (*p != '\0') && (! isspace( *p )) ) Fatal( ERR_SYNTAX ) ;
  return( k ) ;
}

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

char *GetArg( pstr , pval )
unsigned char *pstr ;
long *pval ;

{
  while ( isspace( *pstr ) ) pstr++ ;
  if ( isdigit( *pstr ) ) *pval = atoi( pstr ) ;
  while ( (*pstr != '\0') && (! isspace( *pstr)) ) pstr++ ;
  return( pstr ) ;
}

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

long ChangeValue( val )
long *val ;

{
  register char c ;
  register long k ;

  k = 0 ;
  c = '\0' ;
  if ( (Arg[k] == '+') || (Arg[k] == '-') )
  {
    c = Arg[k] ;
    k++ ;
  }

  if (! isdigit( Arg[k] )) return( 0 ) ;
  k = atoi( &Arg[k] ) ;

  switch ( c )
  {
    case '+' : *val += k ; break ;
    case '-' : *val -= k ; break ;
    default  : *val  = k ; break ;
  }

  return( 1 ) ;
}

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

void ChangePageNumber( number )
long number ;

{
   PageNumber = number ;
   SetReg( "pn" , PageNumber , 0 ) ;
   NewPageNumber = number + 1 ;
}

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

long LigneSuiv( str )
char *str ;

{
  register long k ;
  register char *p ;
  register struct Macro *m ;

  m = CurrentMacro ;
  k = strlen( str ) ;

  if ( TestTrp( OutputLine ) )
  {
    BSET( Flg , F_TRAP ) ;
    BSET( CurrentMacro->m_Flag , MF_TRAP ) ;
    p = myalloc( k+1 , 0 ) ;
    strcpy( p , str ) ;
    Push( TE_OUTLINE , p ) ;
    Push( TE_TOWRITE , EmptyToWrite ) ;
    if ( m ) BSET( m->m_Flag , MF_WAIT ) ;
    EmptyToWrite = 0 ;
    return ;
  }

  if ( k ) do_fl( str ) ;
  OutputLine++ ;
  if ( OutputLine > PageLen )
  {
    OutputLine = 1 ;
    ChangePageNumber( NewPageNumber ) ;
  }

}

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

void WriteLine()

{
  register long k ;
  register unsigned char *p ;

  /* ajoute le decalage de page */

  p = tmp ;
  for ( k = 0 ; k < PageOffset ; k++ , p++ ) *p = ' ' ;

  /* effectue la numerotation */

  for ( k = 0 ; k < OutputLen ; k++ ) if (! isspace( OutputBuf[k] )) break ;
  if ( (Flg & F_NUMBER) && (k < OutputLen) && (! TmpNoNum) )
  {
    for ( k = 0 ; k < NumIndent ; k++ , p++ ) *p = ' ' ;
    sprintf( aux , "%d" , LineNumber ) ;
    LineNumber++ ;
    SetReg( "ol" , LineNumber , 0 ) ;
    if ( (NumInterv != 1) && ((LineNumber % NumInterv) != 1) )
      for ( k = 0 ; aux[k] != '\0' ; k++ ) aux[k] = ' ' ;
    strcpy( p , aux ) ;
    p += strlen( aux ) ;
    for ( k = 0 ; k < NumSpace ; k++ , p++ ) *p = ' ' ;
  }

  /* ajoute l'indentation */

  for ( k = 0 ; k < TotalIndent ; k++ , p++ ) *p = ' ' ;
  if ( Flg & F_NOTI ) TmpIndent = 0 ;

  /* ecrit la ligne finale */

  bcopy( OutputBuf , p , OutputLen ) ;
  p += OutputLen ;
  OutputLen = 0 ;
  *p = '\n' ;
  p[1] = '\0' ;

  EmptyToWrite += LineSpacing - 1 ;
  LigneSuiv( tmp ) ;
}

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

void SauveContexte( indic )
long indic ;

{
  register long k ;
  register struct Macro *m ;
  register struct Contexte *p ;

  p = (struct Contexte *)myalloc( sizeof(struct Contexte) , 0 ) ;

  if ( InputMode & IM_EXMACRO )
  {
    m = CurrentMacro ;
    bcopy( m , &(p->src.c_macro) , sizeof(struct Macro) ) ;
    p->c_ptr = (long)m ;
    p->c_len = sizeof(struct Macro) ;
  }
  else if ( InputMode & IM_STRING )
  {
    k = 9 + CurrentString->s_len ;
    bcopy( CurrentString , &(p->src.c_string) , k ) ;
    p->c_ptr = (long)CurrentString ;
    p->c_len = k ;
  }
  else
  {
    bcopy( CurrentInputFile , &(p->src.c_file) , sizeof(struct InputFile) ) ;
    if (! (CurrentInputFile->if_Flag & IFF_LOADED))
    {
      bcopy( CurrentInputFile->if_Buf , p->c_ibuf , CurrentInputFile->if_BufLen ) ;
      p->c_seek = lseek( p->src.c_file.if_Desc , 0 , 1 ) ;
      if ( p->c_seek == -1 ) Fatal( ERR_SEEK ) ;
    }
    p->c_ptr = (long)CurrentInputFile ;
  }

  bcopy( OutputBuf , p->c_obuf , OutputLen ) ;
  p->c_olen = OutputLen ;
  p->c_imod = InputMode ;
  p->c_flag = Flg ;

  if ( indic )
  {
    if ( LastContext ) free( LastContext ) ;
    LastContext = p ;
  }
  else Push( TE_CONTEXT , p ) ;
}

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

void RestoreContexte( indic )
long indic ;

{
  register struct Contexte *p ;

  if ( indic ) p = LastContext ;
  else p = (struct Contexte *) Pop( TE_CONTEXT , 1 ) ;

  Flg = p->c_flag ;
  InputMode = p->c_imod ;
  if ( InputMode & IM_EXMACRO )
  {
    CurrentMacro = (struct Macro *)p->c_ptr ;
    bcopy( &(p->src.c_macro) , CurrentMacro , p->c_len ) ;
  }
  else if ( InputMode & IM_STRING )
  {
    CurrentString = (struct String *)p->c_ptr ;
    bcopy( &(p->src.c_string) , CurrentString , p->c_len ) ;
  }
  else
  {
    CurrentInputFile = (struct InputFile *)p->c_ptr ;
    bcopy( &(p->src.c_file) , CurrentInputFile, sizeof(struct InputFile) ) ;
    if (! (CurrentInputFile->if_Flag & IFF_LOADED))
    {
      bcopy( p->c_ibuf , CurrentInputFile->if_Buf , CurrentInputFile->if_BufLen ) ;
      if ( lseek( p->src.c_file.if_Desc , p->c_seek , 0 ) == -1 )
	Fatal( ERR_SEEK ) ;
    }
  }

  bcopy( p->c_obuf , OutputBuf , OutputLen ) ;
  OutputLen = p->c_olen ;
  free( p ) ;
  if ( indic ) LastContext = NULL ;
}

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

void NewString( str )
char *str ;

{
  register struct String *s ;

  if ( ! *str ) return ;

  if ( InputMode & IM_STRING ) Push( TE_STRING , CurrentString ) ;
  if ( InputMode & IM_EXMACRO ) Push( TE_MACRO , CurrentMacro ) ;
  Push( TE_INMODE , InputMode ) ;
  BCLR( InputMode , (IM_STRING|IM_EXMACRO) ) ;

  s = (struct String *)myalloc( sizeof(struct String) , 0 ) ;
  strcpy( s->s_val , str ) ;
  s->s_len = strlen( str ) ;
  s->s_pos = 0 ;
  CurrentString = s ;
  BSET( InputMode , IM_STRING ) ;
}

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

unsigned char *TestString( p )
register unsigned char *p ;

{
  unsigned char *s, *d ;

  p++ ;
  if (! *p) Fatal( ERR_SYNTAX ) ;
  s = p ;
  while ( (*p != '\'') && (*p != '\0') ) p++ ;
  if ( *p != '\'' ) Fatal( ERR_SYNTAX ) ;
  *p = '\0' ;

  p++ ;
  if (! *p) Fatal( ERR_SYNTAX ) ;
  d = p ;
  while ( (*p != '\'') && (*p != '\0') ) p++ ;
  if ( *p != '\'' ) Fatal( ERR_SYNTAX ) ;
  *p = '\0' ;

  p++ ;
  if ( strcmp( s , d ) ) BSET( Flg , F_WASFALSE ) ;
  return( p ) ;
}

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

unsigned char *TestNum( p )
unsigned char *p ;

{
  register char c ;
  register long s , d ;

  s = strtol( p , &p , 10 ) ;
  while ( isspace(*p) ) p++ ;

  if (! *p) Fatal( ERR_SYNTAX ) ;
  c = *p ;
  p++ ;
  while ( isspace(*p) ) p++ ;

  if (! *p) Fatal( ERR_SYNTAX ) ;
  d = strtol( p , &p , 10 ) ;

  switch ( c )
  {
    case '<' : if ( s >= d ) BSET( Flg , F_WASFALSE ) ; break ;
    case '>' : if ( s <= d ) BSET( Flg , F_WASFALSE ) ; break ;
    case '=' : if ( s != d ) BSET( Flg , F_WASFALSE ) ; break ;
    default  : Fatal( ERR_SYNTAX ) ;
  }

  return( p ) ;
}
