/* GNU moe - My Own Editor
   Copyright (C) 2005-2025 Antonio Diaz Diaz.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <cctype>
#include <cerrno>
#include <climits>		// SSIZE_MAX
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <list>
#include <string>
#include <vector>
#include <dirent.h>
#include <stdint.h>		// SIZE_MAX
#include <unistd.h>
#include <sys/stat.h>

#include "arg_parser.h"
#include "buffer.h"
#include "buffer_handle.h"
#include "iso_8859.h"
#include "menu.h"
#include "rc.h"
#include "regex.h"

#if ( defined  SIZE_MAX &&  SIZE_MAX < ULONG_MAX ) || \
    ( defined SSIZE_MAX && SSIZE_MAX <  LONG_MAX )
#error "Environments where 'size_t' is narrower than 'long' are not supported."
#endif

// order of application of file options by place of specification:
// 1 options specified in file moe.conf as 'default file options'.
// 2 options specified in file moe.conf as 'file name dependent options'.
// 3 options specified in command line as 'default file options'.
// 4 options specified in command line as 'file options'.
// (4 is not applied to files opened during the editing sesion).

namespace RC {

const Arg_parser * parserp;

class File_options
  {
  struct Record
    {
    int code;
    int argument;
    Record( const int c, const int arg ) : code( c ), argument( arg ) {}
    };
  std::vector< Record > data;

public:
  File_options() {}
  void add_option( const int c, const int arg = 0 )
    { data.push_back( Record( c, arg ) ); }
  void reset() { data.clear(); }
  int size() const { return data.size(); }
  int code( const int i ) const { return data[i].code; }
  int argument( const int i ) const { return data[i].argument; }
  };


struct Regex_options
  {
  std::vector< std::string > regex_vector;
  File_options file_options;
  };


Options editor_options_;
File_options default_file_options;
File_options moeconf_file_options;
std::vector< Regex_options > regex_options_vector;


enum Optcode
  {
  auto_unmark = 'u', backup = 'b', no_backup = 'B', /*beep = 'p',*/
  exit_ask = 'e', ignore_case = 'i', indent_step = 'n', keep_lines = 'k',
  max_windows = 'm', preserve_links = 'p', rectangle_mode = 'x',
  search_wrap = 's', smart_home = 'H', auto_indent = 'a', lmargin = 'l',
  rmargin = 'r', overwrite = 'O', read_only = 'o', two_spaces = '2',
  word_wrap = 'w', no_word_wrap = 'W',
  no_auto_unmark = 256, /*no_beep,*/ no_exit_ask, no_ignore_case,
  no_preserve_links, no_rectangle_mode, no_search_wrap, no_smart_home,
  no_auto_indent, no_overwrite, no_read_only, no_two_spaces
  };

const Arg_parser::Option options[] =
  {
  { 'h',               "help",              Arg_parser::no  },
  { 'V',               "version",           Arg_parser::no  },
  { auto_unmark,       "auto-unmark",       Arg_parser::no  },
  { no_auto_unmark,    "no-auto-unmark",    Arg_parser::no  },
  { backup,            "backup",            Arg_parser::no  },
  { no_backup,         "no-backup",         Arg_parser::no  },
//  { beep,              "beep",              Arg_parser::no  },
//  { no_beep,           "no-beep",           Arg_parser::no  },
  { exit_ask,          "exit-ask",          Arg_parser::no  },
  { no_exit_ask,       "no-exit-ask",       Arg_parser::no  },
  { ignore_case,       "ignore-case",       Arg_parser::no  },
  { no_ignore_case,    "no-ignore-case",    Arg_parser::no  },
  { indent_step,       "indent-step",       Arg_parser::yes },
  { keep_lines,        "keep-lines",        Arg_parser::yes },
  { max_windows,       "max-windows",       Arg_parser::yes },
  { preserve_links,    "preserve-links",    Arg_parser::no  },
  { no_preserve_links, "no-preserve-links", Arg_parser::no  },
  { rectangle_mode,    "rectangle",         Arg_parser::no  },
  { no_rectangle_mode, "no-rectangle",      Arg_parser::no  },
  { search_wrap,       "search-wrap",       Arg_parser::no  },
  { no_search_wrap,    "no-search-wrap",    Arg_parser::no  },
  { smart_home,        "smart-home",        Arg_parser::no  },
  { no_smart_home,     "no-smart-home",     Arg_parser::no  },
  { auto_indent,       "auto-indent",       Arg_parser::no  },
  { no_auto_indent,    "no-auto-indent",    Arg_parser::no  },
  { lmargin,           "lmargin",           Arg_parser::yes },
  { rmargin,           "rmargin",           Arg_parser::yes },
  { overwrite,         "overwrite",         Arg_parser::no  },
  { no_overwrite,      "no-overwrite",      Arg_parser::no  },
  { read_only,         "read-only",         Arg_parser::no  },
  { no_read_only,      "no-read-only",      Arg_parser::no  },
  { two_spaces,        "two-spaces",        Arg_parser::no  },
  { no_two_spaces,     "no-two-spaces",     Arg_parser::no  },
  { word_wrap,         "word-wrap",         Arg_parser::no  },
  { no_word_wrap,      "no-word-wrap",      Arg_parser::no  },
  { '1',               "orphan",            Arg_parser::no  },	// ignored
  { 0,                 0,                   Arg_parser::no  } };


bool is_editor_option( const int code )
  {
  switch( code )
    {
    case auto_indent   :
    case no_auto_indent:
    case lmargin       :
    case rmargin       :
    case overwrite     :
    case no_overwrite  :
    case read_only     :
    case no_read_only  :
    case two_spaces    :
    case no_two_spaces :
    case word_wrap     :
    case no_word_wrap  : return false;
    }
  return true;
  }


bool set_option_from_code( const Arg_parser & parser, const int argind,
                           File_options & file_options, const char * const msg,
                           const bool allow_editor_options = false )
  {
  const int code = parser.code( argind );
  const char * const parsed_name = parser.parsed_name( argind ).c_str();
  const char * const arg = parser.argument( argind ).c_str();
  if( !allow_editor_options && is_editor_option( code ) )
    { std::fprintf( stderr, "%serror: Option '%s' not allowed here.\n",
                    msg, parsed_name ); return false; }

  int errcode = 0, n;
  switch( code )
    {
    case auto_unmark:       editor_options_.auto_unmark = true; break;
    case no_auto_unmark:    editor_options_.auto_unmark = false; break;
    case backup:            editor_options_.backup = true; break;
    case no_backup:         editor_options_.backup = false; break;
//    case beep:              editor_options_.beep = true; break;
//    case no_beep:           editor_options_.beep = false; break;
    case exit_ask:          editor_options_.exit_ask = true; break;
    case no_exit_ask:       editor_options_.exit_ask = false; break;
    case ignore_case:       editor_options_.ignore_case = true; break;
    case no_ignore_case:    editor_options_.ignore_case = false; break;
    case indent_step:
      if( !parse_int( arg, n ) ) errcode = 1;
      else if( !editor_options_.set_indent_step( n ) ) errcode = 2;
      break;
    case keep_lines:
      if( !parse_int( arg, n ) ) errcode = 1;
      else if( !editor_options_.set_keep_lines( n ) ) errcode = 2;
      break;
    case max_windows:
      if( !parse_int( arg, n ) ) errcode = 1;
      else if( !editor_options_.set_max_windows( n ) ) errcode = 2;
      break;
    case preserve_links:    editor_options_.preserve_links = true; break;
    case no_preserve_links: editor_options_.preserve_links = false; break;
    case rectangle_mode:    editor_options_.rectangle_mode = true; break;
    case no_rectangle_mode: editor_options_.rectangle_mode = false; break;
    case search_wrap:       editor_options_.search_wrap = true; break;
    case no_search_wrap:    editor_options_.search_wrap = false; break;
    case smart_home:        editor_options_.smart_home = true; break;
    case no_smart_home:     editor_options_.smart_home = false; break;

    case auto_indent:
    case no_auto_indent:
    case overwrite:
    case no_overwrite:
    case read_only:
    case no_read_only:
    case two_spaces:
    case no_two_spaces:
    case word_wrap:
    case no_word_wrap:      file_options.add_option( code ); break;
    case lmargin:
      if( !parse_int( arg, n ) ) errcode = 1;
      else if( !Buffer::Options().set_lmargin( n - 1 ) ) errcode = 2;
      else file_options.add_option( code, n - 1 );
      break;
    case rmargin:
      if( !parse_int( arg, n ) ) errcode = 1;
      else if( !Buffer::Options().set_rmargin( n - 1 ) ) errcode = 2;
      else file_options.add_option( code, n - 1 );
      break;
    case '1': break;		// ignored
    default: std::fprintf( stderr, "%sinternal error: uncaught option.\n", msg );
             return false;
    }

  if( errcode == 1 )
    std::fprintf( stderr, "%serror: Bad argument for option '%s'\n",
                  msg, parsed_name );
  else if( errcode == 2 )
    std::fprintf( stderr, "%serror: Value out of range for option '%s'\n",
                  msg, parsed_name );
  return errcode == 0;
  }


int parse_line_col( const char * const ptr, long & line, long & col )
  {
  if( ptr[0] == '+' && ptr[1] == 0 ) { line = col = LONG_MAX - 1; return 1; }
  int c = parse_long( ptr, line, false );
  if( c <= 0 || --line < 0 ) return 0;
  if( ptr[c] )
    {
    const int c2 = parse_long( ptr + c + 1, col );
    if( c2 <= 0 || --col < 0 || ptr[c] != ',' ) return 0;
    c += c2 + 1;
    }
  else col = 0;
  return c;
  }


void apply_file_options( Buffer & buffer, const File_options & file_options )
  {
  for( int i = 0; i < file_options.size(); ++i )
    {
    const int code = file_options.code( i );
    const int arg = file_options.argument( i );
    switch( code )
      {
      case auto_indent:    buffer.options.auto_indent = true; break;
      case no_auto_indent: buffer.options.auto_indent = false; break;
      case lmargin:        buffer.options.set_lmargin( arg ); break;
      case rmargin:        buffer.options.set_rmargin( arg ); break;
      case overwrite:      buffer.options.overwrite = true; break;
      case no_overwrite:   buffer.options.overwrite = false; break;
      case read_only:      buffer.options.read_only = true; break;
      case no_read_only:   buffer.options.read_only = false; break;
      case two_spaces:     buffer.options.two_spaces = true; break;
      case no_two_spaces:  buffer.options.two_spaces = false; break;
      case word_wrap:      buffer.options.word_wrap = true; break;
      case no_word_wrap:   buffer.options.word_wrap = false; break;
      }
    }
  }


void apply_regex_options( Buffer & buffer )
  {
  if( buffer.name().empty() ) return;
  for( unsigned i = 0; i < regex_options_vector.size(); ++i )
    {
    const Regex_options & ro = regex_options_vector[i];
    unsigned j = 0;
    for( ; j < ro.regex_vector.size(); ++j )
      if( Regex::match_filename( ro.regex_vector[j],
          Menu::my_basename( buffer.name() ) ) )
        break;
    if( j < ro.regex_vector.size() )
      { apply_file_options( buffer, ro.file_options ); break; }
    }
  }


bool unwanted_name( const std::string & name )
  {
  const char * const ext1[] = { "a", "o", "z", "Z", 0 };
  const char * const ext2[] = { "gz", "lz", "mo", "po", "ps", "so", "xz", 0 };
  const char * const ext3[] = { "avi", "bmp", "bz2", "deb", "dll", "dvi",
    "elf", "exe", "gif", "gmo", "ico", "jar", "jpg", "mp3", "mp4", "mpg",
    "obj", "pbm", "pdf", "pgm", "png", "pnm", "ppm", "rpm", "svg", "tar",
    "tgz", "tif", "tlz", "txz", "wav", "xcf", "zip", "zst", 0 };
  const char * const ext4[] = { "docx", "info", "jpeg", "lzma", "rpms", "svgz",
    "tiff", "tzst", 0 };
  const char * const ext5[] = { "class", 0 };
  const char * const * ext[5] = { ext1, ext2, ext3, ext4, ext5 };

  if( name == "." || name == ".." || name.empty() || name.end()[-1] == '~' )
    return true;
  for( unsigned i = 0; i < 5; ++i )
    {
    if( name.size() <= i + 2 ) break;
    if( name[name.size()-i-2] != '.' ) continue;
    for( const char * const * p = ext[i]; *p; ++p )
      if( name.compare( name.size() - i - 1, i + 1, *p ) == 0 ) return true;
    if( i == 0 && std::isdigit( (unsigned char)name.end()[-1] ) )
      return true;		// exclude man pages *.[0-9]
    break;			// compare only one extension size
    }
  return false;
  }


/* Depth first. Files first in each directory. Load regular files only.
   Ignore backups '*~', unwanted names, and symbolic links.
*/
bool add_handles_recursively( std::string name, const long line, const long col,
                              const File_options & file_options,
                              bool & first_post, bool & read_error_found )
  {
  while( !name.empty() && name.end()[-1] == '/' )
    name.resize( name.size() - 1 );		// reject "/", "//", etc.
  if( name.empty() ) return false;
  while( name.size() > 2 && name[0] == '.' && name[1] == '/' )
    name.erase( 0, 2 );				// remove leading "./"
  struct stat st;
  if( stat( name.c_str(), &st ) != 0 || !S_ISDIR( st.st_mode ) )
    return false;
  std::list< std::string > dirnames;		// only directory names here
  dirnames.push_back( name );
  const int prev_handles = Bufhandle_vector::handles();
  while( !dirnames.empty() )
    {
    const std::string dirname = dirnames.front();
    dirnames.pop_front();
    DIR * const dirp = opendir( dirname.c_str() );
    if( !dirp ) { read_error_found = true; continue; }
    std::vector< std::string > name_vector;
    const bool isdot = dirname == ".";
    while( true )
      {
      const struct dirent * const entryp = readdir( dirp );
      if( !entryp ) { closedir( dirp ); break; }
      std::string tmp( entryp->d_name );
      if( unwanted_name( tmp ) ) continue;
      if( !isdot ) tmp = dirname + "/" + tmp;
      name_vector.push_back( tmp );
      }
    if( name_vector.empty() ) continue;
    if( name_vector.size() > 1 )
      std::sort( name_vector.begin(), name_vector.end() );
    std::list< std::string > subdir_list;	// subdirs temporary list
    for( unsigned i = 0; i < name_vector.size(); )
      {
      const unsigned long dot = name_vector[i].rfind( '.' );
      const unsigned long slash = name_vector[i].rfind( '/' );
      const unsigned long size = name_vector[i].size();
      const unsigned j = i++;	// names differing only in the extension
      if( dot < size - 1 && dot > 0 && ( slash >= size || slash + 1 < dot ) &&
          ( !std::isdigit( (unsigned char)name_vector[j][dot-1] ) ||
            !std::isdigit( (unsigned char)name_vector[j][dot+1] ) ) )
        while( i < name_vector.size() &&
               name_vector[j].compare( 0, dot + 1, name_vector[i],
                                       0, dot + 1 ) == 0 &&
               name_vector[i].rfind( '.' ) == dot ) ++i;
      for( unsigned k = i; k > j; --k )		// reverse sort by extension
        {
        if( lstat( name_vector[k-1].c_str(), &st ) != 0 ) continue;
        if( S_ISDIR( st.st_mode ) ) subdir_list.push_back( name_vector[k-1] );
        else if( S_ISREG( st.st_mode ) )
          {
          try {
            const int h = Bufhandle_vector::find_or_add_handle(
                      name_vector[k-1], -1, line, col, false, prev_handles );
            apply_file_options( Bufhandle_vector::handle( h ).buffer(),
                                file_options );
            }
          catch( Buffer::Error & e )
            {
            if( first_post ) { first_post = false; std::fputc( '\n', stderr ); }
            std::fprintf( stderr, "warning: '%s': %s\n",
                          name_vector[k-1].c_str(), e.msg );
            read_error_found = true;
            }
          }
        }
      }
    dirnames.splice( dirnames.begin(), subdir_list );	// prepend subdirs
    }
  return true;
  }


int chvalue( const unsigned char ch )
  {
  if( ch >= '0' && ch <= '9' ) return ch - '0';
  if( ch >= 'A' && ch <= 'Z' ) return ch - 'A' + 10;
  if( ch >= 'a' && ch <= 'z' ) return ch - 'a' + 10;
  return 255;
  }

} // end namespace RC


RC::Options & RC::editor_options() { return editor_options_; }


void RC::apply_all_file_options( Buffer & buffer )
  {
  apply_file_options( buffer, moeconf_file_options );
  apply_regex_options( buffer );
  apply_file_options( buffer, default_file_options );
  }


// Return true if first non-whitespace character is '+' or '-'.
bool RC::issigned( const char * ptr )
  {
  while( *ptr && ISO_8859::isspace( *ptr ) ) ++ptr;	// strip spaces
  return *ptr == '+' || *ptr == '-';
  }


// Return the number of bytes read, or 0 if error.
int RC::parse_int( const char * const ptr, int & result )
  {
  long tmp;
  const int c = parse_long( ptr, tmp );
  if( !c || tmp < -INT_MAX || tmp > INT_MAX ) return 0;
  result = tmp;
  return c;
  }

// Return the number of bytes read, or 0 if error.
int RC::parse_long( const char * const ptr, long & result, const bool last )
  {
  int c = 0, base;
  while( ISO_8859::isspace( ptr[c] ) ) ++c;
  const bool minus = ptr[c] == '-';
  if( minus || ptr[c] == '+' ) ++c;
  if( !std::isdigit( ptr[c] ) ) return 0;

  if( ptr[c] != '0' ) base = 10;			// decimal
  else if( ptr[c+1] == 'x' || ptr[c+1] == 'X' ) { base = 16; c += 2; }	// hex
  else base = 8;					// octal or 0
  const int dpg = ( base != 16 ) ? 3 : 2;	// min digits per group
  int dig = dpg - 1;	// digits in current group, first may have 1 digit
  result = 0;
  for( ; ptr[c]; ++c )
    {
    if( ptr[c] == '_' ) { if( dig < dpg ) break; else { dig = 0; continue; } }
    const int val = chvalue( ptr[c] ); if( val >= base ) break; else ++dig;
    if( ( LONG_MAX - val ) / base < result ) return 0;
    result = result * base + val;
    }
  if( ( last && ptr[c] != 0 ) || dig < dpg ) return 0;
  if( minus ) result = -result;
  return c;
  }

// Return the number of bytes read, or 0 if error.
int RC::parse_relative_long( const char * const ptr, long & result )
  {
  long tmp = 0;
  const int c = parse_long( ptr, tmp );
  if( !c ) return 0;
  if( !issigned( ptr ) ) result = tmp;
  else if( tmp > 0 )
    {
    if( LONG_MAX - tmp < result ) result = LONG_MAX;
    else result += tmp;
    }
  else if( tmp < 0 )
    {
    if( result + tmp < 1 ) result = 1;
    else result += tmp;
    }
  return c;
  }


// Return 0 if success, 1 if file not found, 2 if syntax error.
int RC::process_rcfile( const char * const name )
  {
  FILE * const f = std::fopen( name, "r" );
  if( !f ) return 1;

  std::fprintf( stderr, "Processing '%s'... ", name );
  std::fflush( stderr );

  const char delimiters[] = " \t\r\n";
  int retval = 0;
  int regex_status = 0;		// 1 = got regex name, 2 got regex option
  enum { bufsize = 1024 };
  char buf[bufsize];
  for( unsigned line = 1; ; ++line )
    {
    if( !std::fgets( buf, bufsize, f ) ) break;
    int len = 0;
    while( len < bufsize && buf[len] != '\n' ) ++len;
    if( len >= bufsize )
      {
      while( true )
        {
        const int ch = std::fgetc( f );
        if( ch == EOF || ch == '\n' ) break;
        }
      if( !retval ) std::fputc( '\n', stderr );
      std::fprintf( stderr, "%s %u: error: Line too long\n", name, line );
      retval = 2;
      }
    if( len == 0 || ISO_8859::isspace( buf[0] ) ) continue;
    if( buf[0] != '-' )		// create new file name dependent options
      {
      const char * const regex = std::strtok( buf, delimiters );
      if( regex_status != 1 )
        regex_options_vector.push_back( Regex_options() );
      regex_options_vector.back().regex_vector.push_back( regex );
      regex_status = 1;
      }
    else			// set an option
      {
      const char * const opt = std::strtok( buf, delimiters );
      const char * const arg = std::strtok( 0, delimiters );
      char msg[80];
      snprintf( msg, sizeof msg, "%s%s %u: ", retval ? "" : "\n", name, line );
      const Arg_parser parser( opt, arg, options );
      if( parser.error().size() || !parser.arguments() || !parser.code( 0 ) )
        { std::fprintf( stderr, "%s%s\n", msg, parser.error().c_str() );
          retval = 2; }
      else if( regex_status == 0 )
        { if( !set_option_from_code( parser, 0, moeconf_file_options,
                                     msg, true ) ) retval = 2; }
      else
        { if( !set_option_from_code( parser, 0,
                                     regex_options_vector.back().file_options,
                                     msg ) ) retval = 2;
          regex_status = 2; }
      }
    }
  std::fclose( f );
  if( !retval ) std::fputs( "done\n", stderr );
  return retval;
  }


// Return 0 if success, 1 if invalid option, 2 if a file can't be read.
int RC::process_options()
  {
  std::fputs( "Processing options... ", stderr ); std::fflush( stderr );
  int argind = 0;
  for( ; argind < parserp->arguments(); ++argind )
    {						// process "global" options
    if( !parserp->code( argind ) ) break;	// line number or file name
    if( !set_option_from_code( *parserp, argind,
                               default_file_options, "\n", true ) ) return 1;
    }

  std::fputs( "done\nLoading files... ", stderr ); std::fflush( stderr );
  long line = 0, col = 0;
  bool first_post = true;
  bool read_error_found = false;
  bool unsupported_found = false;
  bool stdin_used = false;
  while( argind < parserp->arguments() )  // process files and file options
    {
    if( parserp->argument( argind ).c_str()[0] == '+' )		// line number
      {
      if( !parse_line_col( parserp->argument( argind ).c_str(), line, col ) )
        {
        if( first_post ) std::fputc( '\n', stderr );
        show_error( "Bad number of line or column." );
        return 1;
        }
      if( ++argind >= parserp->arguments() ) break;
      if( parserp->code( argind ) )
        {
        if( first_post ) std::fputc( '\n', stderr );
        show_error( "Options not allowed between line number and file name." );
        return 1;
        }
      }
    std::string name( parserp->argument( argind++ ) );
    Menu::tilde_expansion( name );
    File_options file_options;
    for( ; argind < parserp->arguments(); ++argind )
      {						// process file options
      if( !parserp->code( argind ) ) break;	// line number or file name
      if( !set_option_from_code( *parserp, argind, file_options,
                                 first_post ? "\n" : "" ) ) return 1;
      }
    bool can_stat = false, can_read = false;
    const bool is_stdin = name == "-";
    if( is_stdin && ( stdin_used || isatty( fileno( stdin ) ) ) )
      ;		// ignore stdin if is repeated or is a terminal
    else if( is_stdin || name.empty() ||
             Menu::is_regular( name, &can_stat, &can_read ) || can_read )
      {
      try {
        const int i = Bufhandle_vector::find_or_add_handle( name, -1, line,
                                                            col, can_read );
        apply_file_options( Bufhandle_vector::handle( i ).buffer(), file_options );
        }
      catch( Buffer::Error & e )
        {
        if( first_post ) { first_post = false; std::fputc( '\n', stderr ); }
        std::fprintf( stderr, "warning: '%s': %s\n", name.c_str(), e.msg );
        read_error_found = true;
        }
      }
    else if( !add_handles_recursively( name, line, col, file_options,
                                       first_post, read_error_found ) )
      {
      if( first_post ) { first_post = false; std::fputc( '\n', stderr ); }
      std::fprintf( stderr, "warning: '%s': %s\n", name.c_str(),
                    can_stat ? "Unsupported file type" : "Can't access file" );
      if( can_stat ) unsupported_found = true; else read_error_found = true;
      }
    if( is_stdin ) stdin_used = true;
    }
  // add empty buffer if no files given or stdin is not a tty
  try { Bufhandle_vector::add_handle_if_pending_input_or_empty( line, col ); }
  catch( Buffer::Error & e )
    {
    if( first_post ) std::fputc( '\n', stderr );
    show_error( e.msg );
    return 1;
    }

  std::fputs( "done\n", stderr );
  delete parserp;
  if( read_error_found || unsupported_found )
    {
    if( read_error_found )
      std::fputs( "Warning: one or more files couldn't be read.\n", stderr );
    if( unsupported_found )
      std::fputs( "Warning: file types not supported will be ignored.\n", stderr );
    return 2;
    }
  return 0;
  }


int RC::read_options( const int argc, const char * const argv[] )
  {
  parserp = new Arg_parser( argc, argv, options, Arg_parser::in_order );
  if( parserp->error().size() )				// bad option
    { show_error( parserp->error().c_str(), 0, true ); return 1; }
  for( int i = 0; i < parserp->arguments(); ++i )
    if( parserp->code( i ) == 'h' || parserp->code( i ) == 'V' )
      return parserp->code( i );
  return 0;
  }


void RC::reset()
  {
  editor_options_.reset();
  default_file_options.reset();
  moeconf_file_options.reset();
  regex_options_vector.clear();
  }
