/* $Id: match.c,v 1.8 2005/09/21 18:38:21 dm Exp $ */
/*
*
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1