Blame main.c

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