
/* glob.c file filter for grep.*/
/* 
   Copyright 2000 Edscott Wilson Garcia

   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, 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, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  */

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

#include "globber.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#ifdef HAVE_SNPRINTF
#  include "snprintf.h"
#endif

#ifdef DMALLOC
#  include "dmalloc.h"
#endif

/** tripas */
#define VERSION_NAME "\nglob 0.5.0\n\nCopyright 2000-2001 Edscott Wilson Garcia\n\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"

#define GREP "grep"

static void *object = NULL;
static int initial;
static int terminated = 0;
static char *token;
static int options = 0, type = 0;
static long size = 0;
static long month_t = 0;
static long unsigned day_t = 0;
static long unsigned hour_t = 0;
static long unsigned min_t = 0;

#define GLOBRUN_PID     	0x01
#define GLOBRUN_COUNT   	0x02
#define GLOBRUN_FILTERED   	0x04
#define GLOBRUN_IGNORE_CASE	0x08
#define GLOBRUN_REG_EXP  	0x10
#define GLOBRUN_INVERT       	0x20
/*#define GLOBRUN_WHATEVER        	0x40*/
#define GLOBRUN_WORDS_ONLY   	0x80
#define GLOBRUN_LINES_ONLY   	0x100
#define GLOBRUN_ZERO_BYTE    	0x200
#define GLOBRUN_NOBINARIES   	0x400
#define GLOBRUN_RECURSIVE    	0x800
#define GLOBRUN_VERBOSE     	0x1000
#define GLOBRUN_XDEV         	0x2000

#define MAX_ARG 25

static int
display (char *input)
{
  if (terminated)
    return terminated;		/* die quietly and quickly */
  printf ("%s\n", input);
  if (time (NULL) - initial > 3)
  {
    fflush (NULL);
    initial = time (NULL);
  }
  return terminated;
}

static int
grep (char *file)
{
  static char *arguments[MAX_ARG];
  int status = 0;
  if (terminated)
    return terminated;		/* die quietly and quickly */

  arguments[status++] = "grep";
  arguments[status++] = "-d";
  arguments[status++] = "skip";
  arguments[status++] = "-H";
  if (options & GLOBRUN_NOBINARIES)
    arguments[status++] = "-I";
  if (options & GLOBRUN_IGNORE_CASE)
    arguments[status++] = "-i";
  if (options & GLOBRUN_WORDS_ONLY)
    arguments[status++] = "-w";
  if (options & GLOBRUN_LINES_ONLY)
    arguments[status++] = "-x";
  if (options & GLOBRUN_ZERO_BYTE)
    arguments[status++] = "-Z";

  if ((options & GLOBRUN_COUNT) && (options & GLOBRUN_INVERT))
  {
    arguments[status++] = "-c";
    arguments[status++] = "-v";
  }
  if ((options & GLOBRUN_COUNT) && !(options & GLOBRUN_INVERT))
  {
    arguments[status++] = "-c";
  }
  if (!(options & GLOBRUN_COUNT) && (options & GLOBRUN_INVERT))
  {
    arguments[status++] = "-L";
  }
  if (!(options & GLOBRUN_COUNT) && !(options & GLOBRUN_INVERT))
  {
    arguments[status++] = "-l";
  }

  if (options & GLOBRUN_REG_EXP)
    arguments[status++] = "-E";
  else
    arguments[status++] = "-e";
  arguments[status++] = token;

  arguments[status++] = file;
  arguments[status++] = (char *) 0;
  if (options & GLOBRUN_VERBOSE)
  {
    int i;
    for (i = 0; i < status; i++)
      printf ("%s ", arguments[i]);
    printf ("\n");
  }

  if (fork () == 0)
  {
    execvp (GREP, arguments);
    fprintf (stderr, "%s not found in path!\n", GREP);
    exit (1);
  }
  wait (&status);

  /*fflush(NULL); */
  return terminated;
}


static char *message[] = {
  " [-vVPrMACaiIyLcwxZ] [-fpotkhsmudgeE (option)] path \n\n",
  "options: \n" "          [-r] [-v] [-d ddd] [-m mmm] [-f filter] [-s (+/-)size]\n",
  "          [-t type] [-p perm] [grep options...] \n",
  "-v            = verbose\n",
  "-V            = print version number information\n",
  "-a            = stay on a single filesystem.\n",
  "-P            = print process id (capital P)\n",
  "-f filter     = file filter (enclosed in quotes if regexp *,? or\n",
  "                [] is used)\n",
  "-r            = recursive\n",
  "-s +kbytes    = size greater than kbytes KBYTES\n",
  "-s -kbytes    = size less than kbytes KBYTES\n",
  "-p perm       = perm is either suid | exe\n",
  "-o octal_mode = octal mode is the file mode in octal notation\n",
  "-t type       = any | reg | dir | sym | sock | blk | chr | fifo\n",
  "                (any, regular, directory, symlink, socket, blk_dev,\n",
  "                 chr_dev, fifo: any is the default.)\n",
  "  * Time options must be used with either  -M, -C, or -A.\n" "-k min        = file time in the previous (int) min minutes (either -M -C -A)\n",
  "-h hhh        = file time in the previous (int) hh hours (either -M -C -A)\n",
  "-d ddd        = file time in the previous (int) dd days (either -M -C -A)\n",
  "-m mmm        = file time in the previous (int) mm months (either -M -C -A)\n",
  "-M            = use mtime for file (modification time: mknod, truncate,\n",
  "                utime,write  \n",
  "-A            = use atime for file (access time: exec, mknod, pipe,\n",
  "                utime, read)  \n",
  "-C            = use ctime for file (change time: setting inode information\n",
  "                i.e., owner, group, link count, mode, etc.) \n",
  "-u user-id    = only files matching numeric user-id\n",
  "-g group-id   = only files matching numeric group-id\n",
  "-Z            = Output  a  zero  byte  (the  ASCII  NUL  character)\n",
  "                instead of the character that  normally  follows  a\n",
  "                file  name (never tested option, if you do, email me)\n",
  "\n",
  "**specifying these option will be used in content search (grep):\n",
  "-e string     = containing string (if *,? or [], use quotes)\n",
  "-E regexp     = containing regexp: (use quotes amigo). \n",
  "-i            = ignore case (for search string -c)\n",
  "-I            = do not search into binary files\n",
  "-y            = same as -i (obsolete)\n",
  "-L            = print the  name  of each input file from which *no*\n",
  "                output would normally have been printed.\n",
  "-c            = only print a count of matching lines for each input\n",
  "                file.\n",
  "-w            = Select  only  those  lines  containing matches that\n",
  "               form whole words. Word-constituent  characters  are\n",
  "               letters, digits, and the underscore.\n",
  "-x            = Select only those matches that  exactly  match  the\n",
  "                whole line.\n",
  "\n",
  NULL
};

void
finish (int sig)
{
  /*printf("\n****\nglob terminated by signal\n****\n"); */
  terminated = 1;
  fflush (NULL);
}

void
halt (int sig)
{
  fflush (NULL);
  globber_destroy (object);
  exit (1);
}


#define CHECK_ARG if (argc <= i) goto error;
int
main (int argc, char **argv)
{
  int i, timetype = 0;
  char *filter = NULL, globbered = 0;
  int (*operate) (char *) = display;
  initial = time (NULL);

  /* initializations  */
  signal (SIGHUP, halt);
  signal (SIGSEGV, finish);
  signal (SIGKILL, finish);
  signal (SIGTERM, finish);


  if (argc < 2)
  {
  error:
    fprintf (stdout, "use:  %s ", argv[0]);
    i = 0;
    while (message[i])
      fprintf (stdout, "%s", message[i++]);
    exit (1);
  }
  object = globber_create ();
  for (i = 1; i < argc; i++)
  {
    if (argv[i][0] == '-')
    {
      /* options for the globber : **************** */
      if (strstr (argv[i], "M") != NULL)
      {
	timetype = 1;
	glob_set_options (object, GLOBBER_MTIME);
	continue;
      }
      if (strstr (argv[i], "A") != NULL)
      {
	timetype = 1;
	glob_set_options (object, GLOBBER_ATIME);
	continue;
      }
      if (strstr (argv[i], "C") != NULL)
      {
	timetype = 1;
	glob_set_options (object, GLOBBER_CTIME);
	continue;
      }
      if (strstr (argv[i], "a") != NULL)
      {
	glob_set_options (object, GLOBBER_XDEV);
	options |= GLOBRUN_XDEV;
	continue;
      }
      if (strstr (argv[i], "v") != NULL)
      {
	glob_set_options (object, GLOBBER_VERBOSE);
	options |= GLOBRUN_VERBOSE;
	continue;
      }
      if (strstr (argv[i], "r") != NULL)
      {
	glob_set_options (object, GLOBBER_RECURSIVE);
	options |= GLOBRUN_RECURSIVE;
	continue;
      }
      if (strstr (argv[i], "u") != NULL)
      {
	i++;
	CHECK_ARG;
	glob_set_user (object, atol (argv[i]));
	continue;
      }
      if (strstr (argv[i], "g") != NULL)
      {
	i++;
	CHECK_ARG;
	glob_set_group (object, atol (argv[i]));
	continue;
      }


      if (strstr (argv[i], "t") != NULL)
      {
	i++;
	type &= 07777;
	CHECK_ARG;
	/*if (strcmp (argv[i], "any") == 0) type &= 07777; */
	if (strcmp (argv[i], "reg") == 0)
	  type |= S_IFREG;
	if (strcmp (argv[i], "dir") == 0)
	  type |= S_IFDIR;
	if (strcmp (argv[i], "sym") == 0)
	  type |= S_IFLNK;
	if (strcmp (argv[i], "sock") == 0)
	  type |= S_IFSOCK;
	if (strcmp (argv[i], "blk") == 0)
	  type |= S_IFBLK;
	if (strcmp (argv[i], "chr") == 0)
	  type |= S_IFCHR;
	if (strcmp (argv[i], "fifo") == 0)
	  type |= S_IFIFO;
	if (strcmp (argv[i], "any") != 0)
	{
	  glob_set_options (object, GLOBBER_TYPE);
	  glob_set_type (object, type);
	}
	continue;
      }
      if (strstr (argv[i], "p") != NULL)
      {
	i++;
	/*type &= S_IFMT; */
	CHECK_ARG;
	if (strcmp (argv[i], "suid") == 0)
	  type |= S_ISUID;
	if (strcmp (argv[i], "exe") == 0)
	  type |= S_IXUSR;
	glob_set_options (object, GLOBBER_PERM);
	glob_set_type (object, type);
	continue;
      }
      if (strstr (argv[i], "o") != NULL)
      {
	int valor;
	i++;
	type &= S_IFMT;
	CHECK_ARG;
	sscanf (argv[i], "%o", &valor);
	type |= (07777 & valor);
	glob_set_options (object, GLOBBER_PERM);
	glob_set_type (object, type);
	continue;
      }

      if (strstr (argv[i], "s") != NULL)
      {
	i++;
	CHECK_ARG;
	size = atol (argv[i]);
	if (size < 0)
	  glob_set_sizeL (object, -size * 1024);
	else
	  glob_set_sizeG (object, size * 1024);
	continue;
      }

      if (strstr (argv[i], "k") != NULL)
      {
	i++;
	CHECK_ARG;
	min_t = atol (argv[i]);
	glob_set_time (object, month_t, day_t, hour_t, min_t);
	continue;
      }
      if (strstr (argv[i], "h") != NULL)
      {
	i++;
	CHECK_ARG;
	hour_t = atol (argv[i]);
	glob_set_time (object, month_t, day_t, hour_t, min_t);
	continue;
      }
      if (strstr (argv[i], "d") != NULL)
      {
	i++;
	CHECK_ARG;
	day_t = atol (argv[i]);
	glob_set_time (object, month_t, day_t, hour_t, min_t);
	continue;
      }
      if (strstr (argv[i], "m") != NULL)
      {
	CHECK_ARG;
	month_t = atol (argv[i]);
	glob_set_time (object, month_t, day_t, hour_t, min_t);
	continue;
      }


      if (strstr (argv[i], "f") != NULL)
      {
	options |= GLOBRUN_FILTERED;
	i++;
	CHECK_ARG;
	filter = argv[i];
	if (options & GLOBRUN_VERBOSE)
	  fprintf (stderr, "filtering %s\n", filter);
	continue;
      }
      /* options for grep : ****************** */
      if (strstr (argv[i], "I") != NULL)
      {
	options |= GLOBRUN_NOBINARIES;
	continue;
      }
      if ((strstr (argv[i], "i") != NULL) || (strstr (argv[i], "y") != NULL))
      {
	options |= GLOBRUN_IGNORE_CASE;
	continue;
      }
      if (strstr (argv[i], "L") != NULL)
      {
	options |= GLOBRUN_INVERT;
	continue;
      }
      if (strstr (argv[i], "c") != NULL)
      {
	options |= GLOBRUN_COUNT;
	continue;
      }
      if (strstr (argv[i], "w") != NULL)
      {
	options |= GLOBRUN_WORDS_ONLY;
	continue;
      }
      if (strstr (argv[i], "x") != NULL)
      {
	options |= GLOBRUN_LINES_ONLY;
	continue;
      }
      if (strstr (argv[i], "Z") != NULL)
      {
	options |= GLOBRUN_ZERO_BYTE;
	continue;
      }
      if (strstr (argv[i], "P") != NULL)
      {
	options |= GLOBRUN_PID;
	printf ("PID=%d\n", (int) getpid ());
	fflush (NULL);
	continue;
      }
      if (strstr (argv[i], "E") != NULL)
      {
	i++;
	CHECK_ARG;
	token = argv[i];
	operate = grep;
	options |= GLOBRUN_REG_EXP;
	continue;
      }
      if (strstr (argv[i], "e") != NULL)
      {
	i++;
	CHECK_ARG;
	token = argv[i];
	operate = grep;
	options |= GLOBRUN_REG_EXP;
	options ^= GLOBRUN_REG_EXP;	/* turn off extended regexp */
	continue;
      }

      if (strstr (argv[i], "V") != NULL)
      {
	printf ("%s", VERSION_NAME);
	return 0;
      }
      fprintf (stdout, "unknown argument: %s\nuse -h for help.\n", argv[i]);
      exit (1);
    }
    if (((min_t) || (hour_t) || (day_t) || (month_t)) && !timetype)
      glob_set_options (object, GLOBBER_MTIME);
    terminated = globber (object, argv[i], operate, filter);
    globbered = 1;
  }				/* end of argument processing */


  if (!globbered)
  {
    fprintf (stderr, "must specify path\n");
    goto error;
  }
/*	if (terminated) printf("glob run was terminated.\n");*/
  if (!terminated)
  {				/* die quietly and quickly */
    if (options & GLOBRUN_PID)
      printf ("GLOB DONE=%d\n", (int) getpid ());
  }
  fflush (NULL);
  globber_destroy (object);
  exit (0);
}
