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:
Sergey Poznyakoff 2014-10-24 10:22:38 +03:00
parent b591f0aee6
commit 08d3154b58
3 changed files with 251 additions and 5 deletions

View file

@ -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]);

View file

@ -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,

View file

@ -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");
@ -270,6 +283,7 @@ main (int argc, char **argv)
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);