Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
/* $NetBSD: exec.c,v 1.53 2018/07/25 14:42:50 kre Exp $ */
|
1995-03-21 12:01:59 +03:00
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
/*-
|
1994-05-11 21:09:42 +04:00
|
|
|
* Copyright (c) 1991, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
1993-03-21 12:45:37 +03:00
|
|
|
*
|
|
|
|
* This code is derived from software contributed to Berkeley by
|
|
|
|
* Kenneth Almquist.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2003-08-07 13:05:01 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1993-03-21 12:45:37 +03:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
1997-07-05 01:01:48 +04:00
|
|
|
#include <sys/cdefs.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
#ifndef lint
|
1995-03-21 12:01:59 +03:00
|
|
|
#if 0
|
1995-06-09 05:53:44 +04:00
|
|
|
static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
|
1995-03-21 12:01:59 +03:00
|
|
|
#else
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
__RCSID("$NetBSD: exec.c,v 1.53 2018/07/25 14:42:50 kre Exp $");
|
1995-03-21 12:01:59 +03:00
|
|
|
#endif
|
1993-03-21 12:45:37 +03:00
|
|
|
#endif /* not lint */
|
|
|
|
|
1995-05-12 01:28:33 +04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2002-09-27 22:56:50 +04:00
|
|
|
#include <sys/wait.h>
|
1995-05-12 01:28:33 +04:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
2002-09-27 22:56:50 +04:00
|
|
|
#include <stdio.h>
|
1995-05-12 01:28:33 +04:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
/*
|
|
|
|
* When commands are first encountered, they are entered in a hash table.
|
|
|
|
* This ensures that a full path search will not have to be done for them
|
|
|
|
* on each invocation.
|
|
|
|
*
|
|
|
|
* We should investigate converting to a linear search, even though that
|
|
|
|
* would make the command name "hash" a misnomer.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "shell.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "nodes.h"
|
|
|
|
#include "parser.h"
|
|
|
|
#include "redir.h"
|
|
|
|
#include "eval.h"
|
|
|
|
#include "exec.h"
|
|
|
|
#include "builtins.h"
|
|
|
|
#include "var.h"
|
|
|
|
#include "options.h"
|
|
|
|
#include "input.h"
|
|
|
|
#include "output.h"
|
|
|
|
#include "syntax.h"
|
|
|
|
#include "memalloc.h"
|
|
|
|
#include "error.h"
|
|
|
|
#include "init.h"
|
|
|
|
#include "mystring.h"
|
1995-05-12 01:28:33 +04:00
|
|
|
#include "show.h"
|
1994-05-11 21:09:42 +04:00
|
|
|
#include "jobs.h"
|
1997-02-07 02:24:52 +03:00
|
|
|
#include "alias.h"
|
1993-03-21 12:45:37 +03:00
|
|
|
|
|
|
|
|
|
|
|
#define CMDTABLESIZE 31 /* should be prime */
|
|
|
|
#define ARB 1 /* actual size determined at run time */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct tblentry {
|
|
|
|
struct tblentry *next; /* next entry in hash chain */
|
|
|
|
union param param; /* definition of builtin function */
|
|
|
|
short cmdtype; /* index identifying command */
|
|
|
|
char rehash; /* if set, cd done since entry created */
|
A better LINENO implementation. This version deletes (well, #if 0's out)
the LINENO hack, and uses the LINENO var for both ${LINENO} and $((LINENO)).
(Code to invert the LINENO hack when required, like when de-compiling the
execution tree to provide the "jobs" command strings, is still included,
that can be deleted when the LINENO hack is completely removed - look for
refs to VSLINENO throughout the code. The var funclinno in parser.c can
also be removed, it is used only for the LINENO hack.)
This version produces accurate results: $((LINENO)) was made as accurate
as the LINENO hack made ${LINENO} which is very good. That's why the
LINENO hack is not yet completely removed, so it can be easily re-enabled.
If you can tell the difference when it is in use, or not in use, then
something has broken (or I managed to miss a case somewhere.)
The way that LINENO works is documented in its own (new) section in the
man page, so nothing more about that, or the new options, etc, here.
This version introduces the possibility of having a "reference" function
associated with a variable, which gets called whenever the value of the
variable is required (that's what implements LINENO). There is just
one function pointer however, so any particular variable gets at most
one of the set function (as used for PATH, etc) or the reference function.
The VFUNCREF bit in the var flags indicates which func the variable in
question uses (if any - the func ptr, as before, can be NULL).
I would not call the results of this perfect yet, but it is close.
2017-06-07 08:08:32 +03:00
|
|
|
char fn_ln1; /* for functions, LINENO from 1 */
|
|
|
|
int lineno; /* for functions abs LINENO of definition */
|
1993-03-21 12:45:37 +03:00
|
|
|
char cmdname[ARB]; /* name of command */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
STATIC struct tblentry *cmdtable[CMDTABLESIZE];
|
|
|
|
STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
|
1996-10-16 18:35:42 +04:00
|
|
|
int exerrno = 0; /* Last exec error */
|
1993-03-21 12:45:37 +03:00
|
|
|
|
|
|
|
|
2002-11-25 01:35:38 +03:00
|
|
|
STATIC void tryexec(char *, char **, char **, int);
|
|
|
|
STATIC void printentry(struct tblentry *, int);
|
2016-05-03 20:21:02 +03:00
|
|
|
STATIC void addcmdentry(char *, struct cmdentry *);
|
2002-11-25 01:35:38 +03:00
|
|
|
STATIC void clearcmdentry(int);
|
|
|
|
STATIC struct tblentry *cmdlookup(const char *, int);
|
|
|
|
STATIC void delete_cmd_entry(void);
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2008-10-16 19:31:05 +04:00
|
|
|
#ifndef BSD
|
|
|
|
STATIC void execinterp(char **, char **);
|
|
|
|
#endif
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2008-02-15 20:26:06 +03:00
|
|
|
extern const char *const parsekwd[];
|
1993-03-21 12:45:37 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Exec a program. Never returns. If you change this routine, you may
|
|
|
|
* have to change the find_command routine as well.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2002-11-25 01:35:38 +03:00
|
|
|
shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
|
1994-12-05 22:07:32 +03:00
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
char *cmdname;
|
|
|
|
int e;
|
|
|
|
|
|
|
|
if (strchr(argv[0], '/') != NULL) {
|
2002-09-27 22:56:50 +04:00
|
|
|
tryexec(argv[0], argv, envp, vforked);
|
1993-03-21 12:45:37 +03:00
|
|
|
e = errno;
|
|
|
|
} else {
|
|
|
|
e = ENOENT;
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
while ((cmdname = padvance(&path, argv[0], 1)) != NULL) {
|
1999-07-09 07:05:49 +04:00
|
|
|
if (--idx < 0 && pathopt == NULL) {
|
2002-09-27 22:56:50 +04:00
|
|
|
tryexec(cmdname, argv, envp, vforked);
|
1993-03-21 12:45:37 +03:00
|
|
|
if (errno != ENOENT && errno != ENOTDIR)
|
|
|
|
e = errno;
|
|
|
|
}
|
|
|
|
stunalloc(cmdname);
|
|
|
|
}
|
|
|
|
}
|
1996-10-16 18:35:42 +04:00
|
|
|
|
|
|
|
/* Map to POSIX errors */
|
|
|
|
switch (e) {
|
2017-07-05 22:58:10 +03:00
|
|
|
case EACCES: /* particularly this (unless no search perm) */
|
|
|
|
/*
|
|
|
|
* should perhaps check if this EACCES is an exec()
|
|
|
|
* EACESS or a namei() EACESS - the latter should be 127
|
|
|
|
* - but not today
|
|
|
|
*/
|
|
|
|
case EINVAL: /* also explicitly these */
|
|
|
|
case ENOEXEC:
|
|
|
|
default: /* and anything else */
|
1996-10-16 18:35:42 +04:00
|
|
|
exerrno = 126;
|
|
|
|
break;
|
2017-07-05 22:58:10 +03:00
|
|
|
|
|
|
|
case ENOENT: /* these are the "pathname lookup failed" errors */
|
|
|
|
case ELOOP:
|
|
|
|
case ENOTDIR:
|
|
|
|
case ENAMETOOLONG:
|
1996-10-16 18:35:42 +04:00
|
|
|
exerrno = 127;
|
|
|
|
break;
|
|
|
|
}
|
2017-07-05 22:58:10 +03:00
|
|
|
CTRACE(DBG_ERRS|DBG_CMDS|DBG_EVAL,
|
|
|
|
("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
|
|
|
|
argv[0], e, vforked, suppressint));
|
1996-10-16 18:35:42 +04:00
|
|
|
exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
|
1998-07-28 15:41:40 +04:00
|
|
|
/* NOTREACHED */
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC void
|
2002-11-25 01:35:38 +03:00
|
|
|
tryexec(char *cmd, char **argv, char **envp, int vforked)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
int e;
|
1995-05-12 01:28:33 +04:00
|
|
|
#ifndef BSD
|
1993-03-21 12:45:37 +03:00
|
|
|
char *p;
|
1995-05-12 01:28:33 +04:00
|
|
|
#endif
|
1993-03-21 12:45:37 +03:00
|
|
|
|
|
|
|
#ifdef SYSV
|
|
|
|
do {
|
|
|
|
execve(cmd, argv, envp);
|
|
|
|
} while (errno == EINTR);
|
|
|
|
#else
|
|
|
|
execve(cmd, argv, envp);
|
|
|
|
#endif
|
|
|
|
e = errno;
|
|
|
|
if (e == ENOEXEC) {
|
2002-09-27 22:56:50 +04:00
|
|
|
if (vforked) {
|
|
|
|
/* We are currently vfork(2)ed, so raise an
|
|
|
|
* exception, and evalcommand will try again
|
|
|
|
* with a normal fork(2).
|
|
|
|
*/
|
|
|
|
exraise(EXSHELLPROC);
|
|
|
|
}
|
2007-06-24 22:36:23 +04:00
|
|
|
#ifdef DEBUG
|
2017-07-05 22:58:10 +03:00
|
|
|
VTRACE(DBG_CMDS, ("execve(cmd=%s) returned ENOEXEC\n", cmd));
|
2007-06-24 22:36:23 +04:00
|
|
|
#endif
|
1993-03-21 12:45:37 +03:00
|
|
|
initshellproc();
|
|
|
|
setinputfile(cmd, 0);
|
|
|
|
commandname = arg0 = savestr(argv[0]);
|
|
|
|
#ifndef BSD
|
|
|
|
pgetc(); pungetc(); /* fill up input buffer */
|
|
|
|
p = parsenextc;
|
|
|
|
if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
|
|
|
|
argv[0] = cmd;
|
|
|
|
execinterp(argv, envp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
setparam(argv + 1);
|
|
|
|
exraise(EXSHELLPROC);
|
|
|
|
}
|
|
|
|
errno = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef BSD
|
|
|
|
/*
|
|
|
|
* Execute an interpreter introduced by "#!", for systems where this
|
|
|
|
* feature has not been built into the kernel. If the interpreter is
|
|
|
|
* the shell, return (effectively ignoring the "#!"). If the execution
|
|
|
|
* of the interpreter fails, exit.
|
|
|
|
*
|
|
|
|
* This code peeks inside the input buffer in order to avoid actually
|
|
|
|
* reading any input. It would benefit from a rewrite.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define NEWARGS 5
|
|
|
|
|
|
|
|
STATIC void
|
2002-11-25 01:35:38 +03:00
|
|
|
execinterp(char **argv, char **envp)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
int n;
|
|
|
|
char *inp;
|
|
|
|
char *outp;
|
|
|
|
char c;
|
|
|
|
char *p;
|
|
|
|
char **ap;
|
|
|
|
char *newargs[NEWARGS];
|
|
|
|
int i;
|
|
|
|
char **ap2;
|
|
|
|
char **new;
|
|
|
|
|
|
|
|
n = parsenleft - 2;
|
|
|
|
inp = parsenextc + 2;
|
|
|
|
ap = newargs;
|
|
|
|
for (;;) {
|
|
|
|
while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
|
|
|
|
inp++;
|
|
|
|
if (n < 0)
|
|
|
|
goto bad;
|
|
|
|
if ((c = *inp++) == '\n')
|
|
|
|
break;
|
|
|
|
if (ap == &newargs[NEWARGS])
|
|
|
|
bad: error("Bad #! line");
|
|
|
|
STARTSTACKSTR(outp);
|
|
|
|
do {
|
|
|
|
STPUTC(c, outp);
|
|
|
|
} while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
|
|
|
|
STPUTC('\0', outp);
|
|
|
|
n++, inp--;
|
|
|
|
*ap++ = grabstackstr(outp);
|
|
|
|
}
|
|
|
|
if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
|
|
|
|
p = newargs[0];
|
|
|
|
for (;;) {
|
|
|
|
if (equal(p, "sh") || equal(p, "ash")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (*p != '/') {
|
|
|
|
if (*p == '\0')
|
|
|
|
goto break2;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
break2:;
|
|
|
|
}
|
|
|
|
i = (char *)ap - (char *)newargs; /* size in bytes */
|
|
|
|
if (i == 0)
|
|
|
|
error("Bad #! line");
|
|
|
|
for (ap2 = argv ; *ap2++ != NULL ; );
|
|
|
|
new = ckmalloc(i + ((char *)ap2 - (char *)argv));
|
|
|
|
ap = newargs, ap2 = new;
|
|
|
|
while ((i -= sizeof (char **)) >= 0)
|
|
|
|
*ap2++ = *ap++;
|
|
|
|
ap = argv;
|
|
|
|
while (*ap2++ = *ap++);
|
|
|
|
shellexec(new, envp, pathval(), 0);
|
1998-07-28 15:41:40 +04:00
|
|
|
/* NOTREACHED */
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do a path search. 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
|
|
|
|
* the possible path expansions in sequence. If an option (indicated by
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
1999-07-09 07:05:49 +04:00
|
|
|
const char *pathopt;
|
1993-03-21 12:45:37 +03:00
|
|
|
|
|
|
|
char *
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
padvance(const char **path, const char *name, int magic_percent)
|
2002-11-25 01:35:38 +03:00
|
|
|
{
|
1999-07-09 07:05:49 +04:00
|
|
|
const char *p;
|
|
|
|
char *q;
|
|
|
|
const char *start;
|
1993-03-21 12:45:37 +03:00
|
|
|
int len;
|
|
|
|
|
|
|
|
if (*path == NULL)
|
|
|
|
return NULL;
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
if (magic_percent)
|
|
|
|
magic_percent = '%';
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
start = *path;
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
for (p = start ; *p && *p != ':' && *p != magic_percent ; p++)
|
|
|
|
;
|
1993-03-21 12:45:37 +03:00
|
|
|
len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
|
|
|
|
while (stackblocksize() < len)
|
|
|
|
growstackblock();
|
|
|
|
q = stackblock();
|
|
|
|
if (p != start) {
|
1994-09-23 15:28:39 +04:00
|
|
|
memcpy(q, start, p - start);
|
1993-03-21 12:45:37 +03:00
|
|
|
q += p - start;
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
if (q[-1] != '/')
|
|
|
|
*q++ = '/';
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
strcpy(q, name);
|
|
|
|
pathopt = NULL;
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
if (*p == magic_percent) {
|
1993-03-21 12:45:37 +03:00
|
|
|
pathopt = ++p;
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
while (*p && *p != ':')
|
|
|
|
p++;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
if (*p == ':')
|
|
|
|
*path = p + 1;
|
|
|
|
else
|
|
|
|
*path = NULL;
|
Many internal memory management type fixes.
PR bin/52302 (core dump with interactive shell, here doc and error
on same line) is fixed. (An old bug.)
echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )"
consistently prints 1002 lines (x, 1000 empty ones, then y) as it should
(And you don't want to know what it did before, or why.) (Another old one.)
(Recently added) Problems with ~ expansion fixed (mem management related).
Proper fix for the cwrappers configure problem (which includes the quick
fix that was done earlier, but extends upon that to be correct). (This was
another newly added problem.)
And the really devious (and rare) old bug - if STACKSTRNUL() needs to
allocate a new buffer in which to store the \0, calculate the size of
the string space remaining correctly, unlike when SPUTC() grows the
buffer, there is no actual data being stored in the STACKSTRNUL()
case - the string space remaining was calculated as one byte too few.
That would be harmless, unless the next buffer also filled, in which
case it was assumed that it was really full, not one byte less, meaning
one junk char (a nul, or anything) was being copied into the next (even
bigger buffer) corrupting the data.
Consistent use of stalloc() to allocate a new block of (stack) memory,
and grabstackstr() to claim a block of (stack) memory that had already
been occupied but not claimed as in use. Since grabstackstr is implemented
as just a call to stalloc() this is a no-op change in practice, but makes
it much easier to comprehend what is really happening. Previous code
sometimes used stalloc() when the use case was really for grabstackstr().
Change grabstackstr() to actually use the arg passed to it, instead of
(not much better than) guessing how much space to claim,
More care when using unstalloc()/ungrabstackstr() to return space, and in
particular when the stack must be returned to its previous state, rather than
just returning no-longer needed space, neither of those work. They also don't
work properly if there have been (really, even might have been) any stack mem
allocations since the last stalloc()/grabstackstr(). (If we know there
cannot have been then the alloc/release sequence is kind of pointless.)
To work correctly in general we must use setstackmark()/popstackmark() so
do that when needed. Have those also save/restore the top of stack string
space remaining.
[Aside: for those reading this, the "stack" mentioned is not
in any way related to the thing used for maintaining the C
function call state, ie: the "stack segment" of the program,
but the shell's internal memory management strategy.]
More comments to better explain what is happening in some cases.
Also cleaned up some hopelessly broken DEBUG mode data that were
recently added (no effect on anyone but the poor semi-human attempting
to make sense of it...).
User visible changes:
Proper counting of line numbers when a here document is delimited
by a multi-line end-delimiter, as in
cat << 'REALLY
END'
here doc line 1
here doc line 2
REALLY
END
(which is an obscure case, but nothing says should not work.) The \n
in the end-delimiter of the here doc (the last one) was not incrementing
the line number, which from that point on in the script would be 1 too
low (or more, for end-delimiters with more than one \n in them.)
With tilde expansion:
unset HOME; echo ~
changed to return getpwuid(getuid())->pw_home instead of failing (returning ~)
POSIX says this is unspecified, which makes it difficult for a script to
compensate for being run without HOME set (as in env -i sh script), so
while not able to be used portably, this seems like a useful extension
(and is implemented the same way by some other shells).
Further, with
HOME=; printf %s ~
we now write nothing (which is required by POSIX - which requires ~ to
expand to the value of $HOME if it is set) previously if $HOME (in this
case) or a user's directory in the passwd file (for ~user) were a null
STRING, We failed the ~ expansion and left behind '~' or '~user'.
2017-06-17 10:22:12 +03:00
|
|
|
return grabstackstr(q + strlen(name) + 1);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** Command hashing code ***/
|
|
|
|
|
|
|
|
|
1994-12-05 22:07:32 +03:00
|
|
|
int
|
2002-11-25 01:35:38 +03:00
|
|
|
hashcmd(int argc, char **argv)
|
1994-12-05 22:07:32 +03:00
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry **pp;
|
|
|
|
struct tblentry *cmdp;
|
|
|
|
int c;
|
|
|
|
struct cmdentry entry;
|
|
|
|
char *name;
|
2017-05-15 22:55:20 +03:00
|
|
|
int allopt=0, bopt=0, fopt=0, ropt=0, sopt=0, uopt=0, verbose=0;
|
|
|
|
|
|
|
|
while ((c = nextopt("bcfrsuv")) != '\0')
|
|
|
|
switch (c) {
|
|
|
|
case 'b': bopt = 1; break;
|
|
|
|
case 'c': uopt = 1; break; /* c == u */
|
|
|
|
case 'f': fopt = 1; break;
|
|
|
|
case 'r': ropt = 1; break;
|
|
|
|
case 's': sopt = 1; break;
|
|
|
|
case 'u': uopt = 1; break;
|
|
|
|
case 'v': verbose = 1; break;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
2017-05-15 22:55:20 +03:00
|
|
|
|
|
|
|
if (ropt)
|
|
|
|
clearcmdentry(0);
|
|
|
|
|
|
|
|
if (bopt == 0 && fopt == 0 && sopt == 0 && uopt == 0)
|
|
|
|
allopt = bopt = fopt = sopt = uopt = 1;
|
|
|
|
|
1994-05-11 21:09:42 +04:00
|
|
|
if (*argptr == NULL) {
|
|
|
|
for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
|
|
|
|
for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
|
2017-05-15 22:55:20 +03:00
|
|
|
switch (cmdp->cmdtype) {
|
|
|
|
case CMDNORMAL:
|
|
|
|
if (!uopt)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case CMDBUILTIN:
|
|
|
|
if (!bopt)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case CMDSPLBLTIN:
|
|
|
|
if (!sopt)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case CMDFUNCTION:
|
|
|
|
if (!fopt)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
default: /* never happens */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!allopt || verbose ||
|
|
|
|
cmdp->cmdtype == CMDNORMAL)
|
2003-02-04 11:51:30 +03:00
|
|
|
printentry(cmdp, verbose);
|
1994-05-11 21:09:42 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-05-15 22:55:20 +03:00
|
|
|
|
|
|
|
while ((name = *argptr++) != NULL) {
|
|
|
|
if ((cmdp = cmdlookup(name, 0)) != NULL) {
|
|
|
|
switch (cmdp->cmdtype) {
|
|
|
|
case CMDNORMAL:
|
|
|
|
if (!uopt)
|
|
|
|
continue;
|
|
|
|
delete_cmd_entry();
|
|
|
|
break;
|
|
|
|
case CMDBUILTIN:
|
|
|
|
if (!bopt)
|
|
|
|
continue;
|
|
|
|
if (builtinloc >= 0)
|
|
|
|
delete_cmd_entry();
|
|
|
|
break;
|
|
|
|
case CMDSPLBLTIN:
|
|
|
|
if (!sopt)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case CMDFUNCTION:
|
|
|
|
if (!fopt)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1997-07-21 01:27:35 +04:00
|
|
|
find_command(name, &entry, DO_ERR, pathval());
|
1993-03-21 12:45:37 +03:00
|
|
|
if (verbose) {
|
|
|
|
if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
|
|
|
|
cmdp = cmdlookup(name, 0);
|
2006-03-18 08:17:36 +03:00
|
|
|
if (cmdp != NULL)
|
|
|
|
printentry(cmdp, verbose);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
flushall();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC void
|
2002-11-25 01:35:38 +03:00
|
|
|
printentry(struct tblentry *cmdp, int verbose)
|
|
|
|
{
|
1999-07-09 07:05:49 +04:00
|
|
|
int idx;
|
|
|
|
const char *path;
|
1993-03-21 12:45:37 +03:00
|
|
|
char *name;
|
|
|
|
|
2002-11-25 01:35:38 +03:00
|
|
|
switch (cmdp->cmdtype) {
|
|
|
|
case CMDNORMAL:
|
1999-07-09 07:05:49 +04:00
|
|
|
idx = cmdp->param.index;
|
1993-03-21 12:45:37 +03:00
|
|
|
path = pathval();
|
|
|
|
do {
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
name = padvance(&path, cmdp->cmdname, 1);
|
1993-03-21 12:45:37 +03:00
|
|
|
stunalloc(name);
|
1999-07-09 07:05:49 +04:00
|
|
|
} while (--idx >= 0);
|
2017-05-15 22:55:20 +03:00
|
|
|
if (verbose)
|
|
|
|
out1fmt("Command from PATH[%d]: ",
|
|
|
|
cmdp->param.index);
|
1993-03-21 12:45:37 +03:00
|
|
|
out1str(name);
|
2002-11-25 01:35:38 +03:00
|
|
|
break;
|
|
|
|
case CMDSPLBLTIN:
|
2017-05-15 22:55:20 +03:00
|
|
|
if (verbose)
|
|
|
|
out1str("special ");
|
|
|
|
/* FALLTHROUGH */
|
2002-11-25 01:35:38 +03:00
|
|
|
case CMDBUILTIN:
|
2017-05-15 22:55:20 +03:00
|
|
|
if (verbose)
|
|
|
|
out1str("builtin ");
|
|
|
|
out1fmt("%s", cmdp->cmdname);
|
2002-11-25 01:35:38 +03:00
|
|
|
break;
|
|
|
|
case CMDFUNCTION:
|
2017-05-15 22:55:20 +03:00
|
|
|
if (verbose)
|
|
|
|
out1str("function ");
|
|
|
|
out1fmt("%s", cmdp->cmdname);
|
1994-05-11 21:09:42 +04:00
|
|
|
if (verbose) {
|
2002-11-25 01:35:38 +03:00
|
|
|
struct procstat ps;
|
2018-06-22 14:04:55 +03:00
|
|
|
|
1994-05-11 21:09:42 +04:00
|
|
|
INTOFF;
|
2018-06-22 14:04:55 +03:00
|
|
|
commandtext(&ps, getfuncnode(cmdp->param.func));
|
1994-05-11 21:09:42 +04:00
|
|
|
INTON;
|
2003-02-04 11:51:30 +03:00
|
|
|
out1str("() { ");
|
|
|
|
out1str(ps.cmd);
|
|
|
|
out1str("; }");
|
1994-05-11 21:09:42 +04:00
|
|
|
}
|
2002-11-25 01:35:38 +03:00
|
|
|
break;
|
|
|
|
default:
|
2017-05-15 22:55:20 +03:00
|
|
|
error("internal error: %s cmdtype %d",
|
|
|
|
cmdp->cmdname, cmdp->cmdtype);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
if (cmdp->rehash)
|
|
|
|
out1c('*');
|
|
|
|
out1c('\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Resolve a command name. If you change this routine, you may have to
|
|
|
|
* change the shellexec routine as well.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2002-11-25 01:35:38 +03:00
|
|
|
find_command(char *name, struct cmdentry *entry, int act, const char *path)
|
1994-12-05 22:07:32 +03:00
|
|
|
{
|
2003-01-22 23:36:03 +03:00
|
|
|
struct tblentry *cmdp, loc_cmd;
|
1999-07-09 07:05:49 +04:00
|
|
|
int idx;
|
1993-03-21 12:45:37 +03:00
|
|
|
int prev;
|
|
|
|
char *fullname;
|
|
|
|
struct stat statb;
|
|
|
|
int e;
|
2002-11-25 01:35:38 +03:00
|
|
|
int (*bltin)(int,char **);
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
/* If name contains a slash, don't use PATH or hash table */
|
1993-03-21 12:45:37 +03:00
|
|
|
if (strchr(name, '/') != NULL) {
|
1997-07-21 01:27:35 +04:00
|
|
|
if (act & DO_ABS) {
|
|
|
|
while (stat(name, &statb) < 0) {
|
2002-11-25 01:35:38 +03:00
|
|
|
#ifdef SYSV
|
1997-07-21 01:27:35 +04:00
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
2002-11-25 01:35:38 +03:00
|
|
|
#endif
|
1997-07-21 01:27:35 +04:00
|
|
|
if (errno != ENOENT && errno != ENOTDIR)
|
|
|
|
e = errno;
|
|
|
|
entry->cmdtype = CMDUNKNOWN;
|
|
|
|
entry->u.index = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
entry->cmdtype = CMDNORMAL;
|
|
|
|
entry->u.index = -1;
|
|
|
|
return;
|
|
|
|
}
|
1993-03-21 12:45:37 +03:00
|
|
|
entry->cmdtype = CMDNORMAL;
|
|
|
|
entry->u.index = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
if (path != pathval())
|
|
|
|
act |= DO_ALTPATH;
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
|
|
|
|
act |= DO_ALTBLTIN;
|
|
|
|
|
|
|
|
/* If name is in the table, check answer will be ok */
|
|
|
|
if ((cmdp = cmdlookup(name, 0)) != NULL) {
|
|
|
|
do {
|
|
|
|
switch (cmdp->cmdtype) {
|
|
|
|
case CMDNORMAL:
|
|
|
|
if (act & DO_ALTPATH) {
|
|
|
|
cmdp = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CMDFUNCTION:
|
|
|
|
if (act & DO_NOFUNC) {
|
|
|
|
cmdp = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CMDBUILTIN:
|
|
|
|
if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
|
|
|
|
cmdp = NULL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* if not invalidated by cd, we're done */
|
|
|
|
if (cmdp->rehash == 0)
|
|
|
|
goto success;
|
|
|
|
} while (0);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
/* If %builtin not in path, check for builtin next */
|
|
|
|
if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
|
|
|
|
(bltin = find_builtin(name)) != 0)
|
|
|
|
goto builtin_success;
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
/* We have to search path. */
|
|
|
|
prev = -1; /* where to start */
|
|
|
|
if (cmdp) { /* doing a rehash */
|
|
|
|
if (cmdp->cmdtype == CMDBUILTIN)
|
|
|
|
prev = builtinloc;
|
|
|
|
else
|
|
|
|
prev = cmdp->param.index;
|
|
|
|
}
|
|
|
|
|
|
|
|
e = ENOENT;
|
1999-07-09 07:05:49 +04:00
|
|
|
idx = -1;
|
1993-03-21 12:45:37 +03:00
|
|
|
loop:
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
while ((fullname = padvance(&path, name, 1)) != NULL) {
|
1993-03-21 12:45:37 +03:00
|
|
|
stunalloc(fullname);
|
1999-07-09 07:05:49 +04:00
|
|
|
idx++;
|
1993-03-21 12:45:37 +03:00
|
|
|
if (pathopt) {
|
|
|
|
if (prefix("builtin", pathopt)) {
|
2002-11-25 01:35:38 +03:00
|
|
|
if ((bltin = find_builtin(name)) == 0)
|
1993-03-21 12:45:37 +03:00
|
|
|
goto loop;
|
2003-01-22 23:36:03 +03:00
|
|
|
goto builtin_success;
|
1993-03-21 12:45:37 +03:00
|
|
|
} else if (prefix("func", pathopt)) {
|
|
|
|
/* handled below */
|
|
|
|
} else {
|
2003-01-22 23:36:03 +03:00
|
|
|
/* ignore unimplemented options */
|
|
|
|
goto loop;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* if rehash, don't redo absolute path names */
|
1999-07-09 07:05:49 +04:00
|
|
|
if (fullname[0] == '/' && idx <= prev) {
|
|
|
|
if (idx < prev)
|
1993-03-21 12:45:37 +03:00
|
|
|
goto loop;
|
2017-07-05 22:58:10 +03:00
|
|
|
VTRACE(DBG_CMDS, ("searchexec \"%s\": no change\n",
|
|
|
|
name));
|
1993-03-21 12:45:37 +03:00
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
while (stat(fullname, &statb) < 0) {
|
|
|
|
#ifdef SYSV
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
if (errno != ENOENT && errno != ENOTDIR)
|
|
|
|
e = errno;
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
e = EACCES; /* if we fail, this will be the error */
|
1995-01-30 22:30:13 +03:00
|
|
|
if (!S_ISREG(statb.st_mode))
|
1993-03-21 12:45:37 +03:00
|
|
|
goto loop;
|
|
|
|
if (pathopt) { /* this is a %func directory */
|
Many internal memory management type fixes.
PR bin/52302 (core dump with interactive shell, here doc and error
on same line) is fixed. (An old bug.)
echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )"
consistently prints 1002 lines (x, 1000 empty ones, then y) as it should
(And you don't want to know what it did before, or why.) (Another old one.)
(Recently added) Problems with ~ expansion fixed (mem management related).
Proper fix for the cwrappers configure problem (which includes the quick
fix that was done earlier, but extends upon that to be correct). (This was
another newly added problem.)
And the really devious (and rare) old bug - if STACKSTRNUL() needs to
allocate a new buffer in which to store the \0, calculate the size of
the string space remaining correctly, unlike when SPUTC() grows the
buffer, there is no actual data being stored in the STACKSTRNUL()
case - the string space remaining was calculated as one byte too few.
That would be harmless, unless the next buffer also filled, in which
case it was assumed that it was really full, not one byte less, meaning
one junk char (a nul, or anything) was being copied into the next (even
bigger buffer) corrupting the data.
Consistent use of stalloc() to allocate a new block of (stack) memory,
and grabstackstr() to claim a block of (stack) memory that had already
been occupied but not claimed as in use. Since grabstackstr is implemented
as just a call to stalloc() this is a no-op change in practice, but makes
it much easier to comprehend what is really happening. Previous code
sometimes used stalloc() when the use case was really for grabstackstr().
Change grabstackstr() to actually use the arg passed to it, instead of
(not much better than) guessing how much space to claim,
More care when using unstalloc()/ungrabstackstr() to return space, and in
particular when the stack must be returned to its previous state, rather than
just returning no-longer needed space, neither of those work. They also don't
work properly if there have been (really, even might have been) any stack mem
allocations since the last stalloc()/grabstackstr(). (If we know there
cannot have been then the alloc/release sequence is kind of pointless.)
To work correctly in general we must use setstackmark()/popstackmark() so
do that when needed. Have those also save/restore the top of stack string
space remaining.
[Aside: for those reading this, the "stack" mentioned is not
in any way related to the thing used for maintaining the C
function call state, ie: the "stack segment" of the program,
but the shell's internal memory management strategy.]
More comments to better explain what is happening in some cases.
Also cleaned up some hopelessly broken DEBUG mode data that were
recently added (no effect on anyone but the poor semi-human attempting
to make sense of it...).
User visible changes:
Proper counting of line numbers when a here document is delimited
by a multi-line end-delimiter, as in
cat << 'REALLY
END'
here doc line 1
here doc line 2
REALLY
END
(which is an obscure case, but nothing says should not work.) The \n
in the end-delimiter of the here doc (the last one) was not incrementing
the line number, which from that point on in the script would be 1 too
low (or more, for end-delimiters with more than one \n in them.)
With tilde expansion:
unset HOME; echo ~
changed to return getpwuid(getuid())->pw_home instead of failing (returning ~)
POSIX says this is unspecified, which makes it difficult for a script to
compensate for being run without HOME set (as in env -i sh script), so
while not able to be used portably, this seems like a useful extension
(and is implemented the same way by some other shells).
Further, with
HOME=; printf %s ~
we now write nothing (which is required by POSIX - which requires ~ to
expand to the value of $HOME if it is set) previously if $HOME (in this
case) or a user's directory in the passwd file (for ~user) were a null
STRING, We failed the ~ expansion and left behind '~' or '~user'.
2017-06-17 10:22:12 +03:00
|
|
|
char *endname;
|
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
if (act & DO_NOFUNC)
|
|
|
|
goto loop;
|
Many internal memory management type fixes.
PR bin/52302 (core dump with interactive shell, here doc and error
on same line) is fixed. (An old bug.)
echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )"
consistently prints 1002 lines (x, 1000 empty ones, then y) as it should
(And you don't want to know what it did before, or why.) (Another old one.)
(Recently added) Problems with ~ expansion fixed (mem management related).
Proper fix for the cwrappers configure problem (which includes the quick
fix that was done earlier, but extends upon that to be correct). (This was
another newly added problem.)
And the really devious (and rare) old bug - if STACKSTRNUL() needs to
allocate a new buffer in which to store the \0, calculate the size of
the string space remaining correctly, unlike when SPUTC() grows the
buffer, there is no actual data being stored in the STACKSTRNUL()
case - the string space remaining was calculated as one byte too few.
That would be harmless, unless the next buffer also filled, in which
case it was assumed that it was really full, not one byte less, meaning
one junk char (a nul, or anything) was being copied into the next (even
bigger buffer) corrupting the data.
Consistent use of stalloc() to allocate a new block of (stack) memory,
and grabstackstr() to claim a block of (stack) memory that had already
been occupied but not claimed as in use. Since grabstackstr is implemented
as just a call to stalloc() this is a no-op change in practice, but makes
it much easier to comprehend what is really happening. Previous code
sometimes used stalloc() when the use case was really for grabstackstr().
Change grabstackstr() to actually use the arg passed to it, instead of
(not much better than) guessing how much space to claim,
More care when using unstalloc()/ungrabstackstr() to return space, and in
particular when the stack must be returned to its previous state, rather than
just returning no-longer needed space, neither of those work. They also don't
work properly if there have been (really, even might have been) any stack mem
allocations since the last stalloc()/grabstackstr(). (If we know there
cannot have been then the alloc/release sequence is kind of pointless.)
To work correctly in general we must use setstackmark()/popstackmark() so
do that when needed. Have those also save/restore the top of stack string
space remaining.
[Aside: for those reading this, the "stack" mentioned is not
in any way related to the thing used for maintaining the C
function call state, ie: the "stack segment" of the program,
but the shell's internal memory management strategy.]
More comments to better explain what is happening in some cases.
Also cleaned up some hopelessly broken DEBUG mode data that were
recently added (no effect on anyone but the poor semi-human attempting
to make sense of it...).
User visible changes:
Proper counting of line numbers when a here document is delimited
by a multi-line end-delimiter, as in
cat << 'REALLY
END'
here doc line 1
here doc line 2
REALLY
END
(which is an obscure case, but nothing says should not work.) The \n
in the end-delimiter of the here doc (the last one) was not incrementing
the line number, which from that point on in the script would be 1 too
low (or more, for end-delimiters with more than one \n in them.)
With tilde expansion:
unset HOME; echo ~
changed to return getpwuid(getuid())->pw_home instead of failing (returning ~)
POSIX says this is unspecified, which makes it difficult for a script to
compensate for being run without HOME set (as in env -i sh script), so
while not able to be used portably, this seems like a useful extension
(and is implemented the same way by some other shells).
Further, with
HOME=; printf %s ~
we now write nothing (which is required by POSIX - which requires ~ to
expand to the value of $HOME if it is set) previously if $HOME (in this
case) or a user's directory in the passwd file (for ~user) were a null
STRING, We failed the ~ expansion and left behind '~' or '~user'.
2017-06-17 10:22:12 +03:00
|
|
|
endname = fullname + strlen(fullname) + 1;
|
|
|
|
grabstackstr(endname);
|
1993-03-21 12:45:37 +03:00
|
|
|
readcmdfile(fullname);
|
2003-01-22 23:36:03 +03:00
|
|
|
if ((cmdp = cmdlookup(name, 0)) == NULL ||
|
|
|
|
cmdp->cmdtype != CMDFUNCTION)
|
1993-03-21 12:45:37 +03:00
|
|
|
error("%s not defined in %s", name, fullname);
|
Many internal memory management type fixes.
PR bin/52302 (core dump with interactive shell, here doc and error
on same line) is fixed. (An old bug.)
echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )"
consistently prints 1002 lines (x, 1000 empty ones, then y) as it should
(And you don't want to know what it did before, or why.) (Another old one.)
(Recently added) Problems with ~ expansion fixed (mem management related).
Proper fix for the cwrappers configure problem (which includes the quick
fix that was done earlier, but extends upon that to be correct). (This was
another newly added problem.)
And the really devious (and rare) old bug - if STACKSTRNUL() needs to
allocate a new buffer in which to store the \0, calculate the size of
the string space remaining correctly, unlike when SPUTC() grows the
buffer, there is no actual data being stored in the STACKSTRNUL()
case - the string space remaining was calculated as one byte too few.
That would be harmless, unless the next buffer also filled, in which
case it was assumed that it was really full, not one byte less, meaning
one junk char (a nul, or anything) was being copied into the next (even
bigger buffer) corrupting the data.
Consistent use of stalloc() to allocate a new block of (stack) memory,
and grabstackstr() to claim a block of (stack) memory that had already
been occupied but not claimed as in use. Since grabstackstr is implemented
as just a call to stalloc() this is a no-op change in practice, but makes
it much easier to comprehend what is really happening. Previous code
sometimes used stalloc() when the use case was really for grabstackstr().
Change grabstackstr() to actually use the arg passed to it, instead of
(not much better than) guessing how much space to claim,
More care when using unstalloc()/ungrabstackstr() to return space, and in
particular when the stack must be returned to its previous state, rather than
just returning no-longer needed space, neither of those work. They also don't
work properly if there have been (really, even might have been) any stack mem
allocations since the last stalloc()/grabstackstr(). (If we know there
cannot have been then the alloc/release sequence is kind of pointless.)
To work correctly in general we must use setstackmark()/popstackmark() so
do that when needed. Have those also save/restore the top of stack string
space remaining.
[Aside: for those reading this, the "stack" mentioned is not
in any way related to the thing used for maintaining the C
function call state, ie: the "stack segment" of the program,
but the shell's internal memory management strategy.]
More comments to better explain what is happening in some cases.
Also cleaned up some hopelessly broken DEBUG mode data that were
recently added (no effect on anyone but the poor semi-human attempting
to make sense of it...).
User visible changes:
Proper counting of line numbers when a here document is delimited
by a multi-line end-delimiter, as in
cat << 'REALLY
END'
here doc line 1
here doc line 2
REALLY
END
(which is an obscure case, but nothing says should not work.) The \n
in the end-delimiter of the here doc (the last one) was not incrementing
the line number, which from that point on in the script would be 1 too
low (or more, for end-delimiters with more than one \n in them.)
With tilde expansion:
unset HOME; echo ~
changed to return getpwuid(getuid())->pw_home instead of failing (returning ~)
POSIX says this is unspecified, which makes it difficult for a script to
compensate for being run without HOME set (as in env -i sh script), so
while not able to be used portably, this seems like a useful extension
(and is implemented the same way by some other shells).
Further, with
HOME=; printf %s ~
we now write nothing (which is required by POSIX - which requires ~ to
expand to the value of $HOME if it is set) previously if $HOME (in this
case) or a user's directory in the passwd file (for ~user) were a null
STRING, We failed the ~ expansion and left behind '~' or '~user'.
2017-06-17 10:22:12 +03:00
|
|
|
ungrabstackstr(fullname, endname);
|
1993-03-21 12:45:37 +03:00
|
|
|
goto success;
|
|
|
|
}
|
1994-05-11 21:09:42 +04:00
|
|
|
#ifdef notdef
|
2003-01-22 23:36:03 +03:00
|
|
|
/* XXX this code stops root executing stuff, and is buggy
|
|
|
|
if you need a group from the group list. */
|
1994-05-11 21:09:42 +04:00
|
|
|
if (statb.st_uid == geteuid()) {
|
1993-03-21 12:45:37 +03:00
|
|
|
if ((statb.st_mode & 0100) == 0)
|
|
|
|
goto loop;
|
|
|
|
} else if (statb.st_gid == getegid()) {
|
|
|
|
if ((statb.st_mode & 010) == 0)
|
|
|
|
goto loop;
|
|
|
|
} else {
|
1994-05-11 21:09:42 +04:00
|
|
|
if ((statb.st_mode & 01) == 0)
|
1993-03-21 12:45:37 +03:00
|
|
|
goto loop;
|
|
|
|
}
|
1993-04-10 18:55:52 +04:00
|
|
|
#endif
|
2017-07-05 22:58:10 +03:00
|
|
|
VTRACE(DBG_CMDS, ("searchexec \"%s\" returns \"%s\"\n", name,
|
|
|
|
fullname));
|
1993-03-21 12:45:37 +03:00
|
|
|
INTOFF;
|
2003-01-22 23:36:03 +03:00
|
|
|
if (act & DO_ALTPATH) {
|
Many internal memory management type fixes.
PR bin/52302 (core dump with interactive shell, here doc and error
on same line) is fixed. (An old bug.)
echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )"
consistently prints 1002 lines (x, 1000 empty ones, then y) as it should
(And you don't want to know what it did before, or why.) (Another old one.)
(Recently added) Problems with ~ expansion fixed (mem management related).
Proper fix for the cwrappers configure problem (which includes the quick
fix that was done earlier, but extends upon that to be correct). (This was
another newly added problem.)
And the really devious (and rare) old bug - if STACKSTRNUL() needs to
allocate a new buffer in which to store the \0, calculate the size of
the string space remaining correctly, unlike when SPUTC() grows the
buffer, there is no actual data being stored in the STACKSTRNUL()
case - the string space remaining was calculated as one byte too few.
That would be harmless, unless the next buffer also filled, in which
case it was assumed that it was really full, not one byte less, meaning
one junk char (a nul, or anything) was being copied into the next (even
bigger buffer) corrupting the data.
Consistent use of stalloc() to allocate a new block of (stack) memory,
and grabstackstr() to claim a block of (stack) memory that had already
been occupied but not claimed as in use. Since grabstackstr is implemented
as just a call to stalloc() this is a no-op change in practice, but makes
it much easier to comprehend what is really happening. Previous code
sometimes used stalloc() when the use case was really for grabstackstr().
Change grabstackstr() to actually use the arg passed to it, instead of
(not much better than) guessing how much space to claim,
More care when using unstalloc()/ungrabstackstr() to return space, and in
particular when the stack must be returned to its previous state, rather than
just returning no-longer needed space, neither of those work. They also don't
work properly if there have been (really, even might have been) any stack mem
allocations since the last stalloc()/grabstackstr(). (If we know there
cannot have been then the alloc/release sequence is kind of pointless.)
To work correctly in general we must use setstackmark()/popstackmark() so
do that when needed. Have those also save/restore the top of stack string
space remaining.
[Aside: for those reading this, the "stack" mentioned is not
in any way related to the thing used for maintaining the C
function call state, ie: the "stack segment" of the program,
but the shell's internal memory management strategy.]
More comments to better explain what is happening in some cases.
Also cleaned up some hopelessly broken DEBUG mode data that were
recently added (no effect on anyone but the poor semi-human attempting
to make sense of it...).
User visible changes:
Proper counting of line numbers when a here document is delimited
by a multi-line end-delimiter, as in
cat << 'REALLY
END'
here doc line 1
here doc line 2
REALLY
END
(which is an obscure case, but nothing says should not work.) The \n
in the end-delimiter of the here doc (the last one) was not incrementing
the line number, which from that point on in the script would be 1 too
low (or more, for end-delimiters with more than one \n in them.)
With tilde expansion:
unset HOME; echo ~
changed to return getpwuid(getuid())->pw_home instead of failing (returning ~)
POSIX says this is unspecified, which makes it difficult for a script to
compensate for being run without HOME set (as in env -i sh script), so
while not able to be used portably, this seems like a useful extension
(and is implemented the same way by some other shells).
Further, with
HOME=; printf %s ~
we now write nothing (which is required by POSIX - which requires ~ to
expand to the value of $HOME if it is set) previously if $HOME (in this
case) or a user's directory in the passwd file (for ~user) were a null
STRING, We failed the ~ expansion and left behind '~' or '~user'.
2017-06-17 10:22:12 +03:00
|
|
|
/*
|
|
|
|
* this should be a grabstackstr() but is not needed:
|
|
|
|
* fullname is no longer needed for anything
|
2003-01-22 23:36:03 +03:00
|
|
|
stalloc(strlen(fullname) + 1);
|
Many internal memory management type fixes.
PR bin/52302 (core dump with interactive shell, here doc and error
on same line) is fixed. (An old bug.)
echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )"
consistently prints 1002 lines (x, 1000 empty ones, then y) as it should
(And you don't want to know what it did before, or why.) (Another old one.)
(Recently added) Problems with ~ expansion fixed (mem management related).
Proper fix for the cwrappers configure problem (which includes the quick
fix that was done earlier, but extends upon that to be correct). (This was
another newly added problem.)
And the really devious (and rare) old bug - if STACKSTRNUL() needs to
allocate a new buffer in which to store the \0, calculate the size of
the string space remaining correctly, unlike when SPUTC() grows the
buffer, there is no actual data being stored in the STACKSTRNUL()
case - the string space remaining was calculated as one byte too few.
That would be harmless, unless the next buffer also filled, in which
case it was assumed that it was really full, not one byte less, meaning
one junk char (a nul, or anything) was being copied into the next (even
bigger buffer) corrupting the data.
Consistent use of stalloc() to allocate a new block of (stack) memory,
and grabstackstr() to claim a block of (stack) memory that had already
been occupied but not claimed as in use. Since grabstackstr is implemented
as just a call to stalloc() this is a no-op change in practice, but makes
it much easier to comprehend what is really happening. Previous code
sometimes used stalloc() when the use case was really for grabstackstr().
Change grabstackstr() to actually use the arg passed to it, instead of
(not much better than) guessing how much space to claim,
More care when using unstalloc()/ungrabstackstr() to return space, and in
particular when the stack must be returned to its previous state, rather than
just returning no-longer needed space, neither of those work. They also don't
work properly if there have been (really, even might have been) any stack mem
allocations since the last stalloc()/grabstackstr(). (If we know there
cannot have been then the alloc/release sequence is kind of pointless.)
To work correctly in general we must use setstackmark()/popstackmark() so
do that when needed. Have those also save/restore the top of stack string
space remaining.
[Aside: for those reading this, the "stack" mentioned is not
in any way related to the thing used for maintaining the C
function call state, ie: the "stack segment" of the program,
but the shell's internal memory management strategy.]
More comments to better explain what is happening in some cases.
Also cleaned up some hopelessly broken DEBUG mode data that were
recently added (no effect on anyone but the poor semi-human attempting
to make sense of it...).
User visible changes:
Proper counting of line numbers when a here document is delimited
by a multi-line end-delimiter, as in
cat << 'REALLY
END'
here doc line 1
here doc line 2
REALLY
END
(which is an obscure case, but nothing says should not work.) The \n
in the end-delimiter of the here doc (the last one) was not incrementing
the line number, which from that point on in the script would be 1 too
low (or more, for end-delimiters with more than one \n in them.)
With tilde expansion:
unset HOME; echo ~
changed to return getpwuid(getuid())->pw_home instead of failing (returning ~)
POSIX says this is unspecified, which makes it difficult for a script to
compensate for being run without HOME set (as in env -i sh script), so
while not able to be used portably, this seems like a useful extension
(and is implemented the same way by some other shells).
Further, with
HOME=; printf %s ~
we now write nothing (which is required by POSIX - which requires ~ to
expand to the value of $HOME if it is set) previously if $HOME (in this
case) or a user's directory in the passwd file (for ~user) were a null
STRING, We failed the ~ expansion and left behind '~' or '~user'.
2017-06-17 10:22:12 +03:00
|
|
|
*/
|
2003-01-22 23:36:03 +03:00
|
|
|
cmdp = &loc_cmd;
|
|
|
|
} else
|
|
|
|
cmdp = cmdlookup(name, 1);
|
1993-03-21 12:45:37 +03:00
|
|
|
cmdp->cmdtype = CMDNORMAL;
|
1999-07-09 07:05:49 +04:00
|
|
|
cmdp->param.index = idx;
|
1993-03-21 12:45:37 +03:00
|
|
|
INTON;
|
|
|
|
goto success;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We failed. If there was an entry for this command, delete it */
|
|
|
|
if (cmdp)
|
|
|
|
delete_cmd_entry();
|
1997-07-21 01:27:35 +04:00
|
|
|
if (act & DO_ERR)
|
1996-10-19 06:38:36 +04:00
|
|
|
outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
|
1993-03-21 12:45:37 +03:00
|
|
|
entry->cmdtype = CMDUNKNOWN;
|
|
|
|
return;
|
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
builtin_success:
|
|
|
|
INTOFF;
|
|
|
|
if (act & DO_ALTPATH)
|
|
|
|
cmdp = &loc_cmd;
|
|
|
|
else
|
|
|
|
cmdp = cmdlookup(name, 1);
|
2003-02-04 11:51:30 +03:00
|
|
|
if (cmdp->cmdtype == CMDFUNCTION)
|
|
|
|
/* DO_NOFUNC must have been set */
|
|
|
|
cmdp = &loc_cmd;
|
2003-01-22 23:36:03 +03:00
|
|
|
cmdp->cmdtype = CMDBUILTIN;
|
|
|
|
cmdp->param.bltin = bltin;
|
|
|
|
INTON;
|
1993-03-21 12:45:37 +03:00
|
|
|
success:
|
2006-03-18 08:23:08 +03:00
|
|
|
if (cmdp) {
|
|
|
|
cmdp->rehash = 0;
|
|
|
|
entry->cmdtype = cmdp->cmdtype;
|
A better LINENO implementation. This version deletes (well, #if 0's out)
the LINENO hack, and uses the LINENO var for both ${LINENO} and $((LINENO)).
(Code to invert the LINENO hack when required, like when de-compiling the
execution tree to provide the "jobs" command strings, is still included,
that can be deleted when the LINENO hack is completely removed - look for
refs to VSLINENO throughout the code. The var funclinno in parser.c can
also be removed, it is used only for the LINENO hack.)
This version produces accurate results: $((LINENO)) was made as accurate
as the LINENO hack made ${LINENO} which is very good. That's why the
LINENO hack is not yet completely removed, so it can be easily re-enabled.
If you can tell the difference when it is in use, or not in use, then
something has broken (or I managed to miss a case somewhere.)
The way that LINENO works is documented in its own (new) section in the
man page, so nothing more about that, or the new options, etc, here.
This version introduces the possibility of having a "reference" function
associated with a variable, which gets called whenever the value of the
variable is required (that's what implements LINENO). There is just
one function pointer however, so any particular variable gets at most
one of the set function (as used for PATH, etc) or the reference function.
The VFUNCREF bit in the var flags indicates which func the variable in
question uses (if any - the func ptr, as before, can be NULL).
I would not call the results of this perfect yet, but it is close.
2017-06-07 08:08:32 +03:00
|
|
|
entry->lineno = cmdp->lineno;
|
|
|
|
entry->lno_frel = cmdp->fn_ln1;
|
2006-03-18 08:23:08 +03:00
|
|
|
entry->u = cmdp->param;
|
|
|
|
} else
|
|
|
|
entry->cmdtype = CMDUNKNOWN;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the table of builtin commands.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2012-03-20 22:42:28 +04:00
|
|
|
(*find_builtin(char *name))(int, char **)
|
1994-12-05 22:07:32 +03:00
|
|
|
{
|
1997-01-11 05:04:27 +03:00
|
|
|
const struct builtincmd *bp;
|
1993-03-21 12:45:37 +03:00
|
|
|
|
|
|
|
for (bp = builtincmd ; bp->name ; bp++) {
|
2012-12-31 18:10:15 +04:00
|
|
|
if (*bp->name == *name
|
|
|
|
&& (*name == '%' || equal(bp->name, name)))
|
2002-11-25 01:35:38 +03:00
|
|
|
return bp->builtin;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2012-03-20 22:42:28 +04:00
|
|
|
(*find_splbltin(char *name))(int, char **)
|
2002-11-25 01:35:38 +03:00
|
|
|
{
|
|
|
|
const struct builtincmd *bp;
|
|
|
|
|
|
|
|
for (bp = splbltincmd ; bp->name ; bp++) {
|
|
|
|
if (*bp->name == *name && equal(bp->name, name))
|
|
|
|
return bp->builtin;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At shell startup put special builtins into hash table.
|
|
|
|
* ensures they are executed first (see posix).
|
|
|
|
* We stop functions being added with the same name
|
|
|
|
* (as they are impossible to call)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
hash_special_builtins(void)
|
|
|
|
{
|
|
|
|
const struct builtincmd *bp;
|
|
|
|
struct tblentry *cmdp;
|
|
|
|
|
|
|
|
for (bp = splbltincmd ; bp->name ; bp++) {
|
|
|
|
cmdp = cmdlookup(bp->name, 1);
|
|
|
|
cmdp->cmdtype = CMDSPLBLTIN;
|
|
|
|
cmdp->param.bltin = bp->builtin;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called when a cd is done. Marks all commands so the next time they
|
|
|
|
* are executed they will be rehashed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2002-11-25 01:35:38 +03:00
|
|
|
hashcd(void)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry **pp;
|
|
|
|
struct tblentry *cmdp;
|
|
|
|
|
|
|
|
for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
|
|
|
|
for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
|
|
|
|
if (cmdp->cmdtype == CMDNORMAL
|
1995-05-12 01:28:33 +04:00
|
|
|
|| (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
|
1993-03-21 12:45:37 +03:00
|
|
|
cmdp->rehash = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2003-01-22 23:36:03 +03:00
|
|
|
* Fix command hash table when PATH changed.
|
1993-03-21 12:45:37 +03:00
|
|
|
* Called before PATH is changed. The argument is the new value of PATH;
|
2003-01-22 23:36:03 +03:00
|
|
|
* pathval() still returns the old value at this point.
|
|
|
|
* Called with interrupts off.
|
1993-03-21 12:45:37 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2002-11-25 01:35:38 +03:00
|
|
|
changepath(const char *newval)
|
1995-01-15 12:29:16 +03:00
|
|
|
{
|
1996-06-25 20:40:38 +04:00
|
|
|
const char *old, *new;
|
1999-07-09 07:05:49 +04:00
|
|
|
int idx;
|
1993-03-21 12:45:37 +03:00
|
|
|
int firstchange;
|
|
|
|
int bltin;
|
|
|
|
|
|
|
|
old = pathval();
|
|
|
|
new = newval;
|
|
|
|
firstchange = 9999; /* assume no change */
|
1999-07-09 07:05:49 +04:00
|
|
|
idx = 0;
|
1993-03-21 12:45:37 +03:00
|
|
|
bltin = -1;
|
|
|
|
for (;;) {
|
|
|
|
if (*old != *new) {
|
1999-07-09 07:05:49 +04:00
|
|
|
firstchange = idx;
|
1995-05-12 01:28:33 +04:00
|
|
|
if ((*old == '\0' && *new == ':')
|
|
|
|
|| (*old == ':' && *new == '\0'))
|
1993-03-21 12:45:37 +03:00
|
|
|
firstchange++;
|
|
|
|
old = new; /* ignore subsequent differences */
|
|
|
|
}
|
|
|
|
if (*new == '\0')
|
|
|
|
break;
|
|
|
|
if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
|
1999-07-09 07:05:49 +04:00
|
|
|
bltin = idx;
|
1993-03-21 12:45:37 +03:00
|
|
|
if (*new == ':') {
|
1999-07-09 07:05:49 +04:00
|
|
|
idx++;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
new++, old++;
|
|
|
|
}
|
|
|
|
if (builtinloc < 0 && bltin >= 0)
|
|
|
|
builtinloc = bltin; /* zap builtins */
|
|
|
|
if (builtinloc >= 0 && bltin < 0)
|
|
|
|
firstchange = 0;
|
|
|
|
clearcmdentry(firstchange);
|
|
|
|
builtinloc = bltin;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear out command entries. The argument specifies the first entry in
|
|
|
|
* PATH which has changed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
STATIC void
|
2002-11-25 01:35:38 +03:00
|
|
|
clearcmdentry(int firstchange)
|
1994-12-05 22:07:32 +03:00
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry **tblp;
|
|
|
|
struct tblentry **pp;
|
|
|
|
struct tblentry *cmdp;
|
|
|
|
|
|
|
|
INTOFF;
|
|
|
|
for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
|
|
|
|
pp = tblp;
|
|
|
|
while ((cmdp = *pp) != NULL) {
|
1995-05-12 01:28:33 +04:00
|
|
|
if ((cmdp->cmdtype == CMDNORMAL &&
|
|
|
|
cmdp->param.index >= firstchange)
|
|
|
|
|| (cmdp->cmdtype == CMDBUILTIN &&
|
|
|
|
builtinloc >= firstchange)) {
|
1993-03-21 12:45:37 +03:00
|
|
|
*pp = cmdp->next;
|
|
|
|
ckfree(cmdp);
|
|
|
|
} else {
|
|
|
|
pp = &cmdp->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
INTON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete all functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef mkinit
|
2002-11-25 01:35:38 +03:00
|
|
|
MKINIT void deletefuncs(void);
|
|
|
|
MKINIT void hash_special_builtins(void);
|
|
|
|
|
|
|
|
INIT {
|
|
|
|
hash_special_builtins();
|
|
|
|
}
|
1993-03-21 12:45:37 +03:00
|
|
|
|
|
|
|
SHELLPROC {
|
|
|
|
deletefuncs();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
2002-11-25 01:35:38 +03:00
|
|
|
deletefuncs(void)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry **tblp;
|
|
|
|
struct tblentry **pp;
|
|
|
|
struct tblentry *cmdp;
|
|
|
|
|
|
|
|
INTOFF;
|
|
|
|
for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
|
|
|
|
pp = tblp;
|
|
|
|
while ((cmdp = *pp) != NULL) {
|
|
|
|
if (cmdp->cmdtype == CMDFUNCTION) {
|
|
|
|
*pp = cmdp->next;
|
|
|
|
freefunc(cmdp->param.func);
|
|
|
|
ckfree(cmdp);
|
|
|
|
} else {
|
|
|
|
pp = &cmdp->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
INTON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Locate a command in the command hash table. If "add" is nonzero,
|
|
|
|
* add the command to the table if it is not already present. The
|
|
|
|
* variable "lastcmdentry" is set to point to the address of the link
|
|
|
|
* pointing to the entry, so that delete_cmd_entry can delete the
|
|
|
|
* entry.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct tblentry **lastcmdentry;
|
|
|
|
|
|
|
|
|
|
|
|
STATIC struct tblentry *
|
2002-11-25 01:35:38 +03:00
|
|
|
cmdlookup(const char *name, int add)
|
1994-12-05 22:07:32 +03:00
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
int hashval;
|
2002-11-25 01:35:38 +03:00
|
|
|
const char *p;
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry *cmdp;
|
|
|
|
struct tblentry **pp;
|
|
|
|
|
|
|
|
p = name;
|
|
|
|
hashval = *p << 4;
|
|
|
|
while (*p)
|
|
|
|
hashval += *p++;
|
|
|
|
hashval &= 0x7FFF;
|
|
|
|
pp = &cmdtable[hashval % CMDTABLESIZE];
|
|
|
|
for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
|
|
|
|
if (equal(cmdp->cmdname, name))
|
|
|
|
break;
|
|
|
|
pp = &cmdp->next;
|
|
|
|
}
|
|
|
|
if (add && cmdp == NULL) {
|
|
|
|
INTOFF;
|
|
|
|
cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
|
|
|
|
+ strlen(name) + 1);
|
|
|
|
cmdp->next = NULL;
|
|
|
|
cmdp->cmdtype = CMDUNKNOWN;
|
|
|
|
cmdp->rehash = 0;
|
|
|
|
strcpy(cmdp->cmdname, name);
|
|
|
|
INTON;
|
|
|
|
}
|
|
|
|
lastcmdentry = pp;
|
|
|
|
return cmdp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete the command entry returned on the last lookup.
|
|
|
|
*/
|
|
|
|
|
|
|
|
STATIC void
|
2002-11-25 01:35:38 +03:00
|
|
|
delete_cmd_entry(void)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry *cmdp;
|
|
|
|
|
|
|
|
INTOFF;
|
|
|
|
cmdp = *lastcmdentry;
|
|
|
|
*lastcmdentry = cmdp->next;
|
|
|
|
ckfree(cmdp);
|
|
|
|
INTON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef notdef
|
|
|
|
void
|
2002-11-25 01:35:38 +03:00
|
|
|
getcmdentry(char *name, struct cmdentry *entry)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry *cmdp = cmdlookup(name, 0);
|
|
|
|
|
|
|
|
if (cmdp) {
|
|
|
|
entry->u = cmdp->param;
|
|
|
|
entry->cmdtype = cmdp->cmdtype;
|
|
|
|
} else {
|
|
|
|
entry->cmdtype = CMDUNKNOWN;
|
|
|
|
entry->u.index = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a new command entry, replacing any existing command entry for
|
2002-11-25 01:35:38 +03:00
|
|
|
* the same name - except special builtins.
|
1993-03-21 12:45:37 +03:00
|
|
|
*/
|
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
STATIC void
|
2002-11-25 01:35:38 +03:00
|
|
|
addcmdentry(char *name, struct cmdentry *entry)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry *cmdp;
|
|
|
|
|
|
|
|
INTOFF;
|
|
|
|
cmdp = cmdlookup(name, 1);
|
2002-11-25 01:35:38 +03:00
|
|
|
if (cmdp->cmdtype != CMDSPLBLTIN) {
|
2018-06-22 14:04:55 +03:00
|
|
|
if (cmdp->cmdtype == CMDFUNCTION)
|
|
|
|
unreffunc(cmdp->param.func);
|
2002-11-25 01:35:38 +03:00
|
|
|
cmdp->cmdtype = entry->cmdtype;
|
A better LINENO implementation. This version deletes (well, #if 0's out)
the LINENO hack, and uses the LINENO var for both ${LINENO} and $((LINENO)).
(Code to invert the LINENO hack when required, like when de-compiling the
execution tree to provide the "jobs" command strings, is still included,
that can be deleted when the LINENO hack is completely removed - look for
refs to VSLINENO throughout the code. The var funclinno in parser.c can
also be removed, it is used only for the LINENO hack.)
This version produces accurate results: $((LINENO)) was made as accurate
as the LINENO hack made ${LINENO} which is very good. That's why the
LINENO hack is not yet completely removed, so it can be easily re-enabled.
If you can tell the difference when it is in use, or not in use, then
something has broken (or I managed to miss a case somewhere.)
The way that LINENO works is documented in its own (new) section in the
man page, so nothing more about that, or the new options, etc, here.
This version introduces the possibility of having a "reference" function
associated with a variable, which gets called whenever the value of the
variable is required (that's what implements LINENO). There is just
one function pointer however, so any particular variable gets at most
one of the set function (as used for PATH, etc) or the reference function.
The VFUNCREF bit in the var flags indicates which func the variable in
question uses (if any - the func ptr, as before, can be NULL).
I would not call the results of this perfect yet, but it is close.
2017-06-07 08:08:32 +03:00
|
|
|
cmdp->lineno = entry->lineno;
|
|
|
|
cmdp->fn_ln1 = entry->lno_frel;
|
2002-11-25 01:35:38 +03:00
|
|
|
cmdp->param = entry->u;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
INTON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Define a shell function.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
A better LINENO implementation. This version deletes (well, #if 0's out)
the LINENO hack, and uses the LINENO var for both ${LINENO} and $((LINENO)).
(Code to invert the LINENO hack when required, like when de-compiling the
execution tree to provide the "jobs" command strings, is still included,
that can be deleted when the LINENO hack is completely removed - look for
refs to VSLINENO throughout the code. The var funclinno in parser.c can
also be removed, it is used only for the LINENO hack.)
This version produces accurate results: $((LINENO)) was made as accurate
as the LINENO hack made ${LINENO} which is very good. That's why the
LINENO hack is not yet completely removed, so it can be easily re-enabled.
If you can tell the difference when it is in use, or not in use, then
something has broken (or I managed to miss a case somewhere.)
The way that LINENO works is documented in its own (new) section in the
man page, so nothing more about that, or the new options, etc, here.
This version introduces the possibility of having a "reference" function
associated with a variable, which gets called whenever the value of the
variable is required (that's what implements LINENO). There is just
one function pointer however, so any particular variable gets at most
one of the set function (as used for PATH, etc) or the reference function.
The VFUNCREF bit in the var flags indicates which func the variable in
question uses (if any - the func ptr, as before, can be NULL).
I would not call the results of this perfect yet, but it is close.
2017-06-07 08:08:32 +03:00
|
|
|
defun(char *name, union node *func, int lineno)
|
2002-11-25 01:35:38 +03:00
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct cmdentry entry;
|
|
|
|
|
|
|
|
INTOFF;
|
|
|
|
entry.cmdtype = CMDFUNCTION;
|
A better LINENO implementation. This version deletes (well, #if 0's out)
the LINENO hack, and uses the LINENO var for both ${LINENO} and $((LINENO)).
(Code to invert the LINENO hack when required, like when de-compiling the
execution tree to provide the "jobs" command strings, is still included,
that can be deleted when the LINENO hack is completely removed - look for
refs to VSLINENO throughout the code. The var funclinno in parser.c can
also be removed, it is used only for the LINENO hack.)
This version produces accurate results: $((LINENO)) was made as accurate
as the LINENO hack made ${LINENO} which is very good. That's why the
LINENO hack is not yet completely removed, so it can be easily re-enabled.
If you can tell the difference when it is in use, or not in use, then
something has broken (or I managed to miss a case somewhere.)
The way that LINENO works is documented in its own (new) section in the
man page, so nothing more about that, or the new options, etc, here.
This version introduces the possibility of having a "reference" function
associated with a variable, which gets called whenever the value of the
variable is required (that's what implements LINENO). There is just
one function pointer however, so any particular variable gets at most
one of the set function (as used for PATH, etc) or the reference function.
The VFUNCREF bit in the var flags indicates which func the variable in
question uses (if any - the func ptr, as before, can be NULL).
I would not call the results of this perfect yet, but it is close.
2017-06-07 08:08:32 +03:00
|
|
|
entry.lineno = lineno;
|
|
|
|
entry.lno_frel = fnline1;
|
1993-03-21 12:45:37 +03:00
|
|
|
entry.u.func = copyfunc(func);
|
|
|
|
addcmdentry(name, &entry);
|
|
|
|
INTON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a function if it exists.
|
|
|
|
*/
|
|
|
|
|
1994-05-11 21:09:42 +04:00
|
|
|
int
|
2002-11-25 01:35:38 +03:00
|
|
|
unsetfunc(char *name)
|
|
|
|
{
|
1993-03-21 12:45:37 +03:00
|
|
|
struct tblentry *cmdp;
|
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
if ((cmdp = cmdlookup(name, 0)) != NULL &&
|
|
|
|
cmdp->cmdtype == CMDFUNCTION) {
|
2018-06-22 14:04:55 +03:00
|
|
|
unreffunc(cmdp->param.func);
|
1993-03-21 12:45:37 +03:00
|
|
|
delete_cmd_entry();
|
|
|
|
}
|
2013-11-01 20:49:02 +04:00
|
|
|
return 0;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1997-02-07 02:24:52 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Locate and print what a word is...
|
2003-01-22 23:36:03 +03:00
|
|
|
* also used for 'command -[v|V]'
|
1997-02-07 02:24:52 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2002-11-25 01:35:38 +03:00
|
|
|
typecmd(int argc, char **argv)
|
1997-02-07 02:24:52 +03:00
|
|
|
{
|
|
|
|
struct cmdentry entry;
|
|
|
|
struct tblentry *cmdp;
|
2008-02-15 20:26:06 +03:00
|
|
|
const char * const *pp;
|
1997-02-07 02:24:52 +03:00
|
|
|
struct alias *ap;
|
1999-07-09 07:05:49 +04:00
|
|
|
int err = 0;
|
2003-01-22 23:36:03 +03:00
|
|
|
char *arg;
|
|
|
|
int c;
|
|
|
|
int V_flag = 0;
|
|
|
|
int v_flag = 0;
|
|
|
|
int p_flag = 0;
|
|
|
|
|
|
|
|
while ((c = nextopt("vVp")) != 0) {
|
|
|
|
switch (c) {
|
|
|
|
case 'v': v_flag = 1; break;
|
|
|
|
case 'V': V_flag = 1; break;
|
|
|
|
case 'p': p_flag = 1; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
if (argv[0][0] != 'c' && v_flag | V_flag | p_flag)
|
|
|
|
error("usage: %s name...", argv[0]);
|
|
|
|
|
|
|
|
if (v_flag && V_flag)
|
|
|
|
error("-v and -V cannot both be specified");
|
|
|
|
|
|
|
|
if (*argptr == NULL)
|
|
|
|
error("usage: %s%s name ...", argv[0],
|
|
|
|
argv[0][0] == 'c' ? " [-p] [-v|-V]" : "");
|
1997-02-07 02:24:52 +03:00
|
|
|
|
2003-01-22 23:36:03 +03:00
|
|
|
while ((arg = *argptr++)) {
|
|
|
|
if (!v_flag)
|
|
|
|
out1str(arg);
|
1997-02-07 02:24:52 +03:00
|
|
|
/* First look at the keywords */
|
2000-07-03 07:26:17 +04:00
|
|
|
for (pp = parsekwd; *pp; pp++)
|
2003-01-22 23:36:03 +03:00
|
|
|
if (**pp == *arg && equal(*pp, arg))
|
1997-02-07 02:24:52 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (*pp) {
|
2003-01-22 23:36:03 +03:00
|
|
|
if (v_flag)
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
out1fmt("%s\n", arg);
|
2003-01-22 23:36:03 +03:00
|
|
|
else
|
|
|
|
out1str(" is a shell keyword\n");
|
1997-02-07 02:24:52 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Then look at the aliases */
|
2003-01-22 23:36:03 +03:00
|
|
|
if ((ap = lookupalias(arg, 1)) != NULL) {
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
int ml = 0;
|
|
|
|
|
|
|
|
if (!v_flag) {
|
|
|
|
out1str(" is an alias ");
|
|
|
|
if (strchr(ap->val, '\n')) {
|
|
|
|
out1str("(multiline)...\n");
|
|
|
|
ml = 1;
|
|
|
|
} else
|
|
|
|
out1str("for: ");
|
|
|
|
}
|
2003-01-22 23:36:03 +03:00
|
|
|
out1fmt("%s\n", ap->val);
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
if (ml && *argptr != NULL)
|
|
|
|
out1c('\n');
|
1997-02-07 02:24:52 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Then check if it is a tracked alias */
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
if (!p_flag && (cmdp = cmdlookup(arg, 0)) != NULL) {
|
1997-02-07 02:24:52 +03:00
|
|
|
entry.cmdtype = cmdp->cmdtype;
|
|
|
|
entry.u = cmdp->param;
|
2003-01-22 23:36:03 +03:00
|
|
|
} else {
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
cmdp = NULL;
|
1997-02-07 02:24:52 +03:00
|
|
|
/* Finally use brute force */
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
find_command(arg, &entry, DO_ABS,
|
|
|
|
p_flag ? syspath() + 5 : pathval());
|
1997-02-07 02:24:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (entry.cmdtype) {
|
|
|
|
case CMDNORMAL: {
|
2003-01-22 23:36:03 +03:00
|
|
|
if (strchr(arg, '/') == NULL) {
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
const char *path;
|
2000-11-01 22:21:41 +03:00
|
|
|
char *name;
|
|
|
|
int j = entry.u.index;
|
Fix several bugs in the command / type builtin ( including PR bin/48499 )
1. Make command -pv (and -pV) work (which is not as easy as the PR
suggests it might be (the "check and cause error" was there because
it did not work, not in order to prevent it from working).
2. Stop -v and -V being both used (that makes no sense).
3. Stop the "type" builtin inheriting the args (-pvV) that "command" has
(which it did, as when -v -or -V is used with command, it and type are
implemented using the same code).
4. make "command -v word" DTRT for sh keywords (was treating them as an error).
5. Require at least one arg for "command -[vV]" or "type" else usage & error.
Strictly this should also apply to "command" and "command -p" (no -v)
but that's handled elsewhere, so perhaps some other time. Perhaps
"command -v" (and -V) should be limited to 1 command name (where "type"
can have many) as in the POSIX definitions, but I don't think that matters.
6. With "command -V alias", (or "type alias" which is the same thing),
(but not "command -v alias") alter the output format, so we get
ll is an alias for: ls -al
instead of the old
ll is an alias for
ls -al
(and note there was a space, for some reason, after "for")
That is, unless the alias value contains any \n characters, in which
case (something approximating) the old multi-line format is retained.
Also note: that if code wants to parse/use the value of an alias, it
should be using the output of "alias name", not command or type.
Note that none of the above affects "command [-p] cmd" (no -v or -V options)
only "command -[vV]" and "type".
Note also that the changes to eval.[ch] are merely to make syspath()
visible in exec.c rather than static in eval.c
2018-07-25 17:42:50 +03:00
|
|
|
|
|
|
|
path = p_flag ? syspath() + 5 : pathval();
|
|
|
|
|
2000-11-01 22:21:41 +03:00
|
|
|
do {
|
Make cd (really) do cd -P, and not just claim that is what it is doing
while doing a half-hearted, broken, partial, version of cd -L instead.
The latter (as the manual says) is not supported, what's more, it is an
abomination, and should never be supported (anywhere.)
Fix the doc so that the pretense that we notice when a path given crosses
a symlink (and turns on printing of the destination directory) is claimed
no more (that used to be true until late Dec 2016, but was changed). Now
the print happens if -o cdprint is set, or if an entry from CDPATH that is
not "" or "." is used (or if the "cd dest repl" cd cmd variant is used.)
Fix CDPATH processing: avoid the magic '%' processing that is used for
PATH and MAILPATH from corrupting CDPATH. The % magic (both variants)
remains undocumented.
Also, don't double the '/' if an entry in PATH or CDPATH ends in '/'
(as in CDPATH=":/usr/src/"). A "cd usr.bin" used to do
chdir("/usr/src//usr.bin"). No more. This is almost invisible,
and relatively harmless, either way....
Also fix a bug where if a plausible destination directory in CDPATH
was located, but the chdir() failed (eg: permission denied) and then
a later "." or "" CDPATH entry succeeded, "print" mode was turned on.
That is:
cd /tmp; mkdir bin
mkdir -p P/bin; chmod 0 P/bin
CDPATH=/tmp/P:
cd bin
would cd to /tmp/bin (correctly) but print it (incorrectly).
Also when in "cd dest replace" mode, if the result of the replacement
generates '-' as the path named, as in:
cd $PWD -
then simply change to '-' (or attempt to, with CDPATH search), rather
than having this being equivalent to "cd -")
Because of these changes, the pwd command (and $PWD) essentially
always acts as pwd -P, even when called as pwd -L (which is still
the default.) That is, even more than it did before.
Also fixed a (kind of minor) mem management error (CDPATH related)
"whosoever shall padvance must stunalloc before repeating" (and the
same for MAILPATH).
2017-06-04 23:27:14 +03:00
|
|
|
name = padvance(&path, arg, 1);
|
1997-07-21 01:27:35 +04:00
|
|
|
stunalloc(name);
|
|
|
|
} while (--j >= 0);
|
2003-01-22 23:36:03 +03:00
|
|
|
if (!v_flag)
|
|
|
|
out1fmt(" is%s ",
|
|
|
|
cmdp ? " a tracked alias for" : "");
|
|
|
|
out1fmt("%s\n", name);
|
2000-11-01 22:21:41 +03:00
|
|
|
} else {
|
2003-01-22 23:36:03 +03:00
|
|
|
if (access(arg, X_OK) == 0) {
|
|
|
|
if (!v_flag)
|
|
|
|
out1fmt(" is ");
|
|
|
|
out1fmt("%s\n", arg);
|
|
|
|
} else {
|
|
|
|
if (!v_flag)
|
|
|
|
out1fmt(": %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
else
|
|
|
|
err = 126;
|
|
|
|
}
|
1997-07-21 01:27:35 +04:00
|
|
|
}
|
2000-11-01 22:21:41 +03:00
|
|
|
break;
|
1997-02-07 02:24:52 +03:00
|
|
|
}
|
|
|
|
case CMDFUNCTION:
|
2003-01-22 23:36:03 +03:00
|
|
|
if (!v_flag)
|
|
|
|
out1str(" is a shell function\n");
|
|
|
|
else
|
|
|
|
out1fmt("%s\n", arg);
|
1997-02-07 02:24:52 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CMDBUILTIN:
|
2003-01-22 23:36:03 +03:00
|
|
|
if (!v_flag)
|
|
|
|
out1str(" is a shell builtin\n");
|
|
|
|
else
|
|
|
|
out1fmt("%s\n", arg);
|
1997-02-07 02:24:52 +03:00
|
|
|
break;
|
|
|
|
|
2002-11-25 01:35:38 +03:00
|
|
|
case CMDSPLBLTIN:
|
2003-01-22 23:36:03 +03:00
|
|
|
if (!v_flag)
|
|
|
|
out1str(" is a special shell builtin\n");
|
|
|
|
else
|
|
|
|
out1fmt("%s\n", arg);
|
2002-11-25 01:35:38 +03:00
|
|
|
break;
|
|
|
|
|
1997-02-07 02:24:52 +03:00
|
|
|
default:
|
2003-01-22 23:36:03 +03:00
|
|
|
if (!v_flag)
|
|
|
|
out1str(": not found\n");
|
|
|
|
err = 127;
|
1997-02-07 02:24:52 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-07-09 07:05:49 +04:00
|
|
|
return err;
|
1997-02-07 02:24:52 +03:00
|
|
|
}
|