Blame main_loop.c

Packit Bot a3ac83
/*  GNU ed - The GNU line editor.
Packit Bot a3ac83
    Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
Packit Bot a3ac83
    Copyright (C) 2006-2017 Antonio Diaz Diaz.
Packit Bot a3ac83
Packit Bot a3ac83
    This program is free software: you can redistribute it and/or modify
Packit Bot a3ac83
    it under the terms of the GNU General Public License as published by
Packit Bot a3ac83
    the Free Software Foundation, either version 2 of the License, or
Packit Bot a3ac83
    (at your option) any later version.
Packit Bot a3ac83
Packit Bot a3ac83
    This program is distributed in the hope that it will be useful,
Packit Bot a3ac83
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Bot a3ac83
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Bot a3ac83
    GNU General Public License for more details.
Packit Bot a3ac83
Packit Bot a3ac83
    You should have received a copy of the GNU General Public License
Packit Bot a3ac83
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Bot a3ac83
*/
Packit Bot a3ac83
Packit Bot a3ac83
#include <ctype.h>
Packit Bot a3ac83
#include <setjmp.h>
Packit Bot a3ac83
#include <stdio.h>
Packit Bot a3ac83
#include <stdlib.h>
Packit Bot a3ac83
#include <string.h>
Packit Bot a3ac83
Packit Bot a3ac83
#include "ed.h"
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
enum Status { QUIT = -1, ERR = -2, EMOD = -3, FATAL = -4 };
Packit Bot a3ac83
Packit Bot a3ac83
static char def_filename[1024] = "";	/* default filename */
Packit Bot a3ac83
static char errmsg[80] = "";		/* error message buffer */
Packit Bot a3ac83
static char prompt_str[80] = "*";	/* command prompt */
Packit Bot a3ac83
static int first_addr = 0, second_addr = 0;
Packit Bot a3ac83
static bool prompt_on = false;		/* if set, show command prompt */
Packit Bot a3ac83
static bool verbose = false;		/* if set, print all error messages */
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
void set_def_filename( const char * const s )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  strncpy( def_filename, s, sizeof def_filename );
Packit Bot a3ac83
  def_filename[sizeof(def_filename)-1] = 0;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
void set_error_msg( const char * msg )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( !msg ) msg = "";
Packit Bot a3ac83
  strncpy( errmsg, msg, sizeof errmsg );
Packit Bot a3ac83
  errmsg[sizeof(errmsg)-1] = 0;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
void set_prompt( const char * const s )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  prompt_on = true;
Packit Bot a3ac83
  strncpy( prompt_str, s, sizeof prompt_str );
Packit Bot a3ac83
  prompt_str[sizeof(prompt_str)-1] = 0;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
void set_verbose( void ) { verbose = true; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static const line_t * mark[26];			/* line markers */
Packit Bot a3ac83
static int markno;				/* line marker count */
Packit Bot a3ac83
Packit Bot a3ac83
static bool mark_line_node( const line_t * const lp, int c )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  c -= 'a';
Packit Bot a3ac83
  if( c < 0 || c >= 26 )
Packit Bot a3ac83
    { set_error_msg( "Invalid mark character" ); return false; }
Packit Bot a3ac83
  if( !mark[c] ) ++markno;
Packit Bot a3ac83
  mark[c] = lp;
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
void unmark_line_node( const line_t * const lp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  int i;
Packit Bot a3ac83
  for( i = 0; markno && i < 26; ++i )
Packit Bot a3ac83
    if( mark[i] == lp )
Packit Bot a3ac83
      { mark[i] = 0; --markno; }
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return address of a marked line */
Packit Bot a3ac83
static int get_marked_node_addr( int c )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  c -= 'a';
Packit Bot a3ac83
  if( c < 0 || c >= 26 )
Packit Bot a3ac83
    { set_error_msg( "Invalid mark character" ); return -1; }
Packit Bot a3ac83
  return get_line_node_addr( mark[c] );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Returns pointer to copy of shell command in the command buffer */
Packit Bot a3ac83
static const char * get_shell_command( const char ** const ibufpp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;		/* temporary buffer */
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  static char * shcmd = 0;		/* shell command buffer */
Packit Bot a3ac83
  static int shcmdsz = 0;		/* shell command buffer size */
Packit Bot a3ac83
  static int shcmdlen = 0;		/* shell command length */
Packit Bot a3ac83
  int i = 0, len = 0;
Packit Bot a3ac83
  bool replacement = false;
Packit Bot a3ac83
Packit Bot a3ac83
  if( restricted() ) { set_error_msg( "Shell access restricted" ); return 0; }
Packit Bot a3ac83
  if( !get_extended_line( ibufpp, &len, true ) ) return 0;
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
Packit Bot a3ac83
  if( **ibufpp != '!' ) buf[i++] = '!';		/* prefix command w/ bang */
Packit Bot a3ac83
  else
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( shcmdlen <= 0 || ( traditional() && !shcmd[1] ) )
Packit Bot a3ac83
      { set_error_msg( "No previous command" ); return 0; }
Packit Bot a3ac83
    memcpy( buf, shcmd, shcmdlen );		/* bufsz >= shcmdlen */
Packit Bot a3ac83
    i += shcmdlen; ++*ibufpp; replacement = true;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  while( **ibufpp != '\n' )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( **ibufpp == '%' )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      const char * p;
Packit Bot a3ac83
      if( !def_filename[0] )
Packit Bot a3ac83
        { set_error_msg( "No current filename" ); return 0; }
Packit Bot a3ac83
      p = strip_escapes( def_filename );
Packit Bot a3ac83
      len = strlen( p );
Packit Bot a3ac83
      if( !resize_buffer( &buf, &bufsz, i + len ) ) return 0;
Packit Bot a3ac83
      memcpy( buf + i, p, len );
Packit Bot a3ac83
      i += len; ++*ibufpp; replacement = true;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0;
Packit Bot a3ac83
      if( ( buf[i++] = *(*ibufpp)++ ) == '\\' ) buf[i++] = *(*ibufpp)++;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  while( **ibufpp == '\n' ) ++*ibufpp;			/* skip newline */
Packit Bot a3ac83
  if( !resize_buffer( &shcmd, &shcmdsz, i + 1 ) ) return 0;
Packit Bot a3ac83
  memcpy( shcmd, buf, i );
Packit Bot a3ac83
  shcmd[i] = 0; shcmdlen = i;
Packit Bot a3ac83
  if( replacement ) printf( "%s\n", shcmd + 1 );
Packit Bot a3ac83
  return shcmd;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static const char * skip_blanks( const char * p )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  while( isspace( (unsigned char)*p ) && *p != '\n' ) ++p;
Packit Bot a3ac83
  return p;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Returns pointer to copy of filename in the command buffer */
Packit Bot a3ac83
static const char * get_filename( const char ** const ibufpp,
Packit Bot a3ac83
                                  const bool traditional_f_command )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  const int pmax = path_max( 0 );
Packit Bot a3ac83
  int n;
Packit Bot a3ac83
Packit Bot a3ac83
  *ibufpp = skip_blanks( *ibufpp );
Packit Bot a3ac83
  if( **ibufpp != '\n' )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int size = 0;
Packit Bot a3ac83
    if( !get_extended_line( ibufpp, &size, true ) ) return 0;
Packit Bot a3ac83
    if( **ibufpp == '!' )
Packit Bot a3ac83
      { ++*ibufpp; return get_shell_command( ibufpp ); }
Packit Bot a3ac83
    else if( size > pmax )
Packit Bot a3ac83
      { set_error_msg( "Filename too long" ); return 0; }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  else if( !traditional_f_command && !def_filename[0] )
Packit Bot a3ac83
    { set_error_msg( "No current filename" ); return 0; }
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, pmax + 1 ) ) return 0;
Packit Bot a3ac83
  for( n = 0; **ibufpp != '\n'; ++n, ++*ibufpp ) buf[n] = **ibufpp;
Packit Bot a3ac83
  buf[n] = 0;
Packit Bot a3ac83
  while( **ibufpp == '\n' ) ++*ibufpp;			/* skip newline */
Packit Bot a3ac83
  return ( may_access_filename( buf ) ? buf : 0 );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static void invalid_address( void ) { set_error_msg( "Invalid address" ); }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Get line addresses from the command buffer until an invalid address
Packit Bot a3ac83
   is seen. Returns the number of addresses read, or -1 if error.
Packit Bot a3ac83
   If no addresses are found, both addresses are set to the current address.
Packit Bot a3ac83
   If one address is found, both addresses are set to that address. */
Packit Bot a3ac83
static int extract_addresses( const char ** const ibufpp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  bool first = true;			/* true == addr, false == offset */
Packit Bot a3ac83
Packit Bot a3ac83
  first_addr = second_addr = -1;	/* set to undefined */
Packit Bot a3ac83
  *ibufpp = skip_blanks( *ibufpp );
Packit Bot a3ac83
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int n;
Packit Bot a3ac83
    const unsigned char ch = **ibufpp;
Packit Bot a3ac83
    if( isdigit( ch ) )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !parse_int( &n, *ibufpp, ibufpp ) ) return -1;
Packit Bot a3ac83
      if( first ) { first = false; second_addr = n; } else second_addr += n;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else switch( ch )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      case '\t':
Packit Bot a3ac83
      case ' ': *ibufpp = skip_blanks( ++*ibufpp ); break;
Packit Bot a3ac83
      case '+':
Packit Bot a3ac83
      case '-': if( first ) { first = false; second_addr = current_addr(); }
Packit Bot a3ac83
                if( isdigit( (unsigned char)(*ibufpp)[1] ) )
Packit Bot a3ac83
                  {
Packit Bot a3ac83
                  if( !parse_int( &n, *ibufpp, ibufpp ) ) return -1;
Packit Bot a3ac83
                  second_addr += n;
Packit Bot a3ac83
                  }
Packit Bot a3ac83
                else { ++*ibufpp;
Packit Bot a3ac83
                       if( ch == '+' ) ++second_addr; else --second_addr; }
Packit Bot a3ac83
                break;
Packit Bot a3ac83
      case '.':
Packit Bot a3ac83
      case '$': if( !first ) { invalid_address(); return -1; };
Packit Bot a3ac83
                first = false; ++*ibufpp;
Packit Bot a3ac83
                second_addr = ( ( ch == '.' ) ? current_addr() : last_addr() );
Packit Bot a3ac83
                break;
Packit Bot a3ac83
      case '/':
Packit Bot a3ac83
      case '?': if( !first ) { invalid_address(); return -1; };
Packit Bot a3ac83
                second_addr = next_matching_node_addr( ibufpp, ch == '/' );
Packit Bot a3ac83
                if( second_addr < 0 ) return -1;
Packit Bot a3ac83
                if( ch == **ibufpp ) ++*ibufpp;		/* remove delimiter */
Packit Bot a3ac83
                first = false; break;
Packit Bot a3ac83
      case '\'':if( !first ) { invalid_address(); return -1; };
Packit Bot a3ac83
                first = false; ++*ibufpp;
Packit Bot a3ac83
                second_addr = get_marked_node_addr( *(*ibufpp)++ );
Packit Bot a3ac83
                if( second_addr < 0 ) return -1;
Packit Bot a3ac83
                break;
Packit Bot a3ac83
      case '%':
Packit Bot a3ac83
      case ',':
Packit Bot a3ac83
      case ';': if( first )
Packit Bot a3ac83
                  {
Packit Bot a3ac83
                  if( first_addr < 0 )
Packit Bot a3ac83
                    { first_addr = ( ( ch == ';' ) ? current_addr() : 1 );
Packit Bot a3ac83
                      second_addr = last_addr(); }
Packit Bot a3ac83
                  }
Packit Bot a3ac83
                else
Packit Bot a3ac83
                  {
Packit Bot a3ac83
                  if( second_addr < 0 || second_addr > last_addr() )
Packit Bot a3ac83
                    { invalid_address(); return -1; }
Packit Bot a3ac83
                  if( ch == ';' ) set_current_addr( second_addr );
Packit Bot a3ac83
                  first_addr = second_addr; first = true;
Packit Bot a3ac83
                  }
Packit Bot a3ac83
                ++*ibufpp;
Packit Bot a3ac83
                break;
Packit Bot a3ac83
      default :
Packit Bot a3ac83
        if( !first && ( second_addr < 0 || second_addr > last_addr() ) )
Packit Bot a3ac83
          { invalid_address(); return -1; }
Packit Bot a3ac83
        {
Packit Bot a3ac83
        int addr_cnt = 0;			/* limited to 2 */
Packit Bot a3ac83
        if( second_addr >= 0 ) addr_cnt = ( first_addr >= 0 ) ? 2 : 1;
Packit Bot a3ac83
        if( addr_cnt <= 0 ) second_addr = current_addr();
Packit Bot a3ac83
        if( addr_cnt <= 1 ) first_addr = second_addr;
Packit Bot a3ac83
        return addr_cnt;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* get a valid address from the command buffer */
Packit Bot a3ac83
static bool get_third_addr( const char ** const ibufpp, int * const addr )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const int old1 = first_addr;
Packit Bot a3ac83
  const int old2 = second_addr;
Packit Bot a3ac83
  int addr_cnt = extract_addresses( ibufpp );
Packit Bot a3ac83
Packit Bot a3ac83
  if( addr_cnt < 0 ) return false;
Packit Bot a3ac83
  if( traditional() && addr_cnt == 0 )
Packit Bot a3ac83
    { set_error_msg( "Destination expected" ); return false; }
Packit Bot a3ac83
  if( second_addr < 0 || second_addr > last_addr() )
Packit Bot a3ac83
    { invalid_address(); return false; }
Packit Bot a3ac83
  *addr = second_addr;
Packit Bot a3ac83
  first_addr = old1; second_addr = old2;
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* set default range and return true if address range is valid */
Packit Bot a3ac83
static bool check_addr_range( const int n, const int m, const int addr_cnt )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( addr_cnt == 0 ) { first_addr = n; second_addr = m; }
Packit Bot a3ac83
  if( first_addr < 1 || first_addr > second_addr || second_addr > last_addr() )
Packit Bot a3ac83
    { invalid_address(); return false; }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
/* set defaults to current_addr and return true if address range is valid */
Packit Bot a3ac83
static bool check_addr_range2( const int addr_cnt )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  return check_addr_range( current_addr(), current_addr(), addr_cnt );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
/* set default second_addr and return true if second_addr is valid */
Packit Bot a3ac83
static bool check_second_addr( const int addr, const int addr_cnt )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( addr_cnt == 0 ) second_addr = addr;
Packit Bot a3ac83
  if( second_addr < 1 || second_addr > last_addr() )
Packit Bot a3ac83
    { invalid_address(); return false; }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* verify the command suffixes in the command buffer */
Packit Bot a3ac83
static bool get_command_suffix( const char ** const ibufpp,
Packit Bot a3ac83
                                int * const pflagsp, int * const snump )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  bool nos_or_rep = !snump;	/* not s command or repeated g/count */
Packit Bot a3ac83
  bool error = false;
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const unsigned char ch = **ibufpp;
Packit Bot a3ac83
    if( ch >= '1' && ch <= '9' )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      int n = 0;
Packit Bot a3ac83
      if( nos_or_rep || !parse_int( &n, *ibufpp, ibufpp ) || n <= 0 )
Packit Bot a3ac83
        { error = true; break; }
Packit Bot a3ac83
      nos_or_rep = true; *snump = n; continue;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else if( ch == 'g' )
Packit Bot a3ac83
      { if( nos_or_rep ) break; else { nos_or_rep = true; *snump = 0; } }
Packit Bot a3ac83
    else if( ch == 'l' ) { if( *pflagsp & GLS ) break; else *pflagsp |= GLS; }
Packit Bot a3ac83
    else if( ch == 'n' ) { if( *pflagsp & GNP ) break; else *pflagsp |= GNP; }
Packit Bot a3ac83
    else if( ch == 'p' ) { if( *pflagsp & GPR ) break; else *pflagsp |= GPR; }
Packit Bot a3ac83
    else break;
Packit Bot a3ac83
    ++*ibufpp;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( error || *(*ibufpp)++ != '\n' )
Packit Bot a3ac83
    { set_error_msg( "Invalid command suffix" ); return false; }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static bool unexpected_address( const int addr_cnt )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( addr_cnt > 0 ) { set_error_msg( "Unexpected address" ); return true; }
Packit Bot a3ac83
  return false;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
static bool unexpected_command_suffix( const unsigned char ch )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( !isspace( ch ) )
Packit Bot a3ac83
    { set_error_msg( "Unexpected command suffix" ); return true; }
Packit Bot a3ac83
  return false;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static bool command_s( const char ** const ibufpp, int * const pflagsp,
Packit Bot a3ac83
                       const int addr_cnt, const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static int pflags = 0;	/* print suffixes */
Packit Bot a3ac83
  static int gmask = GPR;	/* the print suffixes to be toggled */
Packit Bot a3ac83
  static int snum = 1;	/* > 0 count, <= 0 global substitute */
Packit Bot a3ac83
  enum Sflags {
Packit Bot a3ac83
    SGG = 0x01,		/* complement previous global substitute suffix */
Packit Bot a3ac83
    SGP = 0x02,		/* complement previous print suffix */
Packit Bot a3ac83
    SGR = 0x04,		/* use regex of last search (if newer) */
Packit Bot a3ac83
    SGF = 0x08
Packit Bot a3ac83
    } sflags = 0;	/* if sflags != 0, repeat last substitution */
Packit Bot a3ac83
Packit Bot a3ac83
  if( !check_addr_range2( addr_cnt ) ) return false;
Packit Bot a3ac83
  do {
Packit Bot a3ac83
    bool error = false;
Packit Bot a3ac83
    if( **ibufpp >= '1' && **ibufpp <= '9' )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      int n = 0;
Packit Bot a3ac83
      if( ( sflags & SGG ) || !parse_int( &n, *ibufpp, ibufpp ) || n <= 0 )
Packit Bot a3ac83
        error = true;
Packit Bot a3ac83
      else
Packit Bot a3ac83
        { sflags |= SGG; snum = n; }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else switch( **ibufpp )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      case '\n':sflags |= SGF; break;
Packit Bot a3ac83
      case 'g': if( sflags & SGG ) error = true;
Packit Bot a3ac83
                else { sflags |= SGG; snum = !snum; ++*ibufpp; }
Packit Bot a3ac83
                break;
Packit Bot a3ac83
      case 'p': if( sflags & SGP ) error = true;
Packit Bot a3ac83
                else { sflags |= SGP; ++*ibufpp; } break;
Packit Bot a3ac83
      case 'r': if( sflags & SGR ) error = true;
Packit Bot a3ac83
                else { sflags |= SGR; ++*ibufpp; } break;
Packit Bot a3ac83
      default : if( sflags ) error = true;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    if( error ) { set_error_msg( "Invalid command suffix" ); return false; }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  while( sflags && **ibufpp != '\n' );
Packit Bot a3ac83
  if( sflags && !subst_regex() )
Packit Bot a3ac83
    { set_error_msg( "No previous substitution" ); return false; }
Packit Bot a3ac83
  if( ( !sflags || ( sflags & SGR ) ) && !set_subst_regex( ibufpp ) )
Packit Bot a3ac83
    return false;
Packit Bot a3ac83
  if( !sflags )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const char delimiter = **ibufpp;
Packit Bot a3ac83
    pflags = 0; snum = 1;
Packit Bot a3ac83
    if( !extract_replacement( ibufpp, isglobal ) ) return false;
Packit Bot a3ac83
    if( **ibufpp == '\n' ) pflags = GPR;	/* omitted last delimiter */
Packit Bot a3ac83
    else if( **ibufpp == delimiter ) ++*ibufpp;		/* skip delimiter */
Packit Bot a3ac83
    if( !get_command_suffix( ibufpp, &pflags, &snum ) ) return false;
Packit Bot a3ac83
    gmask = pflags & ( GPR | GLS | GNP ); if( gmask == 0 ) gmask = GPR;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  else if( sflags & SGP ) pflags ^= gmask;
Packit Bot a3ac83
  *pflagsp = pflags;
Packit Bot a3ac83
  if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
  if( !search_and_replace( first_addr, second_addr, snum, isglobal ) )
Packit Bot a3ac83
    return false;
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static bool exec_global( const char ** const ibufpp, const int pflags,
Packit Bot a3ac83
                         const bool interactive );
Packit Bot a3ac83
Packit Bot a3ac83
/* execute the next command in command buffer; return error status */
Packit Bot a3ac83
static int exec_command( const char ** const ibufpp, const int prev_status,
Packit Bot a3ac83
                         const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const char * fnp;				/* filename */
Packit Bot a3ac83
  int pflags = 0;				/* print suffixes */
Packit Bot a3ac83
  int addr, c, n;
Packit Bot a3ac83
  const int addr_cnt = extract_addresses( ibufpp );
Packit Bot a3ac83
Packit Bot a3ac83
  if( addr_cnt < 0 ) return ERR;
Packit Bot a3ac83
  *ibufpp = skip_blanks( *ibufpp );
Packit Bot a3ac83
  c = *(*ibufpp)++;
Packit Bot a3ac83
  switch( c )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    case 'a': if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( !append_lines( ibufpp, second_addr, false, isglobal ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'c': if( first_addr == 0 ) first_addr = 1;
Packit Bot a3ac83
              if( second_addr == 0 ) second_addr = 1;
Packit Bot a3ac83
              if( !check_addr_range2( addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( !delete_lines( first_addr, second_addr, isglobal ) ||
Packit Bot a3ac83
                  !append_lines( ibufpp, current_addr(),
Packit Bot a3ac83
                                 current_addr() >= first_addr, isglobal ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'd': if( !check_addr_range2( addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( !delete_lines( first_addr, second_addr, isglobal ) ) return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'e': if( modified() && prev_status != EMOD ) return EMOD;
Packit Bot a3ac83
              /* fall through */
Packit Bot a3ac83
    case 'E': if( unexpected_address( addr_cnt ) ||
Packit Bot a3ac83
                  unexpected_command_suffix( **ibufpp ) ) return ERR;
Packit Bot a3ac83
              fnp = get_filename( ibufpp, false );
Packit Bot a3ac83
              if( !fnp || !delete_lines( 1, last_addr(), isglobal ) ||
Packit Bot a3ac83
                  !close_sbuf() ) return ERR;
Packit Bot a3ac83
              if( !open_sbuf() ) return FATAL;
Packit Bot a3ac83
              if( fnp[0] && fnp[0] != '!' ) set_def_filename( fnp );
Packit Bot a3ac83
              if( read_file( fnp[0] ? fnp : def_filename, 0 ) < 0 )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              reset_undo_state(); set_modified( false );
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'f': if( unexpected_address( addr_cnt ) ||
Packit Bot a3ac83
                  unexpected_command_suffix( **ibufpp ) ) return ERR;
Packit Bot a3ac83
              fnp = get_filename( ibufpp, traditional() );
Packit Bot a3ac83
              if( !fnp ) return ERR;
Packit Bot a3ac83
              if( fnp[0] == '!' )
Packit Bot a3ac83
                { set_error_msg( "Invalid redirection" ); return ERR; }
Packit Bot a3ac83
              if( fnp[0] ) set_def_filename( fnp );
Packit Bot a3ac83
              printf( "%s\n", strip_escapes( def_filename ) );
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'g':
Packit Bot a3ac83
    case 'v':
Packit Bot a3ac83
    case 'G':
Packit Bot a3ac83
    case 'V': if( isglobal )
Packit Bot a3ac83
                { set_error_msg( "Cannot nest global commands" ); return ERR; }
Packit Bot a3ac83
              n = ( c == 'g' || c == 'G' );	/* mark matching lines */
Packit Bot a3ac83
              if( !check_addr_range( 1, last_addr(), addr_cnt ) ||
Packit Bot a3ac83
                  !build_active_list( ibufpp, first_addr, second_addr, n ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              n = ( c == 'G' || c == 'V' );		/* interactive */
Packit Bot a3ac83
              if( ( n && !get_command_suffix( ibufpp, &pflags, 0 ) ) ||
Packit Bot a3ac83
                  !exec_global( ibufpp, pflags, n ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'h':
Packit Bot a3ac83
    case 'H': if( unexpected_address( addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( c == 'H' ) verbose = !verbose;
Packit Bot a3ac83
              if( ( c == 'h' || verbose ) && errmsg[0] )
Packit Bot a3ac83
                printf( "%s\n", errmsg );
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'i': if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( !append_lines( ibufpp, second_addr, true, isglobal ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'j': if( !check_addr_range( current_addr(), current_addr() + 1, addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( first_addr < second_addr &&
Packit Bot a3ac83
                  !join_lines( first_addr, second_addr, isglobal ) ) return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'k': n = *(*ibufpp)++;
Packit Bot a3ac83
              if( second_addr == 0 ) { invalid_address(); return ERR; }
Packit Bot a3ac83
              if( !get_command_suffix( ibufpp, &pflags, 0 ) ||
Packit Bot a3ac83
                  !mark_line_node( search_line_node( second_addr ), n ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'l':
Packit Bot a3ac83
    case 'n':
Packit Bot a3ac83
    case 'p': if( c == 'l' ) n = GLS; else if( c == 'n' ) n = GNP; else n = GPR;
Packit Bot a3ac83
              if( !check_addr_range2( addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ||
Packit Bot a3ac83
                  !print_lines( first_addr, second_addr, pflags | n ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              pflags = 0;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'm': if( !check_addr_range2( addr_cnt ) ||
Packit Bot a3ac83
                  !get_third_addr( ibufpp, &addr ) ) return ERR;
Packit Bot a3ac83
              if( addr >= first_addr && addr < second_addr )
Packit Bot a3ac83
                { set_error_msg( "Invalid destination" ); return ERR; }
Packit Bot a3ac83
              if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( !move_lines( first_addr, second_addr, addr, isglobal ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'P':
Packit Bot a3ac83
    case 'q':
Packit Bot a3ac83
    case 'Q': if( unexpected_address( addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( c == 'P' ) prompt_on = !prompt_on;
Packit Bot a3ac83
              else if( c == 'q' && modified() && prev_status != EMOD )
Packit Bot a3ac83
                return EMOD;
Packit Bot a3ac83
              else return QUIT;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'r': if( unexpected_command_suffix( **ibufpp ) ) return ERR;
Packit Bot a3ac83
              if( addr_cnt == 0 ) second_addr = last_addr();
Packit Bot a3ac83
              fnp = get_filename( ibufpp, false );
Packit Bot a3ac83
              if( !fnp ) return ERR;
Packit Bot a3ac83
              if( !def_filename[0] && fnp[0] != '!' ) set_def_filename( fnp );
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              addr = read_file( fnp[0] ? fnp : def_filename, second_addr );
Packit Bot a3ac83
              if( addr < 0 ) return ERR;
Packit Bot a3ac83
              if( addr ) set_modified( true );
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 's': if( !command_s( ibufpp, &pflags, addr_cnt, isglobal ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 't': if( !check_addr_range2( addr_cnt ) ||
Packit Bot a3ac83
                  !get_third_addr( ibufpp, &addr ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( !copy_lines( first_addr, second_addr, addr ) ) return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'u': if( unexpected_address( addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ||
Packit Bot a3ac83
                  !undo( isglobal ) ) return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'w':
Packit Bot a3ac83
    case 'W': n = **ibufpp;
Packit Bot a3ac83
              if( n == 'q' || n == 'Q' ) ++*ibufpp;
Packit Bot a3ac83
              if( unexpected_command_suffix( **ibufpp ) ) return ERR;
Packit Bot a3ac83
              fnp = get_filename( ibufpp, false );
Packit Bot a3ac83
              if( !fnp ) return ERR;
Packit Bot a3ac83
              if( addr_cnt == 0 && last_addr() == 0 )
Packit Bot a3ac83
                first_addr = second_addr = 0;
Packit Bot a3ac83
              else if( !check_addr_range( 1, last_addr(), addr_cnt ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              if( !def_filename[0] && fnp[0] != '!' ) set_def_filename( fnp );
Packit Bot a3ac83
              addr = write_file( fnp[0] ? fnp : def_filename,
Packit Bot a3ac83
                     ( c == 'W' ) ? "a" : "w", first_addr, second_addr );
Packit Bot a3ac83
              if( addr < 0 ) return ERR;
Packit Bot a3ac83
              if( addr == last_addr() && fnp[0] != '!' ) set_modified( false );
Packit Bot a3ac83
              else if( n == 'q' && modified() && prev_status != EMOD )
Packit Bot a3ac83
                return EMOD;
Packit Bot a3ac83
              if( n == 'q' || n == 'Q' ) return QUIT;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'x': if( second_addr < 0 || second_addr > last_addr() )
Packit Bot a3ac83
                { invalid_address(); return ERR; }
Packit Bot a3ac83
              if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              if( !isglobal ) clear_undo_stack();
Packit Bot a3ac83
              if( !put_lines( second_addr ) ) return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'y': if( !check_addr_range2( addr_cnt ) ||
Packit Bot a3ac83
                  !get_command_suffix( ibufpp, &pflags, 0 ) ||
Packit Bot a3ac83
                  !yank_lines( first_addr, second_addr ) ) return ERR;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case 'z': if( !check_second_addr( current_addr() + !isglobal, addr_cnt ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              if( **ibufpp > '0' && **ibufpp <= '9' )
Packit Bot a3ac83
                { if( parse_int( &n, *ibufpp, ibufpp ) ) set_window_lines( n );
Packit Bot a3ac83
                  else return ERR; }
Packit Bot a3ac83
              if( !get_command_suffix( ibufpp, &pflags, 0 ) ||
Packit Bot a3ac83
                  !print_lines( second_addr,
Packit Bot a3ac83
                    min( last_addr(), second_addr + window_lines() - 1 ),
Packit Bot a3ac83
                    pflags ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              pflags = 0;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case '=': if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
Packit Bot a3ac83
              printf( "%d\n", addr_cnt ? second_addr : last_addr() );
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case '!': if( unexpected_address( addr_cnt ) ) return ERR;
Packit Bot a3ac83
              fnp = get_shell_command( ibufpp );
Packit Bot a3ac83
              if( !fnp ) return ERR;
Packit Bot a3ac83
              if( system( fnp + 1 ) < 0 )
Packit Bot a3ac83
                { set_error_msg( "Can't create shell process" ); return ERR; }
Packit Bot a3ac83
              if( !scripted() ) fputs( "!\n", stdout );
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    case '\n': if( !check_second_addr( current_addr() +
Packit Bot a3ac83
                     ( traditional() || !isglobal ), addr_cnt ) ||
Packit Bot a3ac83
                   !print_lines( second_addr, second_addr, 0 ) )
Packit Bot a3ac83
                return ERR;
Packit Bot a3ac83
              break;
Packit Bot bac822
    case '#': while( *(*ibufpp)++ != '\n' ) {} ;
Packit Bot a3ac83
              break;
Packit Bot a3ac83
    default : set_error_msg( "Unknown command" ); return ERR;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( pflags && !print_lines( current_addr(), current_addr(), pflags ) )
Packit Bot a3ac83
    return ERR;
Packit Bot a3ac83
  return 0;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* apply command list in the command buffer to the active lines in a
Packit Bot a3ac83
   range; return false if error */
Packit Bot a3ac83
static bool exec_global( const char ** const ibufpp, const int pflags,
Packit Bot a3ac83
                         const bool interactive )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  const char * cmd = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  if( !interactive )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( traditional() && strcmp( *ibufpp, "\n" ) == 0 )
Packit Bot a3ac83
      cmd = "p\n";			/* null cmd_list == 'p' */
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !get_extended_line( ibufpp, 0, false ) ) return false;
Packit Bot a3ac83
      cmd = *ibufpp;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  clear_undo_stack();
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const line_t * const lp = next_active_node();
Packit Bot a3ac83
    if( !lp ) break;
Packit Bot a3ac83
    set_current_addr( get_line_node_addr( lp ) );
Packit Bot a3ac83
    if( current_addr() < 0 ) return false;
Packit Bot a3ac83
    if( interactive )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      /* print current_addr; get a command in global syntax */
Packit Bot a3ac83
      int len = 0;
Packit Bot a3ac83
      if( !print_lines( current_addr(), current_addr(), pflags ) )
Packit Bot a3ac83
        return false;
Packit Bot a3ac83
      *ibufpp = get_stdin_line( &len );
Packit Bot a3ac83
      if( !*ibufpp ) return false;			/* error */
Packit Bot a3ac83
      if( len <= 0 ) return false;			/* EOF */
Packit Bot a3ac83
      if( len == 1 && strcmp( *ibufpp, "\n" ) == 0 ) continue;
Packit Bot a3ac83
      if( len == 2 && strcmp( *ibufpp, "&\n" ) == 0 )
Packit Bot a3ac83
        { if( !cmd ) { set_error_msg( "No previous command" ); return false; } }
Packit Bot a3ac83
      else
Packit Bot a3ac83
        {
Packit Bot a3ac83
        if( !get_extended_line( ibufpp, &len, false ) ||
Packit Bot a3ac83
            !resize_buffer( &buf, &bufsz, len + 1 ) ) return false;
Packit Bot a3ac83
        memcpy( buf, *ibufpp, len + 1 );
Packit Bot a3ac83
        cmd = buf;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    *ibufpp = cmd;
Packit Bot a3ac83
    while( **ibufpp ) if( exec_command( ibufpp, 0, true ) < 0 ) return false;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static void script_error( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( verbose ) fprintf( stderr, "script, line %d: %s\n", linenum(), errmsg );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
int main_loop( const bool loose )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  extern jmp_buf jmp_state;
Packit Bot a3ac83
  const char * ibufp;			/* pointer to command buffer */
Packit Bot a3ac83
  volatile int err_status = 0;		/* program exit status */
Packit Bot a3ac83
  int len = 0, status;
Packit Bot a3ac83
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  set_signals();
Packit Bot a3ac83
  status = setjmp( jmp_state );
Packit Bot a3ac83
  if( !status ) enable_interrupts();
Packit Bot a3ac83
  else { status = -1; fputs( "\n?\n", stdout ); set_error_msg( "Interrupt" ); }
Packit Bot a3ac83
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    fflush( stdout ); fflush( stderr );
Packit Bot a3ac83
    if( status < 0 && verbose ) { printf( "%s\n", errmsg ); fflush( stdout ); }
Packit Bot a3ac83
    if( prompt_on ) { fputs( prompt_str, stdout ); fflush( stdout ); }
Packit Bot a3ac83
    ibufp = get_stdin_line( &len );
Packit Bot a3ac83
    if( !ibufp ) return 2;			/* an error happened */
Packit Bot a3ac83
    if( len <= 0 )				/* EOF on stdin ('q') */
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !modified() || status == EMOD ) status = QUIT;
Packit Bot a3ac83
      else { status = EMOD; if( !loose ) err_status = 2; }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else status = exec_command( &ibufp, status, false );
Packit Bot a3ac83
    if( status == 0 ) continue;
Packit Bot a3ac83
    if( status == QUIT ) return err_status;
Packit Bot a3ac83
    fputs( "?\n", stdout );			/* give warning */
Packit Bot a3ac83
    if( !loose && err_status == 0 ) err_status = 1;
Packit Bot a3ac83
    if( status == EMOD ) set_error_msg( "Warning: buffer modified" );
Packit Bot a3ac83
    if( is_regular_file( 0 ) )
Packit Bot a3ac83
      { script_error(); return ( ( status == FATAL ) ? 1 : err_status ); }
Packit Bot a3ac83
    if( status == FATAL )
Packit Bot a3ac83
      { if( verbose ) { printf( "%s\n", errmsg ); } return 1; }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  }