/* * glob.c * * Full file wildcard matching * * © 1998 Straylight/Edgeware */ /*----- Licensing note ----------------------------------------------------* * * This file is part of Straylight's core utilities (coreutils). * * Coreutils 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. * * Coreutils 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 coreutils. If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*----- Header files ------------------------------------------------------*/ #include #include #include #include #include "swis.h" #include "swiv.h" #include "alloc.h" #include "gf.h" #include "glob.h" /*----- Type definitions --------------------------------------------------*/ typedef struct glob_ctx { char buf[1024]; /* Pointer to output buffer */ void (*proc)(const char *, void *); /* User procedure */ void *ctx; /* Context to pass the procedure */ int done; /* Number of matches found */ } glob_ctx; /*----- Main code ---------------------------------------------------------*/ /* --- Wildcard syntax --- * * * The wildcards `*' and `#' do what they normally do: match zero-or-more * and any-one characters respectively. Additionally, a `.' where a * filename is expected will search recursively down the directory * structure. Only globby matches with really existing things are counted; * if a name contains nothing globbable then it matches nothing. This is * done for speed: I expect the client to use a name literally if it fails * to match anything. The syntax is a sort of blend between traditional * RISC OS and kpathsea. */ /* --- Hack note --- * * * This will fail gloriously given something like `adfs::ak*ha.$.foo.*'. * Do I look like I care? This program has already taken five times longer * than it should have done because I decided I wanted to do wildcarding. */ /* --- Forward reference --- */ static void glob_do(glob_ctx *g, char *p, char *in); /* --- @glob_got@ --- * * * Arguments: @glob_ctx *g@ = pointer to my context * @char *p@ = pointer to current position in buffer * @char *fn@ = filename to add * @char *in@ = rest of wildcard pattern to match * * Returns: --- * * Use: Handles a matched filename in the glob matcher. */ static void glob_got(glob_ctx *g, char *p, char *fn, char *in) { size_t sz; /* --- Build the filename --- */ if (p != g->buf) *p++ = '.'; sz = strlen(fn); memcpy(p, fn, sz + 1); p += sz; /* --- See if this is the final element --- */ if (!in) { int ty; if (_swix(OS_File, _inr(0, 1) | _out(0), 17, g->buf, &ty) || !ty) return; g->done++; g->proc(g->buf, g->ctx); } else glob_do(g, p, in); } /* --- @glob_match@ --- * * * Arguments: @const char *pat@ = pointer to pattern string * @const char *s@ = pointer to candidate string * * Returns: Nonzero if pattern matches candidate. * * Use: Tries to match globbily. This is very simple stuff. * I may add character classes later if I feel really eager. */ static int glob_match(const char *pat, const char *s) { for (;;) { if (!*pat && !*s) return (1); else if (!*pat) return (0); else if (*pat == '*') { do pat++; while (*pat == '*'); do { if (glob_match(pat, s)) return (1); } while (*s++); return (0); } else if (!*s) return (0); else if (*pat != '#' && tolower(*pat) != tolower(*s)) return (0); else pat++, s++; } } /* --- @glob_do@ --- * * * Arguments: @glob_ctx *g@ = pointer to my context * @char *p@ = pointer to current position in buffer * @char *in@ = rest of wildcard pattern to match * * Returns: --- * * Use: Main recursive glob matcher. */ static void glob_do(glob_ctx *g, char *p, char *in) { char *q; char *rec = 0; /* --- Pick out the next component of the pathname --- */ if (*in == '.') { do in++; while (*in == '.'); rec = in - 1; if (!*in) in = "*"; } q = strchr(in, '.'); if (q) *q++ = 0; else q = 0; /* --- See if this contains any wildcards --- */ if (rec) { gf_ctx gx; char *n; char *qq = q ? q - 1 : 0; for (gf_init(&gx, "*", g->buf); *p = 0, (n = gf_next(&gx)) != 0; ) { if (qq) *qq = 0; if (glob_match(in, n)) glob_got(g, p, n, q); if (qq) *qq = '.'; glob_got(g, p, n, rec); } } else if (strpbrk(in, "*#")) { gf_ctx gx; char *n; for (gf_init(&gx, in, g->buf); *p = 0, (n = gf_next(&gx)) != 0; ) glob_got(g, p, n, q); } else glob_got(g, p, in, q); } /* --- @glob@ --- * * * Arguments: @const char *pat@ = pointer to pattern string to match * @void (*proc)(const char *, void *)@ = client function * @void *ctx@ = context pointer to pass function * * Returns: Number of filenames matched. * * Use: Does filename globbing. */ int glob(const char *pat, void (*proc)(const char *, void *), void *ctx) { glob_ctx gx; char *in; if (!strpbrk(pat, "*#") && !strstr(pat, "..")) return (0); gx.proc = proc; gx.ctx = ctx; gx.done = 0; in = xstrdup(pat); glob_do(&gx, gx.buf, in); free(in); return (gx.done); } /*----- That's all, folks -------------------------------------------------*/