/*
 * grep.cpp -- a minimal grep-like program
 * usage: grep pattern [files...]
 *
 */

#include <iostream>     // for basic io
#include <fstream>      // for files
#include <string>       // for string class
#include <cerrno>       // for errno
#include <cstring>      // for strerror()
#include <cstdlib>      // for exit() in usage()


void gr( std::istream& in, std::string pattern)
{
  std::string  line;
  while ( getline( in, line) )
  {
    if ( std::string::npos != line.find(pattern) )
    {
      std::cout << line << std::endl;
    }
  }
}


int main( int argc, char *argv[])
{
  if ( argc  < 2 )  // missing pattern
  {
    std::cerr << "Usage: " << argv[0] << " pattern [files...]" << std::endl;
    exit(1); // terminate program
  }
  else if ( argc < 3 ) // missing filenames
  {
    gr( std::cin, argv[1]);
  }
  else
  {
    for ( int i = 2; i < argc; ++i )  // start from i for file arguments
    {
      std::ifstream inpfile( argv[i], std::ios_base::in);   // constructor

      if ( ! inpfile )  // converted to logical value
      {
        std::setlocale(LC_MESSAGES, "en_EN.utf8");
        std::cerr << argv[i] << ": " << std::strerror(errno) << std::endl;
      }
      else
      {
        gr( inpfile, argv[1]);
      }
    }  // inpfile closes automatically -  inpfile destructor called  
  }
  return 0;
}




/***********************************************************************/




/*
 * grep.cpp -- a simple grep-like program
 * usage: gr [-iv] pattern [files...]
 *
 */

#include <iostream>     // for basic io
#include <fstream>      // for files
#include <string>       // for string class
#include <cctype>       // for toupper()
#include <cerrno>       // for errno
#include <cstring>      // for strerror()
#include <cstdlib>      // for exit() in usage()
// #include <algorithm> // for transform()

struct param_t
{
  int         iflag;    /* case insensitive on */
  int         vflag;    /* negate on           */
  std::string pattern;  /* pattern to search   */
  std::string upattern; /* upper case pattern  */
};

void        usage( char *prname);
std::string toUpper(std::string s);
int         doParams( param_t& p, int argc, char *argv[]);
void        gr( param_t p, std::istream& in, std::ostream& out);


void usage( char *prname)
{
  std::cerr << "Usage: " << prname << " [-iv] pattern [files...]" << std::endl;
  exit(1); // terminate program
}

std::string toUpper( std::string s)
{
  std::string upper;
  for (size_t i = 0; i < s.size(); ++i)
      upper += std::toupper(s[i]);

/* more C++-like solution would be: #include <algorithm>
  std::transform( s.begin(), s.end(), std::back_inserter(upper), ::toupper);
 */

  return upper;
}

int doParams( param_t& p, int argc, char *argv[])
{
  int i = 1;

  p.iflag = 0;
  p.vflag = 0;

  /* letter flags first */
  while ( i < argc  &&  '-' == argv[i][0] )
  {
    int j = 1;
    while ( '\0' != argv[i][j] )
    {
      switch(argv[i][j])
      {
      case 'i': p.iflag = 1; break;
      case 'v': p.vflag = 1; break;
      default : std::cerr << "Invalid flag: " << argv[i][j] << std::endl;                       
                usage(argv[0]); /* invalid flag: exit() */
      }
      ++j;
    }
    ++i;
  }
  /* end of flags, pattern should come here */
  if ( i >= argc )
  {
    std::cerr << "No pattern was given" << std::endl;
    usage(argv[0]);      /* no pattern: exit() */
  }
  p.pattern = argv[i];   /* ok, pattern found */

  if ( p.iflag )  /* case insensitive */
  {
    p.upattern = toUpper(p.pattern);  /* prepare uppercase patern */
  }
  return ++i;  /* continue from next parameter */
}

void gr( param_t p, std::istream& in, std::ostream& out)
{
  std::string  line;
  while ( getline( in, line) )
  {
    bool isMatch = false; /* true if matches */

    if ( p.iflag )
    {
      std::string upper = toUpper(line);
      isMatch = (std::string::npos != upper.find(p.upattern));
    }
    else
    {
      isMatch = (std::string::npos != line.find(p.pattern));
    }

    if ( p.vflag )
    {
      isMatch = ! isMatch;
    }

    if ( isMatch )
    {
      out << line << std::endl;
    }
  }
}


int main( int argc, char *argv[])
{
  param_t params;
  int i = doParams( params, argc, argv);

#ifdef DEBUG
  std::cerr << "iflag    = " << params.iflag    << std::endl;
  std::cerr << "vflag    = " << params.vflag    << std::endl;
  std::cerr << "pattern  = " << params.pattern  << std::endl;
  std::cerr << "upattern = " << params.upattern << std::endl;
  std::cerr << "i == "       << i               << std::endl;
#endif /* DEBUG */

  if ( i == argc )  // i is the first file argument
  {
    gr( params, std::cin, std::cout);
  }
  else
  {
    for ( ; i < argc; ++i )  // start from i for file arguments
    {
      std::ifstream inpfile( argv[i], std::ios_base::in);   // constructor

      if ( ! inpfile )  // converted to logical value
      {
        std::setlocale(LC_MESSAGES, "en_EN.utf8");
        std::cerr << argv[i] << ": " << std::strerror(errno) << std::endl;
      }
      else
      {
        gr( params, inpfile, std::cout);
      }
    }  // inpfile closes automatically -  inpfile destructor called  
  }
  return 0;
}



/********************************************************************/




/*
 * gr.c -- a simple grep-like program in pure c
 * usage: gr [-ivw] pattern [files...]
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define BUFSIZE 1024

struct param_s
{
  int  iflag;     /* case insensitive on */
  int  vflag;     /* negation on         */
  int  wflag;     /* word regex on       */
  char *pattern;  /* pattern to search   */
  char *upattern; /* upper case pattern  */
};

void usage( char *prname)
{
  fprintf( stderr, "Usage: %s [-ivw] pattern [files...]\n", prname);
  exit(1);
}

int do_params( struct param_s *p, int argc, char *argv[])
{
  int i = 1;

  p->iflag = 0;
  p->vflag = 0;
  p->wflag = 0;

  /* letter flags first */
  while ( i < argc  &&  '-' == argv[i][0] )
  {
    int j = 1;
    while ( '\0' != argv[i][j] )
    {
      switch(argv[i][j])
      {
      case 'i': p->iflag = 1; break;
      case 'v': p->vflag = 1; break;
      case 'w': p->wflag = 1; break;
      default : fprintf( stderr, "Invalid flag: %c\n", argv[i][j]);
                usage(argv[0]); /* invalid flag: exit() */
      }
      ++j;
    }
    ++i;
  }
  /* end of flags, pattern should come here */
  if ( i >= argc )
  {
    fprintf( stderr, "No pattern was given\n");
    usage(argv[0]);      /* no pattern: exit() */
  }
  p->pattern = argv[i];   /* ok, pattern found */

  if ( p->iflag )  /* case insensitive */
  {
    int k;
    p->upattern = (char *) malloc(strlen(p->pattern)+1);
    for ( k = 0; k < strlen(p->pattern)+1; ++k)
    {
      p->upattern[k] = toupper(p->pattern[k]);
    }
  }
  return ++i;  /* continue from next parameter */
}

int is_delim( char ch)
{
  return !( isalpha(ch) || isdigit(ch) || '_' == ch );
}

int wmatch( char *buffer, char *pattern, char *where)
{
  char *before = where - 1;
  char *after =  where + strlen(pattern);

  return (  where == buffer  &&  is_delim(*after) ) ||
         (  is_delim(*before)  &&  '\n' == *after ) ||
         (  is_delim(*before) && is_delim(*after) );
}

void gr( struct param_s *p, FILE *in, FILE *out)
{
  char buffer[BUFSIZE];
  while ( NULL != fgets( buffer, BUFSIZE, in) )
  {
    int is_match  = 0; /* true if matches */
    char *where   = 0; /* pointer to beginning of matching */

    if ( p->iflag )
    {
      char ubuffer[BUFSIZE];
      int k;
      for ( k = 0; k < strlen(buffer)+1; ++k)
      {
        ubuffer[k] = toupper(buffer[k]);
      }
      is_match = ( NULL != (where = strstr( ubuffer, p->upattern)) );
      if ( is_match && p->wflag )
        is_match = wmatch( ubuffer, p->upattern, where);
    }
    else
    {
      is_match = ( NULL != (where = strstr( buffer, p->pattern)) );
      if ( is_match && p->wflag )
        is_match = wmatch( buffer, p->pattern, where);
    }

    if ( p->vflag )
    {
      is_match = ! is_match;
    }

    if ( is_match )
    {
      fputs( buffer, out);
    }
  }
}


int main( int argc, char *argv[])
{
  struct param_s params;
  int i = do_params( &params, argc, argv);

#ifdef DEBUG
  fprintf( stderr, "iflag    = %d\n", params.iflag);
  fprintf( stderr, "vflag    = %d\n", params.vflag);
  fprintf( stderr, "wflag    = %d\n", params.wflag);
  fprintf( stderr, "pattern  = %s\n", params.pattern);
  fprintf( stderr, "upattern = %s\n", params.upattern);
  fprintf( stderr, "i == %d\n", i);
#endif /* DEBUG */

  if ( i == argc )
  {
    gr( &params, stdin, stdout);
  }
  else
  {
    for ( ; i < argc; ++i)
    {
      FILE *fp = fopen( argv[i], "r");
      if ( NULL != fp )
      {
        gr( &params, fp, stdout);
        fclose(fp);
      }
      else
      {
        fprintf( stderr, "Can't open %s for read\n", argv[i]);
      }
    }
  }
  return 0;
}