}
-/* ============ Path search helper
- *
+/* ============ Path search helper */
+static const char *
+legal_pathopt(const char *opt, const char *term, int magic)
+{
+ switch (magic) {
+ case 0:
+ opt = NULL;
+ break;
+
+ case 1:
+ opt = prefix(opt, "builtin") ?: prefix(opt, "func");
+ break;
+
+ default:
+ opt += strcspn(opt, term);
+ break;
+ }
+
+ if (opt && *opt == '%')
+ opt++;
+
+ return opt;
+}
+
+/*
* The variable path (passed by reference) should be set to the start
* of the path before the first call; padvance will update
* this value as it proceeds. Successive calls to padvance will return
* a percent sign) appears in the path entry then the global variable
* pathopt will be set to point to it; otherwise pathopt will be set to
* NULL.
+ *
+ * If magic is 0 then pathopt recognition will be disabled. If magic is
+ * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
+ * pathopt.
*/
static const char *pathopt; /* set by padvance */
static int
-padvance(const char **path, const char *name)
+padvance_magic(const char **path, const char *name, int magic)
{
+ const char *term = "%:";
+ const char *lpathopt;
const char *p;
char *q;
const char *start;
+ size_t qlen;
size_t len;
if (*path == NULL)
return -1;
+
+ lpathopt = NULL;
start = *path;
- for (p = start; *p && *p != ':' && *p != '%'; p++)
- continue;
- len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
- q = growstackto(len);
- if (p != start) {
- q = mempcpy(q, start, p - start);
- *q++ = '/';
+
+ if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
+ lpathopt = start + 1;
+ start = p;
+ term = ":";
}
- strcpy(q, name);
- pathopt = NULL;
+
+ len = strcspn(start, term);
+ p = start + len;
+
if (*p == '%') {
- pathopt = ++p;
- while (*p && *p != ':')
- p++;
+ size_t extra = strchrnul(p, ':') - p;
+
+ if (legal_pathopt(p + 1, term, magic))
+ lpathopt = p + 1;
+ else
+ len += extra;
+
+ p += extra;
}
- if (*p == ':')
- *path = p + 1;
- else
- *path = NULL;
- return len;
+
+ pathopt = lpathopt;
+ *path = *p == ':' ? p + 1 : NULL;
+
+ /* "2" is for '/' and '\0' */
+ qlen = len + strlen(name) + 2;
+ q = growstackto(qlen);
+
+ if (len) {
+ q = mempcpy(q, start, len);
+ *q++ = '/';
+ }
+ strcpy(q, name);
+
+ return qlen;
+}
+
+static int
+padvance(const char **path, const char *name)
+{
+ return padvance_magic(path, name, 1);
}
}
/*
- * Clear out command entries. The argument specifies the first entry in
- * PATH which has changed.
+ * Clear out command entries.
*/
static void
-clearcmdentry(int firstchange)
+clearcmdentry(void)
{
struct tblentry **tblp;
struct tblentry **pp;
for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
pp = tblp;
while ((cmdp = *pp) != NULL) {
- if ((cmdp->cmdtype == CMDNORMAL &&
- cmdp->param.index >= firstchange)
- || (cmdp->cmdtype == CMDBUILTIN &&
- builtinloc >= firstchange)
+ if (cmdp->cmdtype == CMDNORMAL
+ || (cmdp->cmdtype == CMDBUILTIN && builtinloc > 0)
) {
*pp = cmdp->next;
free(cmdp);
char *name;
if (nextopt("r") != '\0') {
- clearcmdentry(0);
+ clearcmdentry();
return 0;
}
* Called with interrupts off.
*/
static void FAST_FUNC
-changepath(const char *new)
+changepath(const char *newval)
{
- const char *old;
- int firstchange;
+ const char *new;
int idx;
- int idx_bltin;
+ int bltin;
- old = pathval();
- firstchange = 9999; /* assume no change */
+ new = newval;
idx = 0;
- idx_bltin = -1;
+ bltin = -1;
for (;;) {
- if (*old != *new) {
- firstchange = idx;
- if ((*old == '\0' && *new == ':')
- || (*old == ':' && *new == '\0')
- ) {
- firstchange++;
- }
- old = new; /* ignore subsequent differences */
+ if (*new == '%' && prefix(new + 1, "builtin")) {
+ bltin = idx;
+ break;
}
- if (*new == '\0')
+ new = strchr(new, ':');
+ if (!new)
break;
- if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
- idx_bltin = idx;
- if (*new == ':')
- idx++;
+ idx++;
new++;
- old++;
}
- if (builtinloc < 0 && idx_bltin >= 0)
- builtinloc = idx_bltin; /* zap builtins */
- if (builtinloc >= 0 && idx_bltin < 0)
- firstchange = 0;
- clearcmdentry(firstchange);
- builtinloc = idx_bltin;
+ builtinloc = bltin;
+ clearcmdentry();
}
enum {
TEOF,
for (;;) {
int len;
- len = padvance(&mpath, nullstr);
+ len = padvance_magic(&mpath, nullstr, 2);
if (!len)
break;
p = stackblock();
while ((len = padvance(&path, basename)) >= 0) {
fullname = stackblock();
- if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
+ if ((!pathopt || *pathopt == 'f')
+ && !stat(fullname, &statb) && S_ISREG(statb.st_mode)
+ ) {
/* This will be freed by the caller. */
return stalloc(len);
}
idx = -1;
loop:
while ((len = padvance(&path, name)) >= 0) {
+ const char *lpathopt = pathopt;
+
fullname = stackblock();
idx++;
- if (pathopt) {
- if (prefix(pathopt, "builtin")) {
+ if (lpathopt) {
+ if (*lpathopt == 'b') {
if (bcmd)
goto builtin_success;
continue;
- }
- if ((act & DO_NOFUNC)
- || !prefix(pathopt, "func")
- ) { /* ignore unimplemented options */
+ } else if (!(act & DO_NOFUNC)) {
+ /* handled below */
+ } else {
+ /* ignore unimplemented options */
continue;
}
}
e = EACCES; /* if we fail, this will be the error */
if (!S_ISREG(statb.st_mode))
continue;
- if (pathopt) { /* this is a %func directory */
+ if (lpathopt) { /* this is a %func directory */
stalloc(len);
/* NB: stalloc will return space pointed by fullname
* (because we don't have any intervening allocations