Blame main_loop.c

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