/* $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 #include #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); }