mirror of
git://git.gnu.org.ua/wordsplit.git
synced 2025-04-26 00:29:54 +03:00
Implement tilde and pathname expansion
* src/wordsplit.c (wordsplit_tildexpand) (wordsplit_pathexpand): New functions. (wordsplit_process_list): Run tilde and pathname expansion if WRDSF_PATHEXPAND flag is set. * src/wordsplit.h (wordsplit)<ws_options>: New member. (WRDSF_PATHEXPAND): New flag. (WRDSO_NULLGLOB,WRDSO_FAILGLOB) (WRDSO_DOTGLOB): New defines. * tests/wsp.c: New options pathexpand, nullglob, failglob, dotglob. Fix help output.
This commit is contained in:
parent
b591f0aee6
commit
08d3154b58
3 changed files with 251 additions and 5 deletions
214
src/wordsplit.c
214
src/wordsplit.c
|
@ -25,6 +25,8 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <pwd.h>
|
||||
#include <glob.h>
|
||||
|
||||
#if ENABLE_NLS
|
||||
# include <gettext.h>
|
||||
|
@ -1210,6 +1212,195 @@ wordsplit_trimws (struct wordsplit *wsp)
|
|||
}
|
||||
|
||||
static int
|
||||
wordsplit_tildexpand (struct wordsplit *wsp)
|
||||
{
|
||||
struct wordsplit_node *p;
|
||||
char *uname = NULL;
|
||||
size_t usize = 0;
|
||||
|
||||
for (p = wsp->ws_head; p; p = p->next)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if (p->flags & _WSNF_QUOTE)
|
||||
continue;
|
||||
|
||||
str = wsnode_ptr (wsp, p);
|
||||
if (str[0] == '~')
|
||||
{
|
||||
size_t i, size, dlen;
|
||||
size_t slen = wsnode_len (p);
|
||||
char *dir;
|
||||
struct passwd *pw;
|
||||
char *newstr;
|
||||
|
||||
for (i = 1; i < slen && str[i] != '/'; i++)
|
||||
;
|
||||
if (i == slen)
|
||||
continue;
|
||||
if (i > 1)
|
||||
{
|
||||
if (i > usize)
|
||||
{
|
||||
char *p = realloc (uname, i);
|
||||
if (!p)
|
||||
{
|
||||
free (uname);
|
||||
return _wsplt_nomem (wsp);
|
||||
}
|
||||
uname = p;
|
||||
usize = i;
|
||||
}
|
||||
--i;
|
||||
memcpy (uname, str + 1, i);
|
||||
uname[i] = 0;
|
||||
pw = getpwnam (uname);
|
||||
}
|
||||
else
|
||||
pw = getpwuid (getuid ());
|
||||
|
||||
if (!pw)
|
||||
continue;
|
||||
|
||||
dlen = strlen (pw->pw_dir);
|
||||
size = slen - i + dlen;
|
||||
newstr = malloc (size);
|
||||
if (!newstr)
|
||||
{
|
||||
free (uname);
|
||||
return _wsplt_nomem (wsp);
|
||||
}
|
||||
--size;
|
||||
|
||||
memcpy (newstr, pw->pw_dir, dlen);
|
||||
memcpy (newstr + dlen, str + i + 1, slen - i - 1);
|
||||
newstr[size] = 0;
|
||||
if (p->flags & _WSNF_WORD)
|
||||
free (p->v.word);
|
||||
p->v.word = newstr;
|
||||
p->flags |= _WSNF_WORD;
|
||||
}
|
||||
}
|
||||
free (uname);
|
||||
}
|
||||
|
||||
static int
|
||||
isglob (const char *s, int l)
|
||||
{
|
||||
while (l--)
|
||||
{
|
||||
if (strchr ("*?[", *s++))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
wordsplit_pathexpand (struct wordsplit *wsp)
|
||||
{
|
||||
struct wordsplit_node *p, *next;
|
||||
char *pattern = NULL;
|
||||
size_t patsize = 0;
|
||||
size_t slen;
|
||||
char *str;
|
||||
int flags = 0;
|
||||
|
||||
#ifdef GLOB_PERIOD
|
||||
if (wsp->ws_options & WRDSO_DOTGLOB)
|
||||
flags = GLOB_PERIOD;
|
||||
#endif
|
||||
|
||||
for (p = wsp->ws_head; p; p = next)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
next = p->next;
|
||||
|
||||
if (p->flags & _WSNF_QUOTE)
|
||||
continue;
|
||||
|
||||
str = wsnode_ptr (wsp, p);
|
||||
slen = wsnode_len (p);
|
||||
|
||||
if (isglob (str, slen))
|
||||
{
|
||||
int i;
|
||||
glob_t g;
|
||||
struct wordsplit_node *prev;
|
||||
|
||||
if (slen + 1 > patsize)
|
||||
{
|
||||
char *p = realloc (pattern, slen + 1);
|
||||
if (!p)
|
||||
return _wsplt_nomem (wsp);
|
||||
pattern = p;
|
||||
patsize = slen + 1;
|
||||
}
|
||||
memcpy (pattern, str, slen);
|
||||
pattern[slen] = 0;
|
||||
|
||||
switch (glob (pattern, flags, NULL, &g))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case GLOB_NOSPACE:
|
||||
free (pattern);
|
||||
return _wsplt_nomem (wsp);
|
||||
|
||||
case GLOB_NOMATCH:
|
||||
if (wsp->ws_options & WRDSO_NULLGLOB)
|
||||
{
|
||||
wsnode_remove (wsp, p);
|
||||
wsnode_free (p);
|
||||
}
|
||||
else if (wsp->ws_options & WRDSO_FAILGLOB)
|
||||
{
|
||||
char buf[128];
|
||||
if (wsp->ws_errno == WRDSE_USERERR)
|
||||
free (wsp->ws_usererr);
|
||||
snprintf (buf, sizeof (buf), _("no files match pattern %s"),
|
||||
pattern);
|
||||
free (pattern);
|
||||
wsp->ws_usererr = strdup (buf);
|
||||
if (!wsp->ws_usererr)
|
||||
return _wsplt_nomem (wsp);
|
||||
else
|
||||
return _wsplt_seterr (wsp, WRDSE_USERERR);
|
||||
}
|
||||
continue;
|
||||
|
||||
default:
|
||||
free (pattern);
|
||||
return _wsplt_seterr (wsp, WRDSE_GLOBERR);
|
||||
}
|
||||
|
||||
prev = p;
|
||||
for (i = 0; i < g.gl_pathc; i++)
|
||||
{
|
||||
struct wordsplit_node *newnode;
|
||||
char *newstr;
|
||||
|
||||
if (wsnode_new (wsp, &newnode))
|
||||
return 1;
|
||||
newstr = strdup (g.gl_pathv[i]);
|
||||
if (!newstr)
|
||||
return _wsplt_nomem (wsp);
|
||||
newnode->v.word = newstr;
|
||||
newnode->flags |= _WSNF_WORD|_WSNF_QUOTE;
|
||||
wsnode_insert (wsp, newnode, prev, 0);
|
||||
prev = newnode;
|
||||
}
|
||||
globfree (&g);
|
||||
|
||||
wsnode_remove (wsp, p);
|
||||
wsnode_free (p);
|
||||
}
|
||||
}
|
||||
free (pattern);
|
||||
}
|
||||
|
||||
static int
|
||||
skip_sed_expr (const char *command, size_t i, size_t len)
|
||||
{
|
||||
int state;
|
||||
|
@ -1653,6 +1844,16 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start)
|
|||
}
|
||||
}
|
||||
|
||||
if (wsp->ws_flags & WRDSF_PATHEXPAND)
|
||||
{
|
||||
wordsplit_tildexpand (wsp);
|
||||
if (wsp->ws_flags & WRDSF_SHOWDBG)
|
||||
{
|
||||
wsp->ws_debug ("After tilde expansion:");
|
||||
wordsplit_dump_nodes (wsp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand variables */
|
||||
if (!(wsp->ws_flags & WRDSF_NOVAR))
|
||||
{
|
||||
|
@ -1709,6 +1910,16 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start)
|
|||
wordsplit_dump_nodes (wsp);
|
||||
}
|
||||
|
||||
if (wsp->ws_flags & WRDSF_PATHEXPAND)
|
||||
{
|
||||
wordsplit_pathexpand (wsp);
|
||||
if (wsp->ws_flags & WRDSF_SHOWDBG)
|
||||
{
|
||||
wsp->ws_debug ("After path expansion:");
|
||||
wordsplit_dump_nodes (wsp);
|
||||
}
|
||||
}
|
||||
|
||||
return wsp->ws_errno;
|
||||
}
|
||||
|
||||
|
@ -1823,7 +2034,8 @@ const char *_wordsplit_errstr[] = {
|
|||
N_("unbalanced curly brace"),
|
||||
N_("undefined variable"),
|
||||
N_("input exhausted"),
|
||||
N_("unbalanced parenthesis")
|
||||
N_("unbalanced parenthesis"),
|
||||
N_("globbing error")
|
||||
};
|
||||
int _wordsplit_nerrs =
|
||||
sizeof (_wordsplit_errstr) / sizeof (_wordsplit_errstr[0]);
|
||||
|
|
|
@ -26,6 +26,7 @@ struct wordsplit
|
|||
size_t ws_offs;
|
||||
size_t ws_wordn;
|
||||
int ws_flags;
|
||||
int ws_options;
|
||||
const char *ws_delim;
|
||||
const char *ws_comment;
|
||||
const char *ws_escape;
|
||||
|
@ -122,11 +123,20 @@ struct wordsplit
|
|||
#define WRDSF_INCREMENTAL 0x20000000
|
||||
/* ws_command needs argv parameter */
|
||||
#define WRDSF_ARGV 0x40000000
|
||||
/* Perform pathname and tilde expansion */
|
||||
#define WRDSF_PATHEXPAND 0x80000000
|
||||
|
||||
#define WRDSF_DEFFLAGS \
|
||||
(WRDSF_NOVAR | WRDSF_NOCMD | \
|
||||
WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS | WRDSF_CESCAPES)
|
||||
|
||||
/* Remove the word that produces empty string after path expansion */
|
||||
#define WRDSO_NULLGLOB 0x01
|
||||
/* Print error message if path expansion produces empty string */
|
||||
#define WRDSO_FAILGLOB 0x02
|
||||
/* Allow a leading period to be matched by metacharacters. */
|
||||
#define WRDSO_DOTGLOB 0x04
|
||||
|
||||
#define WRDSE_OK 0
|
||||
#define WRDSE_EOF WRDSE_OK
|
||||
#define WRDSE_QUOTE 1
|
||||
|
@ -136,7 +146,8 @@ struct wordsplit
|
|||
#define WRDSE_UNDEF 5
|
||||
#define WRDSE_NOINPUT 6
|
||||
#define WRDSE_PAREN 7
|
||||
#define WRDSE_USERERR 8
|
||||
#define WRDSE_GLOBERR 8
|
||||
#define WRDSE_USERERR 9
|
||||
|
||||
int wordsplit (const char *s, struct wordsplit *p, int flags);
|
||||
int wordsplit_len (const char *s, size_t len,
|
||||
|
|
29
tests/wsp.c
29
tests/wsp.c
|
@ -56,6 +56,14 @@ struct kwd bool_keytab[] = {
|
|||
{ "default", WRDSF_DEFFLAGS },
|
||||
{ "env_kv", WRDSF_ENV_KV },
|
||||
{ "incremental", WRDSF_INCREMENTAL },
|
||||
{ "pathexpand", WRDSF_PATHEXPAND },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
struct kwd opt_keytab[] = {
|
||||
{ "nullglob", WRDSO_NULLGLOB },
|
||||
{ "failglob", WRDSO_FAILGLOB },
|
||||
{ "dotglob", WRDSO_DOTGLOB },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
@ -93,8 +101,13 @@ help ()
|
|||
putchar ('\n');
|
||||
for (i = 0; string_keytab[i].name; i++)
|
||||
{
|
||||
printf (" -%s\n", bool_keytab[i].name);
|
||||
printf (" %s ARG\n", bool_keytab[i].name);
|
||||
printf (" -%s\n", string_keytab[i].name);
|
||||
printf (" %s ARG\n", string_keytab[i].name);
|
||||
}
|
||||
putchar ('\n');
|
||||
for (i = 0; opt_keytab[i].name; i++)
|
||||
{
|
||||
printf (" [-]%s\n", opt_keytab[i].name);
|
||||
}
|
||||
putchar ('\n');
|
||||
printf (" -dooffs\n");
|
||||
|
@ -269,7 +282,8 @@ main (int argc, char **argv)
|
|||
size_t fenvmax = sizeof (fenvbase) / sizeof (fenvbase[0]);
|
||||
|
||||
progname = argv[0];
|
||||
|
||||
|
||||
ws.ws_options = 0;
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
char *opt = argv[i];
|
||||
|
@ -389,6 +403,15 @@ main (int argc, char **argv)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (kwxlat (opt_keytab, opt, &flag) == 0)
|
||||
{
|
||||
if (negate)
|
||||
ws.ws_options &= ~flag;
|
||||
else
|
||||
ws.ws_options |= flag;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strchr (opt, '='))
|
||||
{
|
||||
assert (fenvidx < fenvmax - 1);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue