Logo Search packages:      
Sourcecode: mailavenger version File versions  Download package

match.c

/* $Id$ */

/*
 *
 * Copyright (C) 2004 David Mazieres (dm@uun.org)
 *
 * 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 "avutil.h"
#include <sys/wait.h>
#include <ctype.h>
#include "vector.h"
#include "getopt_long.h"

typedef VECTOR(char *) strvec_t;
#define MATCH_RIGHT 0x1
#define MATCH_SLASH 0x2
#define MATCH_NOCASE 0x4
#define MATCH_PAREN 0x8
#define MATCH_RECURSING 0x100

char *progname;
int opt_fail = 67;

void
strvec_free (strvec_t *sv)
{
  int i;
  for (i = 0; i < sv->v_size; i++)
    free (sv->v_vec[i]);
  VECTOR_CLEAR (sv);
  VECTOR_INIT (sv);
}

static int
match_class (const char **patp, char c, int flags)
{
  const char *pat;
  int first = 1;
  int not = 0;
  int hit = 0;

  assert (**patp == '[');
  for (pat = *patp + 1;; pat++) {
    if (!*pat) {
      if (c != '[')
      return 0;
      (*patp)++;
      return 1;
    }
    if (*pat == '!' && first && !not) {
      not = 1;
      continue;
    }
    if (*pat == ']' && !first) {
      if (hit != not)
      *patp = pat + 1;
      return hit != not;
    }
    if (*pat == '\\' && pat[1])
      pat++;
    if (pat[1] == '-' && pat[2] && pat[2] != ']') {
      const char *next = pat + 2;
      if (*next == '\\' && next[1])
      next++;
      if (flags & MATCH_NOCASE) {
      if ((*pat <= tolower (c) && tolower (c) <= *next)
          || (*pat <= toupper (c) && toupper (c) <= *next))
        hit = 1;
      }
      else if (*pat <= c && c <= *next)
      hit = 1;
      pat = next;
    }
    else if (flags & MATCH_NOCASE) {
      if (tolower (*pat) == tolower (c))
      hit = 1;
    }
    else if (*pat == c)
      hit = 1;
    first = 0;
  }
}

struct match_state {
  const char *pat;
  const char *s;
};
int
match (strvec_t *mv, const char *pat, const char *s, int flags,
       struct match_state *msp)
{
  for (;;) {
    switch (*pat) {
    case '\0':
      if (msp)
      fprintf (stderr, "%s: unmatched '(' in pattern\n", progname);
      return !*s;
    case '(':
      if (!(flags & MATCH_PAREN))
      goto do_default;
      else if (mv) {
      struct match_state ms;
      size_t mn = mv->v_size;
      bzero (&ms, sizeof (ms));
      VECTOR_PUSH (mv, NULL);
      if (!match (mv, pat + 1, s, flags | MATCH_RECURSING, &ms) || !ms.s) {
        VECTOR_POP (mv);
        return 0;
      }
      mv->v_vec[mn] = xmalloc (ms.s - s + 1);
      memcpy (mv->v_vec[mn], s, ms.s - s);
      mv->v_vec[mn][ms.s - s] = '\0';
      pat = ms.pat + 1;
      s = ms.s;
      }
      else
      pat++;
      break;
    case ')':
      if (!(flags & MATCH_PAREN))
      goto do_default;
      if (!(flags & MATCH_RECURSING)) {
      fprintf (stderr, "%s: spurious ')' in pattern\n", progname);
      return 0;
      }
      else if (match (NULL, pat + 1, s, flags, NULL)) {
      if (msp) {
        msp->pat = pat;
        msp->s = s;
      }
      return 1;
      }
      else
      return 0;
      break;
    case '*':
      {
      const char *e;
      size_t mn = 0;
      if (mv && !(flags & MATCH_PAREN)) {
        mn = mv->v_size;
        VECTOR_PUSH (mv, NULL);
      }
      if (flags & MATCH_RIGHT) {
        e = s;
        for (pat++; !match (mv, pat, e, flags, msp); e++)
          if (!*e || (!(flags & MATCH_SLASH) && *e == '/')) {
            if (mv && !(flags & MATCH_PAREN))
            VECTOR_POP (mv);
            if (msp)
            msp->s = NULL;
            return 0;
          }
      }
      else {
        if ((flags & MATCH_SLASH) || !(e = strchr (s, '/')))
          e = s + strlen (s);
        for (pat++; !match (mv, pat, e, flags, msp); e--)
          if (e == s) {
            if (mv && !(flags & MATCH_PAREN))
            VECTOR_POP (mv);
            if (msp)
            msp->s = NULL;
            return 0;
          }
      }
      if (mv && !(flags & MATCH_PAREN)) {
        mv->v_vec[mn] = xmalloc (e - s + 1);
        memcpy (mv->v_vec[mn], s, e - s);
        mv->v_vec[mn][e - s] = '\0';
      }
      return 1;
      break;
      }
    case '?':
      if (!(flags & MATCH_SLASH) && *s == '/')
      return 0;
      if (!*s++)
      return 0;
      pat++;
      break;
    case '[':
      if (!*s)
      return 0;
      if (!(flags & MATCH_SLASH) && *s == '/' && pat[1] == '!')
      return 0;
      if (!match_class (&pat, *s++, flags))
      return 0;
      break;
    case '\\':
      if (pat[1])
      pat++;
      /* cascade */
    default:
    do_default:
      if ((flags & MATCH_NOCASE)) {
      if (tolower (*pat++) != tolower (*s++))
        return 0;
      }
      else if (*pat++ != *s++)
      return 0;
      break;
    }
  }
}

static void
run_cmd (char **av)
{
  pid_t pid;
  int status = -1;

  pid = fork ();
  if (pid == -1) {
    perror ("fork");
    exit (71);
  }
  if (!pid) {
    execv (av[0], av);
    perror (av[0]);
    _exit (72);
  }

  if (waitpid (pid, &status, 0) != pid) {
    perror ("waitpid");
    exit (71);
  }
  if (!status)
    return;
  if (WIFEXITED (status))
    exit (WEXITSTATUS (status));
  fprintf (stderr, "%s: died with signal %d\n", av[0], WTERMSIG (status));
  exit (75);
}

static void
file2vec (strvec_t *vp, const char *path)
{
  FILE *fp;
  struct lnbuf buf;
  int err;

  fp = fopen (path, "r");
  if (!fp) {
    perror (path);
    switch (errno) {
    case EIO:
    case ESTALE:
    case EAGAIN:
      exit (71);
      break;
    default:
      exit (opt_fail);
      break;
    }
  }

  bzero (&buf, sizeof (buf));
  while ((err = readln (&buf, fp, (size_t) -1)) == LNBUF_OK) {
    assert (buf.size > 0);
    buf.buf[buf.size - 1] = '\0';
    VECTOR_PUSH (vp, xstrdup (buf.buf));
  }
  switch (err) {
  case LNBUF_EOF:
    break;
  case LNBUF_EOFNL:
    VECTOR_PUSH (vp, xstrdup (buf.buf));
    break;
  default:
    exit (75);
    break;
  }
}

static void usage (void) __attribute__ ((noreturn));
static void
usage (void)
{
  fprintf (stderr, "usage: %s [-gilrs] [-n n] [-c cmd] [-x val]"
         " {[-p] pattern | -f file} str1 ...\n", progname);
  exit (64);
}
int
main (int argc, char **argv)
{
  struct option o[] = {
    { "version", no_argument, NULL, 'v' },
    { NULL, 0, NULL, 0 }
  };
  int c;
  strvec_t v, pv;
  int opt_flags = 0;
  int opt_num = 0;
  int opt_num_set = 0;
  char *opt_cmd = NULL;
  char *opt_file = NULL;
  int ok = 0;

  progname = strrchr (argv[0], '/');
  if (progname)
    progname++;
  else
    progname = argv[0];

  VECTOR_INIT (&pv);

  while ((c = getopt_long (argc, argv, "+ilrsc:f:gn:p:qx:", o, NULL)) != -1)
    switch (c) {
    case 'v':
      version (progname, 1);
      exit (0);
      break;
    case 'i':
      opt_flags |= MATCH_NOCASE;
      break;
    case 'l':
      opt_flags &= ~MATCH_RIGHT;
      break;
    case 'r':
      opt_flags |= MATCH_RIGHT;
      break;
    case 'c':
      opt_cmd = optarg;
      break;
    case 'f':
      if (opt_file)
      usage ();
      opt_file = optarg;
      break;
    case 'g':
      opt_flags |= MATCH_PAREN;
      if (!opt_num_set)
      opt_num = 1;
      break;
    case 'n':
      opt_num = atoi (optarg);
      opt_num_set = 1;
      break;
    case 'p':
      VECTOR_PUSH (&pv, xstrdup (optarg));
      break;
    case 'q':
      opt_num = -1;
      opt_num_set = 1;
      break;
    case 's':
      opt_flags |= MATCH_SLASH;
      break;
    case 'x':
      opt_fail = atoi (optarg);
      break;
    default:
      usage ();
      break;
    }

  if (!pv.v_size && !opt_file) {
    if (optind >= argc)
      usage ();
    VECTOR_PUSH (&pv, xstrdup (argv[optind++]));
  }
  if (opt_file)
    file2vec (&pv, opt_file);

  VECTOR_INIT (&v);
  for (; optind < argc; optind++) {
    int i;
    if (opt_cmd) {
      VECTOR_PUSH (&v, NULL);
      VECTOR_PUSH (&v, NULL);
      VECTOR_PUSH (&v, NULL);
      VECTOR_PUSH (&v, NULL);
    }
    for (i = 0; i < pv.v_size; i++)
      if (match (&v, pv.v_vec[i], argv[optind], opt_flags, NULL)) {
      ok = 1;
      if (opt_cmd) {
        v.v_vec[0] = xstrdup ("/bin/sh");
        v.v_vec[1] = xstrdup ("-c");
        v.v_vec[2] = xstrdup (opt_cmd);
        v.v_vec[3] = xstrdup (argv[optind]);
        VECTOR_PUSH (&v, NULL);
        run_cmd (v.v_vec);
      }
      else if (!opt_num)
        printf ("%s\n", argv[optind]);
      else if (opt_num > 0 && opt_num <= v.v_size)
        printf ("%s\n", v.v_vec[opt_num - 1]);
      break;
      }
    strvec_free (&v);
  }

  if (ok)
    exit (0);
  exit (opt_fail);
}

Generated by  Doxygen 1.6.0   Back to index