Blame main.c

Packit Bot a3ac83
/*  GNU ed - The GNU line editor.
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
    Exit status: 0 for a normal exit, 1 for environmental problems
Packit Bot a3ac83
    (file not found, invalid flags, I/O errors, etc), 2 to indicate a
Packit Bot a3ac83
    corrupt or invalid input file, 3 for an internal consistency error
Packit Bot a3ac83
    (eg, bug) which caused ed to panic.
Packit Bot a3ac83
*/
Packit Bot a3ac83
/*
Packit Bot a3ac83
 * CREDITS
Packit Bot a3ac83
 *
Packit Bot a3ac83
 *      This program is based on the editor algorithm described in
Packit Bot a3ac83
 *      Brian W. Kernighan and P. J. Plauger's book "Software Tools
Packit Bot a3ac83
 *      in Pascal", Addison-Wesley, 1981.
Packit Bot a3ac83
 *
Packit Bot a3ac83
 *      The buffering algorithm is attributed to Rodney Ruddock of
Packit Bot a3ac83
 *      the University of Guelph, Guelph, Ontario.
Packit Bot a3ac83
 *
Packit Bot a3ac83
 */
Packit Bot a3ac83
Packit Bot a3ac83
#include <stdio.h>
Packit Bot a3ac83
#include <stdlib.h>
Packit Bot a3ac83
#include <string.h>
Packit Bot a3ac83
#include <sys/stat.h>
Packit Bot a3ac83
#include <locale.h>
Packit Bot a3ac83
Packit Bot a3ac83
#include "carg_parser.h"
Packit Bot a3ac83
#include "ed.h"
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static const char * const Program_name = "GNU Ed";
Packit Bot a3ac83
static const char * const program_name = "ed";
Packit Bot a3ac83
static const char * const program_year = "2017";
Packit Bot a3ac83
static const char * invocation_name = 0;
Packit Bot a3ac83
Packit Bot a3ac83
static bool restricted_ = false;	/* if set, run in restricted mode */
Packit Bot a3ac83
static bool scripted_ = false;		/* if set, suppress diagnostics,
Packit Bot a3ac83
					   byte counts and '!' prompt */
Packit Bot a3ac83
static bool traditional_ = false;	/* if set, be backwards compatible */
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
bool restricted( void ) { return restricted_; }
Packit Bot a3ac83
bool scripted( void ) { return scripted_; }
Packit Bot a3ac83
bool traditional( void ) { return traditional_; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static void show_help( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  printf( "%s - The GNU line editor.\n", Program_name );
Packit Bot a3ac83
  printf( "\nUsage: %s [options] [file]\n", invocation_name );
Packit Bot a3ac83
  printf( "\nOptions:\n"
Packit Bot a3ac83
          "  -h, --help                 display this help and exit\n"
Packit Bot a3ac83
          "  -V, --version              output version information and exit\n"
Packit Bot a3ac83
          "  -G, --traditional          run in compatibility mode\n"
Packit Bot a3ac83
          "  -l, --loose-exit-status    exit with 0 status even if a command fails\n"
Packit Bot a3ac83
          "  -p, --prompt=STRING        use STRING as an interactive prompt\n"
Packit Bot a3ac83
          "  -r, --restricted           run in restricted mode\n"
Packit Bot a3ac83
          "  -s, --quiet, --silent      suppress diagnostics, byte counts and '!' prompt\n"
Packit Bot a3ac83
          "  -v, --verbose              be verbose; equivalent to the 'H' command\n"
Packit Bot a3ac83
          "Start edit by reading in 'file' if given.\n"
Packit Bot a3ac83
          "If 'file' begins with a '!', read output of shell command.\n"
Packit Bot a3ac83
          "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n"
Packit Bot a3ac83
          "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n"
Packit Bot a3ac83
          "invalid input file, 3 for an internal consistency error (eg, bug) which\n"
Packit Bot a3ac83
          "caused ed to panic.\n"
Packit Bot a3ac83
          "\nReport bugs to bug-ed@gnu.org\n"
Packit Bot a3ac83
          "Ed home page: http://www.gnu.org/software/ed/ed.html\n"
Packit Bot a3ac83
          "General help using GNU software: http://www.gnu.org/gethelp\n" );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static void show_version( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  printf( "GNU %s %s\n", program_name, PROGVERSION );
Packit Bot a3ac83
  printf( "Copyright (C) 1994 Andrew L. Moore.\n"
Packit Bot a3ac83
          "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
Packit Bot a3ac83
  printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
Packit Bot a3ac83
          "This is free software: you are free to change and redistribute it.\n"
Packit Bot a3ac83
          "There is NO WARRANTY, to the extent permitted by law.\n" );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
void show_strerror( const char * const filename, const int errcode )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( !scripted_ )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( filename && filename[0] ) fprintf( stderr, "%s: ", filename );
Packit Bot a3ac83
    fprintf( stderr, "%s\n", strerror( errcode ) );
Packit Bot a3ac83
    }
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static void show_error( const char * const msg, const int errcode, const bool help )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( msg && msg[0] )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    fprintf( stderr, "%s: %s", program_name, msg );
Packit Bot a3ac83
    if( errcode > 0 ) fprintf( stderr, ": %s", strerror( errcode ) );
Packit Bot a3ac83
    fputc( '\n', stderr );
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( help )
Packit Bot a3ac83
    fprintf( stderr, "Try '%s --help' for more information.\n",
Packit Bot a3ac83
             invocation_name );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return true if file descriptor is a regular file */
Packit Bot a3ac83
bool is_regular_file( const int fd )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  struct stat st;
Packit Bot a3ac83
  return ( fstat( fd, &st ) != 0 || S_ISREG( st.st_mode ) );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
bool may_access_filename( const char * const name )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  if( restricted_ &&
Packit Bot a3ac83
      ( *name == '!' || strcmp( name, ".." ) == 0 || strchr( name, '/' ) ) )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    set_error_msg( "Shell access restricted" );
Packit Bot a3ac83
    return false;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
int main( const int argc, const char * const argv[] )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  int argind;
Packit Bot a3ac83
  bool loose = false;
Packit Bot a3ac83
  const struct ap_Option options[] =
Packit Bot a3ac83
    {
Packit Bot a3ac83
    { 'G', "traditional",       ap_no  },
Packit Bot a3ac83
    { 'h', "help",              ap_no  },
Packit Bot a3ac83
    { 'l', "loose-exit-status", ap_no  },
Packit Bot a3ac83
    { 'p', "prompt",            ap_yes },
Packit Bot a3ac83
    { 'r', "restricted",        ap_no  },
Packit Bot a3ac83
    { 's', "quiet",             ap_no  },
Packit Bot a3ac83
    { 's', "silent",            ap_no  },
Packit Bot a3ac83
    { 'v', "verbose",           ap_no  },
Packit Bot a3ac83
    { 'V', "version",           ap_no  },
Packit Bot a3ac83
    {  0 ,  0,                  ap_no } };
Packit Bot a3ac83
Packit Bot a3ac83
  struct Arg_parser parser;
Packit Bot a3ac83
  invocation_name = argv[0];
Packit Bot a3ac83
Packit Bot a3ac83
  if( !ap_init( &parser, argc, argv, options, 0 ) )
Packit Bot a3ac83
    { show_error( "Memory exhausted.", 0, false ); return 1; }
Packit Bot a3ac83
  if( ap_error( &parser ) )				/* bad option */
Packit Bot a3ac83
    { show_error( ap_error( &parser ), 0, true ); return 1; }
Packit Bot a3ac83
Packit Bot a3ac83
  for( argind = 0; argind < ap_arguments( &parser ); ++argind )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const int code = ap_code( &parser, argind );
Packit Bot a3ac83
    const char * const arg = ap_argument( &parser, argind );
Packit Bot a3ac83
    if( !code ) break;					/* no more options */
Packit Bot a3ac83
    switch( code )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      case 'G': traditional_ = true; break;	/* backward compatibility */
Packit Bot a3ac83
      case 'h': show_help(); return 0;
Packit Bot a3ac83
      case 'l': loose = true; break;
Packit Bot a3ac83
      case 'p': set_prompt( arg ); break;
Packit Bot a3ac83
      case 'r': restricted_ = true; break;
Packit Bot a3ac83
      case 's': scripted_ = true; break;
Packit Bot a3ac83
      case 'v': set_verbose(); break;
Packit Bot a3ac83
      case 'V': show_version(); return 0;
Packit Bot a3ac83
      default : show_error( "internal error: uncaught option.", 0, false );
Packit Bot a3ac83
                return 3;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    } /* end process options */
Packit Bot a3ac83
Packit Bot a3ac83
  setlocale( LC_ALL, "" );
Packit Bot a3ac83
  if( !init_buffers() ) return 1;
Packit Bot a3ac83
Packit Bot a3ac83
  while( argind < ap_arguments( &parser ) )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const char * const arg = ap_argument( &parser, argind );
Packit Bot a3ac83
    if( strcmp( arg, "-" ) == 0 ) { scripted_ = true; ++argind; continue; }
Packit Bot a3ac83
    if( may_access_filename( arg ) )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( read_file( arg, 0 ) < 0 && is_regular_file( 0 ) )
Packit Bot a3ac83
        return 2;
Packit Bot a3ac83
      else if( arg[0] != '!' ) set_def_filename( arg );
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      fputs( "?\n", stdout );
Packit Bot a3ac83
      if( arg[0] ) set_error_msg( "Invalid filename" );
Packit Bot a3ac83
      if( is_regular_file( 0 ) ) return 2;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    break;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  ap_free( &parser );
Packit Bot a3ac83
Packit Bot a3ac83
  return main_loop( loose );
Packit Bot a3ac83
  }